> ## 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 — Secret printed to logs or written to step output

> An inline script echoes a ${{ secrets.* }} value or writes it to $GITHUB_OUTPUT/$GITHUB_ENV, defeating log masking.

| Field    | Value                                                                                                                                                |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Category | `CICD-SEC-6`                                                                                                                                         |
| Rule ID  | `cicd-sec-6-secret-in-run-output`                                                                                                                    |
| 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 the check does

Flags inline `run:` scripts that expose a `${{ secrets.* }}` value, in any of these shapes:

* a print-family command — `echo`, `printf`, `print`, `cat`, `console.log` — whose line interpolates a secret
* a secret redirected into `$GITHUB_OUTPUT` or `$GITHUB_ENV`
* the legacy `::set-output` workflow command carrying a secret

A plain assignment that passes a secret to a step via `env:` is **not** flagged — that's the recommended pattern.

## Why it matters

GitHub masks a secret in logs only where it sees the **exact** registered value. The moment a script prints it (often base64'd, URL-encoded, or concatenated), masking no longer matches and the plaintext lands in logs that anyone with read access to the run can see. Writing a secret to `$GITHUB_OUTPUT`/`$GITHUB_ENV` is worse: it persists the value for later steps (and, for outputs, downstream jobs) as plaintext, far outside the step that needed it.

## Vulnerable examples

```yaml theme={null}
- run: echo "${{ secrets.API_KEY }}"                       # printed to logs
- run: echo "token=${{ secrets.API_KEY }}" >> "$GITHUB_OUTPUT"  # persisted as output
- run: echo "::set-output name=tok::${{ secrets.TOKEN }}"  # legacy output command
```

## Safe alternative

Pass the secret to the consuming step through `env:` and reference it from the environment — never echo it:

```yaml theme={null}
- env:
    API_KEY: ${{ secrets.API_KEY }}
  run: curl --oauth2-bearer "$API_KEY" https://example.com
```

If a derived value must outlive the step, store it as a masked secret rather than writing plaintext to outputs.
