Git, CI/CD & GitOpsXCVI · Incident: Malicious DependencyIncidentResponse
Rebuild trusted artifacts — clean source, pinned versions
What you'll learn
- Sequence the rebuild: re-trigger from clean commits, regenerate the lockfile, build on a clean runner, re-sign, re-deploy
- Apply the three verification gates: clean lockfile, clean runner, clean signing key
- Distinguish a re-build (same source, new lockfile) from a re-release (new source) and pick the right one for each artifact class
- Document the rebuild with lockfile diff, commit hash, and signing-key identifier that survives the audit
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Invalidation is the warning. The rebuild is the fix. A team that invalidates the compromised artifacts but does not rebuild and re-deploy leaves production running on the bytes the malicious hook may have tampered with. The rebuild runs from a clean commit, with a regenerated lockfile pinned to a known-good version, on a clean runner, signed with a new key.
The rebuild sequence
The sequence is forced by the supply-chain constraint that every step must be reproducible from a trusted input:
flowchart LR
A["invalidated artifacts"] --> B["re-trigger from clean commit"]
B --> C["regenerate pinned lockfile"]
C --> D["build on clean runner"]
D --> E["re-sign with new key"]
E --> F["deploy with admission control"]
F --> G["three gates passed"]
- Re-trigger from clean commit. The workflow is re-run, but not on the compromised build environment.
- Regenerate pinned lockfile. The lockfile is regenerated with the known-good version of the malicious transitive.
- Build on a clean runner. A fresh runner that has never run a job for this workflow.
- Re-sign with a new key. The previous key is in the credential inventory and must be rotated.
- Deploy with admission control. The cluster’s image admission policy rejects the invalidated artifacts.
The three verification gates
Each gate answers a question about a different link:
| Gate | Question | Evidence |
|---|---|---|
| Clean lockfile | Does the lockfile pin the known-good version with matching integrity hash? | Lockfile diff against pre-malicious commit |
| Clean runner | Was the artifact built on a runner the hook never touched? | Runner registration time after pool rebuilt |
| Clean signing key | Was the signature made by a key the attacker never held? | Key rotation timestamp before rebuild |
Regenerating the lockfile
The lockfile regeneration is the supply-chain change the audit team will examine most closely:
npm install "popular-logging-library@4.3.9" \
--package-lock-only --save-exact
npm ci
The npm ci step is the verification: a CI command
that fails on drift will fail if the lockfile and the
package.json range are inconsistent.
Re-build versus re-release
The team chooses between two operations for each artifact:
- Re-build. The source is the same commit. The bytes may differ because the dependency tree differs. The default.
- Re-release. The source is a newer-known-good commit with a security fix. The right choice when the original source contained a vulnerability.
A re-release requires a code change, PR, and review. A re-build requires a clean runner and re-trigger.
The signing key rotation
The signing key the previous build used is in the credential inventory from lesson 04:
cosign generate-key-pair
cosign sign --key new-key.pem "$IMAGE_REFERENCE"
The new key is stored in the secret manager with a key rotation timestamp that survives the audit.
Deploying with admission control
The rebuilt artifact is deployed through the cluster’s image admission policy:
apiVersion: security.k8s.io/v1
kind: ImagePolicyWebhook
metadata:
name: production-image-policy
webhook:
imagePolicy:
allowWhenInsecure: false
allowedRegistries:
- production-registry.example.com
The policy rejects the invalidated digests and accepts the new signed ones.
Production discipline
- Three gates, not one. A single gate is not a rebuild.
- Re-build by default; re-release when source changed.
- Signing key rotation before rebuild.
- Admission policy is the deploy gate.
- Lockfile diff accompanies the rebuild. The diff is the audit artefact.
Cross-course references
- Git, CI/CD & GitOps — Part LXVII-06 covers the lockfile shape.
- Git, CI/CD & GitOps — Part XCV-05 covers the rebuild sequence.
- Container Security for Production Sysadmins — Part V covers the registry-side validation patterns.
Quiz
Knowledge check · 4 questions
Q1. A rebuild is being planned for a container image built from a lockfile that resolved the malicious version. The lockfile update to a known-good version has been committed. The signing key the previous build used is in the credential inventory. When must the signing key be rotated?
Q2. A rebuild that uses the original lockfile but with the malicious transitive bumped to the known-good version passes the clean lockfile gate.
Q3. Name the three verification gates the rebuild must pass and state what each gate answers.
Q4. A production container image has been invalidated. The rebuild plan runs on a fresh Kubernetes runner pod, from the same commit hash, with a regenerated lockfile pinned to the known-good version, signed with the rotated cosign key. Walk through the gates and identify what is missing.
The runner pool was rebuilt at 09:00. The new runner pod is registered at 09:14. The workflow re-runs at 09:16 from commit a3f9d2c. The lockfile was regenerated at 09:18 with the known-good version. The cosign key was rotated at 08:45 and the new keypair is stored in the secret manager. The image is pushed at 09:23 and signed at 09:24.
Passing score: 75%. Answers are checked in this browser.