Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXI · CI/CD Threat ModellingAttack

The malicious commit attack — the insider or compromised PR scenario

Advanced⏱ ~24 mingit

What you'll learn

  • Describe the malicious-commit attack: a PR that passes review, builds green, and ships a backdoor
  • Distinguish an insider attack from a compromised-insider attack by what the audit trail can and cannot show
  • Identify the detection signals: build behaviour that diverges from the commit, secrets accessed at unusual times, network egress to unexpected destinations
  • Apply controls — branch protection, required reviewers, signed commits, behavioural diffing — that raise the cost of a successful malicious-commit attack

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.

The malicious-commit attack is the canonical CI/CD attack. A pull request lands a commit that contains a backdoor, a secret exfiltration, or a credential bypass. The PR passes review because the diff looks like a routine change. The build succeeds because the backdoor is hidden in code that compiles. The CI pipeline runs because the workflow is triggered by the merge. The artifact is published because the pipeline is the team’s authorised publisher. The attestation is generated because the pipeline is the team’s authorised signer. The audit trail shows a clean PR, a green build, a signed artifact, and a backdoor.

The attack in three steps

The attack has three steps. Each step is on the legitimate path; each step produces an artefact the team is trained to trust.

  1. Land the commit. The attacker opens a pull request with a routine-looking diff. The diff includes a backdoor — a new dependency with a typosquatted name, a subtle change to a permission check, a credential that is logged to an attacker-controlled endpoint. The PR passes review because reviewers see the diff, not the behaviour. Branch protection is configured; required reviewers approve; the merge proceeds.
  2. Run the build. The merged commit triggers the CI pipeline. The pipeline checks out the commit, pulls the new dependency, builds the artifact, runs the tests, and pushes the artifact. The tests pass because the tests do not exercise the backdoor’s payload path. The artifact is signed by the team’s build platform. The attestation is generated.
  3. Deploy the artifact. The deployment pipeline pulls the signed artifact from the registry, verifies the signature, and deploys it. The verifier passes because the signature is valid. The backdoor runs in production.
flowchart LR
    A["Malicious PR"] --> B["Review (passes)"]
    B --> C["Merge"]
    C --> D["Build"]
    D --> E["Test (passes)"]
    E --> F["Sign"]
    F --> G["Registry"]
    G --> H["Verify (passes)"]
    H --> I["Deploy"]
    I --> J["Production (backdoor)"]
    D -. "backdoor hidden in code path" .-> J
    F -. "signature valid because platform signed" .-> G

Every step in the diagram is on the legitimate path. Every artefact produced is signed and verified. The backdoor arrives in production not because any control failed but because the control checked the wrong thing. The signature says “this artifact was built by our pipeline”. The attestation says “this artifact came from commit X”. Both are true. Neither says “this artifact does not contain a backdoor”.

Insider versus compromised insider

The attacker behind the malicious commit is one of two profiles. They look identical to the audit trail, but they have different upstream causes and different recovery paths.

  • Insider. The attacker is a developer on the team with legitimate commit rights. The motivation is direct: financial, ideological, coerced, or compromised at the personal level. The audit trail shows a clean PR by a known developer; the recovery path involves HR, legal, and revocation of the developer’s access.
  • Compromised insider. The attacker has stolen a developer’s PAT, SSH key, or session cookie. The motivation is the external operator’s. The audit trail shows the same clean PR by the same known developer; the recovery path involves identity rotation, session revocation, and forensic reconstruction of what the external operator did.

The two profiles are not distinguishable from the repository alone. They are distinguishable from the behaviour: an insider knows the repository and submits a diff that fits the team’s style; a compromised insider may submit a diff that breaks conventions or operates at unusual hours. The behavioural diff is a weak signal but it is the only signal the audit log can produce.

Detection signals

