| Field | Value |
|---|---|
| Category | CICD-SEC-6 |
| Rule ID | cicd-sec-6-secret-in-run-output |
| Severity | HIGH |
| OWASP | CICD-SEC-6: Insufficient Credential Hygiene |
| Auto-fix | ✗ |
What the check does
Flags inlinerun: 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_OUTPUTor$GITHUB_ENV - the legacy
::set-outputworkflow command carrying a secret
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
Safe alternative
Pass the secret to the consuming step throughenv: and reference it from the environment — never echo it: