---
name: harden-workflow
description: Use when asked to fix, harden, or secure a GitHub Actions workflow file — pin actions to SHAs, add least-privilege permissions, add timeouts. Returns the rewritten YAML from the Pipefort public API. No credentials required.
license: Proprietary
---

# Harden a GitHub Actions workflow

Pipefort rewrites a workflow YAML with security fixes applied and hands back the
result. The endpoint is unauthenticated and stateless — nothing is stored.

## Two-step protocol

Always **preview first**. Omit `categories` to learn which fix groups apply to
this specific file without changing anything:

```bash
curl -sS https://pipefort.com/api/public/harden \
  -H 'Content-Type: application/json' \
  -d "$(jq -Rs '{yaml: .}' .github/workflows/ci.yml)"
```

```json
{
  "hardened": "...",
  "original": "...",
  "fixes_applied": 0,
  "changed": false,
  "applied_categories": [],
  "available": [
    { "category": "pin-actions", "title": "Pin actions to commit SHAs", "count": 4 },
    { "category": "permissions", "title": "Add least-privilege permissions", "count": 2 }
  ]
}
```

Then apply the subset the user agreed to:

```bash
curl -sS https://pipefort.com/api/public/harden \
  -H 'Content-Type: application/json' \
  -d "$(jq -Rs --argjson c '["pin-actions","permissions"]' '{yaml: ., categories: $c}' .github/workflows/ci.yml)"
```

The `category` strings come from `available` in the preview — do not invent
them. An unrecognized category is simply not applied.

## Rules for applying the result

- **`changed: false` means write nothing.** `hardened` is byte-identical to the
  input; overwriting the file would produce an empty diff and a pointless commit.
- **Diff before writing.** `original` is the YAML exactly as submitted; diff it
  against `hardened` and show the user what changed before touching their file.
- **Never fabricate a fix.** If a finding has no matching category in
  `available`, it has no auto-fix — explain the manual remediation from
  `https://docs.pipefort.com/rules/{rule_id}` instead of hand-editing YAML and
  claiming it is a Pipefort fix.
- **One file per call.** Send the complete contents of a single workflow file,
  not a concatenation and not a fragment.
- **Preserve the result verbatim.** The rewriter maintains comments and
  formatting deliberately; re-serializing the YAML yourself will undo that.

## Errors

`400` means `yaml` was missing, empty, oversized, or not a parseable GitHub
Actions workflow. Check that you sent the file contents, not a path.

## Typical flow

1. `scan-repo` → find what is wrong across the repository.
2. `harden-workflow` preview → see which of those have auto-fixes.
3. Confirm with the user → apply → diff → commit.
4. `vet-action` → sanity-check any third-party action the workflow still uses.

## Related

- Full API description: https://pipefort.com/openapi.json
- Rule catalog: https://docs.pipefort.com/rules/overview