The signals that distinguish a malicious commit from a clean commit are not in the build log. They are in the behaviour of the build and the behaviour of the artifact.

  • Build behaviour that diverges from the commit. A commit that adds a single function should not pull a new network dependency. A commit that fixes a typo should not invoke a new shell process. A commit that touches one file should not change the dependency tree. Behavioural diffing — comparing the network egress, the process tree, the filesystem writes of the new build against the previous build — is a signal that the attacker cannot easily fake.
  • Secrets accessed at unusual times. A CI job that reads the production deploy role during a unit-test step is a CI job that is doing something the team did not expect. The access pattern is in the cloud audit log; the alert is a comparison against the job’s declared scope.
  • Network egress to unexpected destinations. A build that connects to an IP range outside the team’s VPC, or to a domain not on the allowlist, or over a port not on the allowlist, is a build that is doing something the team did not expect. The signal is in the egress log.
  • Anomalous dependency resolution. A new transitive dependency, a sudden upgrade of a package, or a dependency fetched from a registry not on the allowlist is a signal that the dependency tree has changed in a way the team did not request. The signal is in the SBOM diff.

The four signals are not exhaustive. They are the canonical signals a CI/CD defender monitors for. Each is a behavioural comparison, not a syntactic check. Each requires the defender to know what normal looks like.

Controls that raise the cost

The controls that make a successful malicious-commit attack expensive fall into four groups.

  1. Branch protection. Direct commits to the default branch are forbidden. Every change goes through a pull request. Required reviewers are configured for the directories the change touches. Code owners are enforced. The attacker must convince a reviewer.
  2. Signed commits. Every commit is signed by the author’s key. The signature binds the commit to an identity the team controls. A commit by an unknown key is rejected at merge time. The attacker must compromise a developer’s signing key — a higher bar than compromising a developer’s account.
  3. Behavioural diffing. The CI pipeline records the network egress, process tree, filesystem writes, and dependency resolution of each build. The defender is alerted when a build diverges from the baseline. The attacker must produce a backdoor that does not change the build’s behaviour — a higher bar than producing a backdoor that compiles.
  4. Runtime canaries. Production includes canary inputs that exercise unusual code paths and verify the artifact’s response. A backdoor that activates on a canary is detected at runtime. The attacker must produce a backdoor that ignores canaries — a higher bar than producing a backdoor that runs.

The four groups together raise the cost of a successful attack from “convince a reviewer” to “convince a reviewer, forge a signature, mimic the baseline, and avoid every canary”. Each control closes a path. The chain is the four together.

Production discipline

  1. Branch protection is the floor. Direct commits to the default branch are forbidden; every change goes through a PR with required reviewers and code-owner enforcement.
  2. Signed commits bind author to key. A commit by an unknown key is rejected at merge time; the attacker must compromise a signing key, not just an account.
  3. Behavioural diffing raises the cost. The build’s network egress, process tree, and dependency tree are compared against the baseline; a divergence is an alert.
  4. Runtime canaries catch what tests miss. Production exercises unusual inputs and verifies the artifact’s response; a backdoor that ignores canaries is a backdoor the attacker must engineer.

Cross-course references

  • Git, CI/CD & GitOps — Part LXXI-01 (Threat Model) is the surfaces this lesson exploits.
  • Git, CI/CD & GitOps — Part VIII (Branches and Protection) covers the branch protection model in detail.
  • Git, CI/CD & GitOps — Part XXIV (Signed Commits) covers the commit-signing model.
  • Container Security for Production Sysadmins — Part VI (Runtime Detection) covers the runtime canary pattern.

Quiz

Knowledge check · 4 questions

  1. Q1. What makes a successful malicious-commit attack most difficult to detect from the build log alone?

  2. Q2. A green CI pipeline is not sufficient evidence that a commit is safe to deploy.

  3. Q3. Name the four detection signals that distinguish a malicious commit from a clean commit, and the log each signal is sourced from.

  4. Q4. Diagnose the attack class, the audit gap, and the controls that would have made this attack expensive.

    Team T's CI pipeline runs on a hosted build platform with ephemeral runners and short-lived OIDC credentials. Branch protection requires two reviewers and code-owner approval. A developer on the team opens a PR that adds a new feature flag and includes a one-line change to a permission check in an adjacent file. The change reduces a permission scope from 'read' to 'none' for an unused service account. The change passes review because the diff is small, the developer is trusted, and the code owner approves the feature-flag file. The build runs; tests pass; the artifact is signed by the platform's key. The artifact deploys to production. Six weeks later, a security audit notices that the unused service account has been assumed by an external identity.

Passing score: 75%. Answers are checked in this browser.