Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXCVI · Incident: Malicious DependencyIncidentResponse

Rebuild trusted artifacts — clean source, pinned versions

Advanced⏱ ~28 mingit

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

Not yet marked complete on this device.

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:

GateQuestionEvidence
Clean lockfileDoes the lockfile pin the known-good version with matching integrity hash?Lockfile diff against pre-malicious commit
Clean runnerWas the artifact built on a runner the hook never touched?Runner registration time after pool rebuilt
Clean signing keyWas 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

  1. Three gates, not one. A single gate is not a rebuild.
  2. Re-build by default; re-release when source changed.
  3. Signing key rotation before rebuild.
  4. Admission policy is the deploy gate.
  5. 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

  1. 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?

  2. Q2. A rebuild that uses the original lockfile but with the malicious transitive bumped to the known-good version passes the clean lockfile gate.

  3. Q3. Name the three verification gates the rebuild must pass and state what each gate answers.

  4. 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.