What the check does
Two detection paths: 1. Suspiciousenv: 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:
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:Safe alternative
Store the value in GitHub Secrets (or org/environment secrets) and reference it via expression: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:
- Replaces every occurrence with a shell-expandable
$ENV_NAMEreference, whereENV_NAMEis derived from the secret type:- GitHub PAT (
ghp_*) →$GH_TOKEN - Slack token (
xoxb-*) →$SLACK_TOKEN - AWS access key (
AKIA*) →$AWS_ACCESS_KEY_ID
- GitHub PAT (
- 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).
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.