Join our Newsletter — 33% off our NHI Course
Home› Guides› How to Audit CI/CD Secrets Scope: Find Over-Shared…
Guide Secrets Management

How to Audit CI/CD Secrets Scope: Find Over-Shared Org, Repo and Environment Secrets

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 11 November 2026 10 min read
On this page

Most CI/CD secret problems are not leaks but reach: an organisation secret shared with every repository, a production key stored at repository level where any writer can use it, or a deployment environment that any branch can target. This guide is a focused audit task for GitHub Actions and GitLab CI/CD (platforms are listed alphabetically): build an inventory of every stored secret and variable at each level, record who and what can reach it, and tighten scope with environments, protection rules and branch restrictions. It covers scope and exposure, not leak hunting in logs or token types. It is for platform engineers, DevSecOps teams and NHI owners who run a quarterly review of pipeline credentials. For the wider platform picture see the GitHub and GitHub Actions Security Guide and the GitLab Security Guide; for Jenkins, use How to audit Jenkins credentials and secrets.

Key takeaways

  • Write access is secret access: GitHub states that any user with write access to a repository has read access to all secrets configured in it, so the repository writer list is part of every secret's access list.
  • Level decides reach: an organisation or group level secret reaches every repository or project it is shared with; a production credential belongs at environment level, behind an approval.
  • Environments gate secrets: on GitHub, a job cannot read environment secrets until a required reviewer approves it; on GitLab, protected variables reach only pipelines on protected branches or tags.
  • Metadata is enough to audit: both platforms list secret names, scope and dates through their APIs without exposing GitHub secret values, so the audit itself does not need to touch values.
  • Remove before you restrict: anything that can use OIDC federation should not be a stored secret at all, and unused secrets should be deleted, not re-scoped.

When you need this

  • Quarterly access review: as part of the credential review your secrets policy requires. See the Secrets Management Policy Template.
  • After a reorganisation or acquisition: new repositories inherit organisation or group secrets they were never meant to see.
  • Before opening a repository to outside contributors: confirm which secrets a fork or merge request pipeline could reach.
  • After a pipeline incident: scope tells you which credentials a compromised job could have read. See the Leaked Credential Response Playbook.

Before you start

  • Read-only access: on GitHub, a fine-grained token with Secrets (read) organisation and repository permission and Environments (read), or a classic token with admin:org and repo. On GitLab, the Owner role on groups and Maintainer on projects, since those roles manage variables. Use a short-lived token and revoke it afterwards.
  • A safe workspace: GitLab's project variables API returns the value attribute. Write output to an encrypted location, strip values before saving, and never paste output into tickets or chat.
  • Owner list: a map of repositories and projects to owning teams, so each finding has someone to act on it.
  • Availability: GitHub environment secrets, required reviewers and branch restrictions depend on plan and repository visibility; on GitLab, limiting a group variable to specific environments needs the Premium or Ultimate tier. Confirm the current details in each vendor's documentation before planning fixes.

Step-by-step

1. Inventory GitHub Actions secrets by level

Use the REST API (or gh api) and record name, level, created_at, updated_at and, for organisation secrets, visibility. Values are never returned.

LevelEndpointWhat to record
OrganisationGET /orgs/{org}/actions/secretsVisibility: all, private or selected
Organisation, selected repositoriesGET /orgs/{org}/actions/secrets/{secret_name}/repositoriesThe repositories each selected secret reaches
RepositoryGET /repos/{owner}/{repo}/actions/secretsName and dates
Organisation secrets visible to a repositoryGET /repos/{owner}/{repo}/actions/organization-secretsCross-check against the organisation view
EnvironmentGET /repos/{owner}/{repo}/environments/{environment_name}/secretsName and dates per environment (URL encode the name)

All of these paginate at up to 100 items per page. Also export the repository's collaborators and teams with write or higher access, because those people can read the repository's secrets through a workflow.

2. Record GitHub environment protection

For each environment that holds secrets, record the deployment protection settings from the repository's Settings > Environments page or the environments API:

  • Required reviewers: up to six users or teams; note whether "prevent self-review" is on, which stops the person who started a deployment from approving it.
  • Deployment branches and tags: no restriction, protected branches only, or selected branches and tags. Note that "protected branches only" allows every branch if the repository has no branch protection rules.
  • Administrator bypass: administrators can bypass protection rules by default; record whether bypass is disallowed.
  • Wait timer: optional, from 1 minute to 30 days.

3. Inventory GitLab CI/CD variables by level

LevelWhereWho manages it
Instance (self-managed)Admin > Settings > CI/CD > VariablesAdministrators
Group (inherited by subgroups and projects)Group Settings > CI/CD > Variables, group-level variables APIOwner role
ProjectProject Settings > CI/CD > Variables, GET /projects/:id/variablesMaintainer role
Pipeline filevariables: in .gitlab-ci.ymlAnyone who can push; visible to all users with repository access

For each variable record key, variable_type, protected, masked, hidden, raw and environment_scope, and drop value from your saved output. Search every .gitlab-ci.yml for credential-like values in variables: blocks, since GitLab advises adding secrets in the UI rather than the file.

