> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pipefort.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Auto-fix

> What --fix can safely rewrite, and what it intentionally won't touch.

```bash theme={null}
pipefort -p . --fix
```

The `--fix` flag rewrites workflow YAML in place for the categories where a safe, deterministic fix exists. After applying fixes, the scanner re-runs so the final report shows only what's left.

## What gets fixed

| Category                                                                           | Fix                                                                                                                                                                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [CICD-SEC-5](/rules/cicd-sec-5) — Missing permissions                              | Prepends `permissions: read-all` at the top of the workflow.                                                                                                                                                                                                                                                                                                              |
| [CICD-SEC-1](/rules/cicd-sec-1) — Dangerous `pull_request_target`                  | Rewrites the trigger to `pull_request`. Handles scalar, sequence, and mapping forms of `on:`.                                                                                                                                                                                                                                                                             |
| [CICD-SEC-3](/rules/cicd-sec-3) — Unpinned action                                  | Resolves the tag/branch to a commit SHA via the GitHub API and pins to `owner/repo@<sha> # <original-ref>`.                                                                                                                                                                                                                                                               |
| [CICD-SEC-4](/rules/cicd-sec-4) — Shell injection                                  | Lifts each `${{ github.event.* }}` reference out of the inline `run:` script into the step's `env:` block, then replaces the interpolation with `$VAR_NAME`.                                                                                                                                                                                                              |
| [CICD-SEC-6](/rules/cicd-sec-6) — Hardcoded secret                                 | **Env-var case:** replaces the literal with `${{ secrets.<KEY_UPPER> }}`. **Run-script case:** hoists typed literals (`ghp_*`, `xoxb-*`, `AKIA*`) into the step's `env:` block as `${{ secrets.GH_TOKEN }}` / `${{ secrets.SLACK_TOKEN }}` / `${{ secrets.AWS_ACCESS_KEY_ID }}` and rewrites occurrences as `$VAR`. The generic `var := "..."` pattern is not auto-fixed. |
| [CICD-SEC-7](/rules/cicd-sec-7) — Actions debug logging                            | Deletes the `ACTIONS_STEP_DEBUG` / `ACTIONS_RUNNER_DEBUG` entry from the offending `env:` block. Sibling env entries are preserved.                                                                                                                                                                                                                                       |
| [CICD-SEC-10](/rules/cicd-sec-10) — Job-level continue-on-error                    | Removes the `continue-on-error: true` entry from the job. Step-level `continue-on-error` is left alone.                                                                                                                                                                                                                                                                   |
| [SLSA-BUILD-L2](/rules/slsa-build-l2-oidc-token-scope) — Missing `id-token: write` | Adds `id-token: write` to the offending job's `permissions:` block (creates `{ contents: read, id-token: write }` if no block exists).                                                                                                                                                                                                                                    |
| [SLSA-BUILD-L2](/rules/slsa-build-l2-perms-overly-broad) — `write-all` permissions | Replaces a `permissions: write-all` scalar (workflow- or job-level) with `read-all`. The mapping form with 4+ explicit writes is left for manual review — those scopes may have been deliberately chosen.                                                                                                                                                                 |
| [BEST-PRAC-2](/rules/best-prac-2) — Missing timeout                                | Adds `timeout-minutes: 30` to the job.                                                                                                                                                                                                                                                                                                                                    |

## What it won't fix

* **The "generic `var := "..."`" CICD-SEC-6 pattern** — matches an enclosing assignment, not just the secret value; a blind rewrite would corrupt syntax.
* **`curl | sh` patterns** ([BEST-PRAC-1](/rules/best-prac-1)). There's no safe automatic rewrite; flagged for manual review.
* **Self-hosted runners** ([BEST-PRAC-3](/rules/best-prac-3)). Often intentional infra choice.

## What you need before running

* **Network access to `api.github.com`** for [CICD-SEC-3](/rules/cicd-sec-3) — the fixer hits the GitHub API to resolve each tag to a commit SHA. If resolution fails, the action is left as-is and a warning is printed to stderr.
* **Write access to the workflow files** — fixes are applied in place. Commit clean first so the diff is reviewable.

<Warning>
  `--fix` is **not** supported when scanning a remote repo with `-g owner/repo`. The CLI prints a warning and skips the fix step. Clone the repo yourself if you want to fix and inspect the diff.
</Warning>

## Repository-configuration auto-fix (`--fix-settings`)

`--fix` rewrites workflow YAML in your working tree. `--fix-settings` is the GitHub-API counterpart: it remediates the **repository-configuration** findings (branch protection, default `GITHUB_TOKEN`, Dependabot, secret scanning) by hitting the GitHub API directly. It's a separate flag because the required token scopes are wider — opt in only when you want it.

```bash theme={null}
# Dry-run first so you see what would change.
pipefort -g owner/repo --github-token <PAT> --fix-settings --dry-run

# Apply.
pipefort -g owner/repo --github-token <PAT> --fix-settings
```

