To inventory CI secrets on GitHub, run the CLI's secret list command for each scope: repository, each environment, the organization, and the Dependabot and Codespaces stores. It returns names, visibility, and updated time, not values. Then add the missing columns yourself: owner, what it unlocks, and when it was last rotated.
Why inventory first
You cannot rotate what you cannot find. After an incident, the first question from responders is who had access, when the secret was used, and when it was last rotated. OWASP's secrets guidance lists exactly these as the information you need for investigation. An inventory built before the incident answers them in minutes.
What the command shows
The GitHub CLI manual lists the flags for the list command: one for the application store (actions, agents, codespaces, or dependabot), one for an environment, one for an organization, one for your user, and a repository selector. The JSON output fields documented are name, numSelectedRepos, selectedReposURL, updatedAt, and visibility. There is no field for the value.
gh secret list --repo OWNER/REPO
gh secret list --repo OWNER/REPO --env production
gh secret list --org OWNER
gh secret list --repo OWNER/REPO --app dependabot
Run these across every repository you own. A short loop over the repository list gets you a table in a few minutes.
Build the register
| Column | Source |
|---|---|
| Name and scope | The list output |
| Last updated | updatedAt from the output |
| Visibility | Output; for organization secrets, which repositories can use it |
| What it unlocks | Ask the owning team; check workflow files that reference it |
| Owner | A named person or team |
| Replace with OIDC? | Decide per secret |
| Next rotation due | You set this |
Treat the register as sensitive. It maps your attack surface even though it holds no values.
Steps
- List repositories, including archived ones. Old repositories keep secrets alive.
- Run the list command for each scope and merge the output into one sheet.
- Search workflow files for references to each name, and note secrets that are defined but never used.
- Mark candidates for deletion. GitHub's guidance says to review registered secrets regularly and remove unneeded ones.
- Mark cloud keys as candidates to replace with OIDC, which lets you stop storing long lived credentials as secrets.
- Assign owners and set rotation dates.
- Re-run monthly and diff against the last run. New names and stale update dates are your signals.
Gaps to check
The CLI covers GitHub. Your pipeline may also hold credentials in a vault, a container registry, a package registry, or another CI tool. Add those sources to the register by hand. Do not forget Jenkins credential stores, GitLab variables, and any scripts on build servers.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how a recurring check surfaces changes over time, which is the same habit that keeps an inventory from going stale.
Quick check
Can the list command reveal a secret value?
No. The documented fields are metadata only. Values cannot be read back from the settings page either.
How often should I refresh the inventory?
Monthly is a reasonable start, and again after any departure or incident. Adjust to how often your team creates secrets.
Safeguard asset discovery can help you find the repositories and pipelines that belong in this register, and compliance and evidence automation keeps the results as dated evidence. A free scan is at gold.safeguard.sh/scan.
Sources
- GitHub CLI manual: gh secret list
- GitHub Docs: Secure use reference
- OWASP Secrets Management Cheat Sheet
Last reviewed: 19 October 2026.