After a supply chain incident, look past the one bad package and check the conditions that let it in: unpinned versions, install time code execution, broad CI permissions, a stale inventory, and no delay or review before new versions were trusted. Each is a contributing factor, not a single root cause.
Why a checklist helps
Teams under pressure stop at the first plausible cause: "a maintainer account was taken over". That may be true, and it is outside your control. The contributing factors inside your control are where the useful actions live. Google's SRE guidance asks reviewers whether the root cause was sufficiently deep. A checklist makes depth routine.
Use it as prompts, not a scorecard. Not every item applies to every incident.
The checklist
How versions were chosen
- Were versions resolved from a lockfile, or floating on each build?
- Did a range such as a caret or tilde pull the new version in without a human decision?
- Was there any waiting period between a version being published and being allowed in?
What the package could do
- Did the package run code at install time?
- Did your build allow install scripts for every dependency by default?
- Did the code run in an environment holding secrets?
What the pipeline allowed
- Were CI tokens scoped to the minimum needed?
- Were third party build steps referenced by a mutable tag? GitHub's hardening guide recommends pinning actions to a full length commit SHA, noting an attacker would need a SHA-1 collision to alter a pinned reference.
- Could one job reach secrets it did not need?
What you could see
- Could you answer "where is this package used?" within an hour?
- Did your inventory include transitive dependencies and build tooling?
- Were developer machines and CI runners in scope of that inventory?
How you responded
- Was there a named owner with authority to block the package?
- Did the runbook cover rotating secrets exposed to the build?
- Was customer communication drafted from facts or from guesses?
Turn answers into findings
For each "no" or "unsure", write one sentence of finding in system language. For example: "Builds resolved floating ranges for a transitive dependency, so a new version entered without review." Then decide if it needs an action, an accepted risk with a named owner, or nothing.
Do not try to fix every item. Pick the two or three that would have changed the outcome the most and put those through your normal planning process.
Keep the weight right
A list of twenty actions that nobody finishes is worse than three that ship. If the finding is real but the fix is large, record the risk, assign an owner, and set a review date.
Quick check
Is the compromised maintainer account the root cause?
It is a cause outside your control. Focus on the contributing factors you can change, such as pinning, delays and pipeline permissions.
Do we have to action every unchecked item?
No. Prioritise the factors that would have changed the outcome, and record the rest as accepted risk with an owner.
Sources
- Google SRE book, Postmortem Culture: Learning from Failure
- GitHub Docs, Security hardening for GitHub Actions
Last reviewed: 19 October 2026.