4. Record GitLab protection settings

  • Protected branches and tags: list them per project, because a protected variable reaches any pipeline on any of them.
  • Environment scope: * (the default) means every environment; production credentials should carry a specific scope.
  • Pipeline variables: record the project's minimum role for overriding variables (ci_pipeline_variables_minimum_override_role). Pipeline variables take precedence over project, group and instance variables.

5. Score each secret against the scope rules

The rules below are NHI Mgmt Group recommendations. Flag any secret that breaks one.

RuleGitHub checkGitLab check
Production deploy credentials live at environment levelProduction keys stored as repository or organisation secretsProduction keys with scope * or without protected
Shared secrets go to named consumers onlyOrganisation secrets with visibility all or privateGroup variables inherited by projects that never reference them
Deployments need a second personProduction environment with no required reviewers, or self-review allowedProtected branches where developers can push or merge without review
Only trusted refs reach production secretsEnvironment with no branch restrictionWildcard protected branch patterns that match feature branches
Federation replaces stored cloud keysCloud access keys stored where OIDC is availableCloud access keys stored where ID tokens are available
No stale secretsupdated_at older than your rotation period, or never referenced in workflowsKeys not referenced in any pipeline file
One value per secretJSON, XML or YAML blobs stored as one secretValues that cannot be masked (multi-line or short)

To check whether a secret is used, search workflow and pipeline files for its name (secrets.NAME in GitHub Actions, $NAME in GitLab). A secret nobody references is a deletion candidate.

6. Fix in order of reach

  1. Delete unused secrets after confirming with the owner. Rotate the value at the issuing system first if it may have been widely reachable.
  2. Replace stored cloud keys with OIDC federation. See the GitHub Actions OIDC Federation Hardening Guide.
  3. Narrow organisation and group sharing: change GitHub organisation secrets to selected repositories; move GitLab group variables used by one project down to that project.
  4. Move production credentials into environments: on GitHub, create the environment, add required reviewers with self-review prevented, restrict deployment branches and disallow administrator bypass; on GitLab, mark the variable protected and set its environment scope.
  5. Reduce writers and pipeline overrides: trim repository write access on GitHub, and on GitLab raise the pipeline variable override role or set it to no_one_allowed where pipeline inputs can be used instead.

Verify it worked

  • Rerun the inventory and confirm every flagged row is resolved or has a documented exception with an owner and end date.
  • Run a test deployment from a non-approved branch: GitHub should refuse to run the job against the protected environment, and a GitLab pipeline on an unprotected branch should not receive protected variables.
  • On GitHub, start a deployment and confirm the job waits for a reviewer before environment secrets are available, and that the initiator cannot approve it when self-review is prevented.
  • Check the platform audit log for the changes you made, so the review itself is evidenced.

Rollback and common errors

  • Deploys fail after moving a secret to an environment: the job must declare the environment (environment: in the job). Add it rather than moving the secret back.
  • Name clashes: GitHub repository and organisation secrets with the same name can mask which one a job used; GitLab resolves clashes by its documented precedence, with project above group above instance. Rename to one clear name per level.
  • Protected variable missing in merge request pipelines: this is expected unless you have deliberately allowed merge request pipelines to access protected variables. Keep it that way for fork contributions.
  • Masking refused: GitLab masked values must be a single line of at least 8 characters with no spaces. Use a file-type variable or an external secrets manager instead of weakening the value.
  • Rollback: keep the old secret for a short overlap period while the new scope is tested, then delete it. Record the change and date in your inventory.

Hardening afterwards

  • Schedule the audit: run the inventory script quarterly and diff it against the last run. Our recommendation is to treat any new organisation-wide or group-wide secret as a finding until justified.
  • Policy as code: make "production secrets only in protected environments" a written rule in your pipeline standard. See the CI/CD Pipeline Identity Security Guide.
  • Fork safety: GitHub does not pass secrets other than GITHUB_TOKEN to workflows triggered from forks; keep it that way by reviewing any use of privileged triggers. GitLab advises reviewing merge requests that change .gitlab-ci.yml before running pipelines on them.
  • Measure: track the number of organisation-wide secrets, the share of production credentials behind approvals, and secrets past their rotation date. See the Secret Sprawl Measurement Guide.

Practitioner checklist

  • Inventory every secret and variable at organisation or instance, group, repository or project, and environment level.
  • Record GitHub organisation secret visibility and the repositories each selected secret reaches.
  • Record GitLab protected, masked, hidden and environment scope flags, and drop values from saved output.
  • List repository writers and project maintainers next to the secrets they can reach.
  • Move production credentials into protected environments with reviewers and branch restrictions.
  • Replace stored cloud keys with OIDC federation where the platform supports it.
  • Delete unreferenced secrets after rotating any that were widely reachable.
  • Test that unapproved branches and fork pipelines cannot reach production secrets.
  • Repeat quarterly and keep the diff as audit evidence.

Standards and references

Related NHI Mgmt Group resources: GitHub and GitHub Actions Security Guide · GitLab Security Guide · How to audit Jenkins credentials and secrets · Top 10 CI/CD Security Risks · OWASP CI/CD Top 10 CICD-SEC-6 Insufficient Credential Hygiene Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 11 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org