| Rule                                                                       | What the fix does                                                                                                                                                                                                 |
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [WPERM-WRITE](/rules/cicd-sec-4-wperm-write)                               | `PUT /actions/permissions/workflow` → `default_workflow_permissions: read`. Preserves the existing `can_approve_pull_request_reviews` value.                                                                      |
| [WPERM-PR-APPROVE](/rules/cicd-sec-4-wperm-pr-approve)                     | Same endpoint → `can_approve_pull_request_reviews: false`. Preserves `default_workflow_permissions`.                                                                                                              |
| [DEPENDABOT-ALERTS-OFF](/rules/cicd-sec-3-dependabot-alerts-off)           | `PUT /vulnerability-alerts`.                                                                                                                                                                                      |
| [DEPENDABOT-FIXES-OFF](/rules/cicd-sec-3-dependabot-fixes-off)             | `PUT /automated-security-fixes`.                                                                                                                                                                                  |
| [SECRET-SCANNING-OFF](/rules/cicd-sec-6-secret-scanning-off)               | `PATCH /repos/...` with `security_and_analysis.secret_scanning.status = enabled`.                                                                                                                                 |
| [SECRET-PUSH-PROTECTION-OFF](/rules/cicd-sec-6-secret-push-protection-off) | `PATCH /repos/...` with `security_and_analysis.secret_scanning_push_protection.status = enabled`.                                                                                                                 |
| [BP-MISSING](/rules/cicd-sec-1-bp-missing)                                 | `PUT /branches/{default}/protection` with sensible defaults: 2 required reviews, dismiss stale reviews, enforce admins, no force-push or deletion. Does **not** add required status checks (we don't know which). |
| [BP-FORCE-PUSH](/rules/cicd-sec-1-bp-force-push)                           | Fetch + mutate + PUT — sets `allow_force_pushes: false`, preserves every other field.                                                                                                                             |
| [BP-DELETION](/rules/cicd-sec-1-bp-deletion)                               | Same shape — sets `allow_deletions: false`.                                                                                                                                                                       |
| [BP-ADMIN-BYPASS](/rules/cicd-sec-1-bp-admin-bypass)                       | Same shape — sets `enforce_admins: true`.                                                                                                                                                                         |
| [BP-NO-CODEOWNERS-REVIEW](/rules/cicd-sec-1-bp-no-codeowners-review)       | Same shape — sets `required_pull_request_reviews.require_code_owner_reviews: true`.                                                                                                                               |
| [BP-NO-SIGNED-COMMITS](/rules/cicd-sec-1-bp-no-signed-commits)             | `POST /branches/{default}/protection/required_signatures`.                                                                                                                                                        |

### What `--fix-settings` won't touch

* [BP-NO-REVIEW](/rules/cicd-sec-1-bp-no-review), [BP-FEW-REVIEWERS](/rules/cicd-sec-1-bp-few-reviewers), [BP-STALE-REVIEWS](/rules/cicd-sec-1-bp-stale-reviews): when BP-MISSING fires, the defaults the fixer installs already cover these. When BP exists but lacks one of them, the fix isn't yet automated — coming in a follow-up.
* [BP-NO-STATUS-CHECKS](/rules/cicd-sec-1-bp-no-status-checks): we can't infer which checks should be required.
* [ACTIONS-ALL-ALLOWED](/rules/cicd-sec-5-actions-all-allowed): switching from `all` to `selected` requires building an allowlist; not automated.

### Token requirements

The fixer uses the same `--github-token` (or `$GITHUB_TOKEN` or `gh auth token`) that the audit uses, but needs **wider** scopes for the writes:

* `administration: write` — branch protection, secret scanning, Dependabot
* `actions: write` — workflow permissions

If the token lacks a scope, that specific rule fails (other fixes in the same run still proceed). Errors are printed to stderr.

### `--dry-run`

`--dry-run` short-circuits PUT/PATCH/POST before they hit GitHub but lets the GET requests through, so the printed action descriptions still reflect the real current state. Use it whenever you're unsure.

## Web app: open a fix PR

The web dashboard exposes the **same fixers** on a different surface: per-finding, click **Open fix PR** to have Pipefort fetch the workflow file from GitHub, run the fixer in memory, push the result to a branch named `pipefort/fix/<rule>/<file>`, and open a pull request for human review.

Compared to the CLI's `--fix`:

* The CLI mutates files in your local working tree; the web app opens a PR. Nothing lands on your default branch until you merge it.
* The web app uses the GitHub App installation token (see [GitHub App permissions](/concepts/github-app-permissions#workflow-yaml-auto-fix-pr-based)). The CLI uses whatever scopes are on the `--github-token` you pass.
* Repeat clicks on the same finding converge on the **same** PR — the branch name is deterministic from `<rule_id>/<file>`. Re-running the fix after the file changed pushes an updated commit to the same branch.
* The web app re-scans the freshly-fetched file before applying the fix. If the finding has been remediated manually since the last scan, you get a clean "already remediated — nothing to change" response instead of an empty PR.

Same eight workflow rules are covered in both places — see the "What gets fixed" table above.

## Recommended workflow

<Steps>
  <Step title="Commit clean">
    `--fix` mutates files. Start from a clean working tree so `git diff` shows only the fixer's changes.
  </Step>

  <Step title="Run the fixer">
    ```bash theme={null}
    pipefort -p . --fix
    ```
  </Step>

  <Step title="Review the diff">
    ```bash theme={null}
    git diff .github/workflows/
    ```

    Especially check the SHA pinning and PPE rewrites — both involve nontrivial structural changes.
  </Step>

  <Step title="Commit and push">
    The fixer leaves a comment after each pinned SHA so reviewers can see the original tag.
  </Step>
</Steps>
