To audit a workflow for secret exposure, check seven things: how secrets are stored, whether derived values are masked, how secrets reach commands, what the logs show, which third party actions run, what permissions the token has, and which environment rules guard production credentials. This checklist follows GitHub's secure use guidance.
1. Storage shape
GitHub says not to put several values in one JSON, XML, or YAML blob, because redaction depends on exact matches. Create a separate secret for each value. Never store sensitive data in workflow files; mask non-secret sensitive values with the add-mask command.
2. Derived values
Register any value generated from a secret, such as a signed JWT, as a secret too. Do the same for encoded versions, such as base64 or URL encoded. A masked original does not mask its transformed copy.
3. How secrets reach commands
GitHub advises avoiding passing secrets between processes on the command line where possible, since command line processes may be visible to other users and captured by audit events. Prefer environment variables or standard input. If a command line is unavoidable, quote the variable.
4. Logs
Secret redaction happens on the runner and only applies to secrets used in that job. Run the workflow with valid and invalid inputs and read the logs, because error output can print credentials. If an unredacted secret appears, delete the log and rotate the secret.
5. Third party actions
Actions run with your job's access. GitHub prefers a lockfile that records the exact commit of every action, including transitive ones. If you cannot use that, pin to a full length commit SHA, and verify the SHA comes from the action's own repository, not a fork. Pin to a tag only if you trust the creator, since tags can be moved. Audit the source whichever way you pin. Note that Dependabot does not create vulnerability alerts for actions pinned to SHA values, so track those updates yourself.
6. Token permissions
Set GITHUB_TOKEN to read-only by default for repository contents and raise permissions per job. Use OIDC instead of stored cloud keys. Remember that anyone with write access can read repository secrets through a workflow, so keep that group small.
7. Environment rules
Use environments with required reviewers so jobs cannot reach environment secrets until someone approves, and add deployment branch rules.
The audit table
| Check | Pass condition | Fail sign |
|---|---|---|
| Storage | One value per secret | JSON blob in a secret |
| Derived values | Registered as secrets | Encoded copy appears in logs |
| Command lines | Env var or stdin | Secret in a command argument |
| Logs | Reviewed after test runs | Never read |
| Actions | Pinned, audited | Floating tags from unknown owners |
| Permissions | Read-only default, job level raises | Write-all token |
| Environments | Reviewers and branch rules | Prod secrets at repository level |
Try it yourself
Click through it at your own pace, or open it full screen. See how a check can hold a pull request, then think about turning the table above into a check on workflow file changes.
Steps to make it routine
- Run this audit on your five most privileged workflows first.
- Record findings and owners.
- Fix storage and permissions issues first; they are quick wins.
- Add a review rule that any change to a workflow file needs a second reviewer.
- Repeat every quarter and after any incident.
Quick check
Does pinning an action to a SHA make it safe?
It makes it immutable, not trustworthy. You still need to audit the code at that SHA.
Is masking enough protection?
No. It matches exact values only and applies only to secrets used in that job. Treat masking as a safety net.
Safeguard policies and gates can review pull requests that change workflow files, and SBOM and dependency scanning cover what the build pulls in. A free scan is at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.