> ## 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.

# CICD-SEC-6 — Hardcoded credentials

> Tokens and secrets embedded in workflow files end up in git history and audit logs.

| Field    | Value                                                                                                                                                |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Category | `CICD-SEC-6`                                                                                                                                         |
| Severity | **HIGH**                                                                                                                                             |
| OWASP    | [CICD-SEC-6: Insufficient Credential Hygiene](https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-06-Insufficient-Credential-Hygiene) |
| Auto-fix | ✓ ([what it does](#auto-fix))                                                                                                                        |

## What the check does

Two detection paths:

**1. Suspicious `env:` keys with literal values.** Any environment variable whose name contains one of `token`, `password`, `secret`, `key`, `webhook`, `passwd`, `credential` and whose value is a literal (not `${{ secrets.* }}` / `${{ ... }}`). Checked at workflow, job, and step scope.

**2. Pattern-matched literals in `run:` scripts.** Regex matches for:

| Pattern                | Example                             |
| ---------------------- | ----------------------------------- |
| GitHub PAT             | `ghp_[A-Za-z0-9]{36}`               |
| Slack bot token        | `xoxb-...`                          |
| AWS access key         | `AKIA[0-9A-Z]{16}`                  |
| Generic `var := "..."` | `token := "abcdef1234567890abcdef"` |

## Why it matters

Hardcoded credentials end up in git history, build logs, action audit logs, and anywhere the workflow file gets mirrored. Rotating them after a leak is painful; preventing the leak is cheaper.

## Vulnerable examples

**Env var with literal:**

```yaml theme={null}
jobs:
  deploy:
    env:
      API_TOKEN: "ghp_1234567890abcdefghijklmnopqrstuvwxyz1"   # ← hardcoded
```

**Token in inline script:**

```yaml theme={null}
- run: |
    curl -H "Authorization: token ghp_1234567890abcdefghijklmnopqrstuvwxyz1" \
      https://api.example.com
```

## Safe alternative

Store the value in GitHub Secrets (or org/environment secrets) and reference it via expression:

```yaml theme={null}
jobs:
  deploy:
    env:
      API_TOKEN: ${{ secrets.API_TOKEN }}
    steps:
      - run: |
          curl -H "Authorization: token $API_TOKEN" https://api.example.com
```

## Auto-fix

`--fix` handles both detection paths:

**Env-var case.** The literal value is replaced with `${{ secrets.<KEY_UPPER> }}` — e.g. `API_TOKEN: "ghp_..."` becomes `API_TOKEN: ${{ secrets.API_TOKEN }}`. The env-var name itself drives the secrets name.

**Inline-script case.** For each typed literal in the script, the fixer:

1. Replaces every occurrence with a shell-expandable `$ENV_NAME` reference, where `ENV_NAME` is derived from the secret type:
   * GitHub PAT (`ghp_*`) → `$GH_TOKEN`
   * Slack token (`xoxb-*`) → `$SLACK_TOKEN`
   * AWS access key (`AKIA*`) → `$AWS_ACCESS_KEY_ID`
2. Adds an entry to the step's `env:` block: `ENV_NAME: ${{ secrets.ENV_NAME }}` (creating the block if it doesn't exist; preserving sibling entries if it does).

Example rewrite:

```yaml theme={null}
# before
- run: |
    curl -H "Authorization: token ghp_1234567890abcdefghijklmnopqrstuvwxyz1" https://api.example.com

# after --fix
- run: |
    curl -H "Authorization: token $GH_TOKEN" https://api.example.com
  env:
    GH_TOKEN: ${{ secrets.GH_TOKEN }}
```

<Warning>
  You still need to **(a) rotate the leaked credential** (it's in git history) and **(b) create a repository or organisation secret with the matching name** before the workflow will run cleanly.
</Warning>

<Note>
  The "generic `var := "..."`" pattern is detected but **not** auto-fixed — it matches an enclosing `key := "value"` shape (Go/Make-style assignment), and blindly substituting would corrupt the surrounding syntax. Review and lift manually.
</Note>
