Ask what the job could read, how third party steps were referenced, which tokens it held, what it could write to, and what logs remain. CI runners are where bad dependencies run with the most access, so they deserve their own section in the review.
Why CI gets a separate section
A bad dependency on a laptop is bad. A bad dependency in CI often runs next to deploy keys, registry tokens and cloud roles. It can also write into artifacts you ship. So the same incident can have a small footprint on paper and a large one in practice.
The questions
Access
- Which secrets were available to the job, by name?
- Were they available to every step, or only the step that needed them?
- What permissions did the job's automatic token hold? GitHub's hardening guide advises granting the minimum permissions needed.
- Could pull request builds from forks or branches reach secrets?
Third party steps
- Were reusable build steps referenced by a mutable tag or a pinned commit? The GitHub guide recommends a full length commit SHA, and explains why: changing a pinned reference would require a SHA-1 collision.
- Who reviews updates to those pins?
- Did any step download and run a script at build time?
Outputs
- What could the job write to: package registries, image registries, release pages?
- Were artifacts signed or given build records, and could the bad code alter those?
- Was the cache shared between trusted and untrusted jobs?
Runners
- Were runners fresh for each job, or reused?
- If reused, what could persist between jobs?
- Who can change the runner image?
Evidence
- How long do build logs last?
- Do logs show the resolved version of each dependency?
- Do they show outbound network connections?
Format the answers
| Question | Answer | Gap | Action |
|---|---|---|---|
| Secrets in scope | Registry token, cloud role | Token was reachable from all steps | Scope to publish step |
| Third party step references | Mix of tags and pins | Tags can move | Pin and review updates |
Keep the table short. Each gap should lead to an action or to an accepted risk with an owner.
Rotate with a list
For every secret the job held, record whether you rotated it. Rotate in a safe order, replacing the credential in the consumer before revoking the old one, so you do not break your own deployments while responding. Note the time of each rotation.
Check what the job produced
Do not stop at the credentials. Inspect images, packages and releases built during the exposure window, as described in your artifact recall table. If CI could publish, treat published outputs as suspect until checked.
Quick check
Why pin third party build steps to a commit?
A tag can be moved to different code. A full commit SHA cannot be altered without a hash collision, so GitHub recommends it.
Should every step see every secret?
No. Give each step only the secrets it needs, ideally scoped to the single step that uses them.
Sources
Last reviewed: 19 October 2026.