Release and respond
Respond to a dependency incident
Identify affected releases, stop new exposure, replace or remove the component, rotate reachable credentials, and communicate from verified facts.
A malicious or compromised dependency can affect source, build, artifacts, credentials, and users. Scope each layer.
This scenario is not theoretical — the 2021 ua-parser-js compromise followed exactly this shape. A package maintainer account is compromised and a malicious postinstall release runs in CI. It may affect developer clones, build logs, registry credentials, released artifacts, and users at different times. Your response has to walk each layer, in order.
First hour: freeze and preserve
Stop new exposure before investigating. Freeze releases and deployments, and pin or remove the affected version so no new install pulls it. Then preserve evidence — CI logs, artifacts, and their digests — because retention windows delete the proof while you sleep.
Establish the timeline from your own records
Find when the component entered your builds. The lockfile history answers precisely:
git log -S '"evil-parser": "2.1.4"' --oneline -- package-lock.json
# 8c1f3d2 chore: update dependencies
That commit’s date bounds the exposure window. Query the SBOMs of your releases to list which shipped artifacts contain the version:
jq '.packages[] | select(.name == "evil-parser") | .versionInfo' sbom.spdx.json
Rotate what the package could reach
Now inspect what authority the package had where it ran. A postinstall script in CI could read every environment variable in the job. So the question is not “did it steal our tokens” — you usually cannot know — it is “which credentials were present in any run that executed it”. Rotate all of them.
Deleting the dependency stops future execution but does not revoke credentials already copied. This is the most common incomplete response.
One nuance cuts scope honestly in both directions: a build that installed the package with lifecycle scripts disabled (npm ci --ignore-scripts) never executed the install-time payload. Scope every release and authority the package could reach before declaring recovery complete.
Then produce a clean release from trusted inputs, and notify affected users with specific actions and versions — verified facts, not speculation.
Write a first-hour incident timeline for a malicious package version and identify affected builds through lockfiles, SBOMs, logs, and artifact digests. Save the queries or commands that produce the affected-release list. Then include a build where the package was installed but its script was disabled and show how the impact assessment changes.
Lesson completed