List every CI secret, note which jobs use it and who can read it, remove those not needed, move the rest into narrower scopes such as protected environments, check that logs do not print them, and rotate them on a schedule. A small team can complete a first pass in about an hour.
Step 1: list what exists
Secrets hide at several levels. On GitHub they can live at repository, environment and organization level. On GitLab, variables can live at project, group and instance level. Walk each level and record, for every entry, its name, where it is stored, what it unlocks, and who created it.
A spreadsheet is enough. Add columns for owner, last rotated and rotation method.
Step 2: find out who can read each one
GitHub's secure use reference gives a fact many teams miss: any user with write access to your repository has read access to all secrets configured in it. That means the people who can push a branch and edit a workflow can read repository secrets by writing a step that uses them.
So for each secret ask, "Who has write access, and would we trust all of them with this?" If the answer is no, narrow it.
Step 3: narrow the scope
| Platform | Narrowing control |
|---|---|
| GitHub | Environment secrets, which jobs only get when they reference the environment, and not before a required reviewer approves |
| GitHub | Avoid organization wide secrets shared by every repository when only one needs them |
| GitLab | Protected variables, available only to pipelines on protected branches and tags |
| GitLab | Group or project scope in place of instance scope |
A note on plans: GitHub's documentation says environment secrets in private repositories need a paid plan, so check the current plan table. See requiring approval before a deploy.
Step 4: check the logs
GitHub's docs warn that automatic redaction is not guaranteed, because a secret can be transformed in several ways. Run each workflow with valid and invalid inputs and read the logs for leaks. Specific checks:
- Is any secret stored as a blob of JSON, XML or YAML? Redaction needs exact matches, so split these into individual secrets.
- Is any value derived from a secret, such as a signed token or a Base64 copy? Register the derived value as a secret as well.
- Do error paths print command lines or environment dumps?
GitLab adds that masking has format rules and is not a guaranteed barrier, and that values altered by the shell may not be masked. If you find a leak, follow the leaked secret runbook.
Step 5: remove, replace, rotate
- Delete secrets nothing uses. GitHub advises periodically reviewing registered secrets and removing ones no longer needed.
- Replace long-lived cloud keys with OIDC where supported, per GitHub's docs, so there is nothing to leak.
- Rotate what remains. GitHub recommends rotating periodically to shorten the window in which a compromised secret is valid. Pick an interval you will keep, and put it in your calendar.
Step 6: audit the code that touches secrets
Review the workflow files and the actions they call. GitHub's guidance is to check that secrets are not sent to unintended hosts or printed. Pin actions to commit SHAs and keep third-party actions to a short list. See reviewing a third-party action.
A repeatable cadence
Run the audit every quarter and after any person with admin rights leaves. Keep the spreadsheet in version control or a shared drive, and note what changed each time.
Where Safeguard fits
For the dependency side of a pipeline, Safeguard offers SBOM and dependency scanning, and policy gates that block risky changes at the pull request.
Click through it at your own pace, or open it full screen.
Quick check
How often should CI secrets be rotated?
Both platforms advise rotating periodically without fixing one interval. Choose one your team will keep, and rotate at once after any suspected leak.
Who should own the secrets list?
One named person for the list, plus an owner for each secret who knows how to rotate it.
Sources
- Secure use reference, GitHub Docs
- Using secrets in GitHub Actions, GitHub Docs
- Deployments and environments, GitHub Docs
- GitLab CI/CD variables, GitLab Docs
Last reviewed: 19 October 2026.