Skip to main content

Catch a compromised CI dependency

SHA-pinning is supposed to make a dependency immutable. But in the 2025–2026 trivy-action and tj-actions incidents, attackers force-pushed the tags themselves — so a pipeline pinned to a tag silently started running credential-harvesting code on its next trigger: sweep /proc/<pid>/environ, read cloud credentials, exfiltrate. The pin didn't move; the code under it did.

In this session the developer doesn't trust a pinned build-helper and asks Claude to find out what it actually does before shipping. Claude runs it under cilock and lets the kernel tell the truth.

Loading recorded terminal session…

What it does, step by step

  1. Run the pinned step under CI/lock. cilock run --step ci-task --trace -a environment,secretscan -- ./build-helper.sh wraps the dependency. --trace turns on kernel-side capture (eBPF here, with ptrace+seccomp fallback where eBPF can't load); -a secretscan adds content scanning. The signed attestation records what actually ran.
  2. Open the evidence. Claude pulls the captured process tree straight from the command-run attestation — and there it is: cat /proc/self/environ, cat aws-credentials, and curl http://169.254.169.254/… (the cloud-metadata endpoint), with the real connect() recorded. The secretscan attestation flags a leaked github-pat.
  3. Visualize it. A terminal diagram links the dependency to the harvested files, the metadata SSRF, and the leaked token — feeding the policy decision.
  4. Show the policy. The signed policy's no-leaked-secrets Rego rule blocks any step that leaks a credential.
  5. Verify → blocked. cilock verify returns Verification failed — credential leak detected in CI step and exits non-zero. See policy verification.
  6. Write the incident report. The evidence that mattered — the process tree, the metadata connection, the leaked secret, the rule — is captured into a markdown incident report: the durable, auditable record of exactly what happened.

The detection is layered and doesn't depend on the dependency cooperating: behavioral evidence (eBPF process tree + network) and content evidence (secretscan), both observed at the kernel.

  • secretscan — Gitleaks over the run output, with recursive base64/hex/URL decoding; can fail the build on detection.
  • The same --trace capture records every connection (destination, port, TLS SNI) in the command-run attestation, so a Rego rule can fail a step that connects to a destination outside an allowlist — here, the cloud-metadata IP.
  • Pair with provenance and scanning from the internal supply-chain use case: Trivy, Grype, Syft SBOMs, Semgrep.
  • Restrict which actions may run at all with a signed Rego policy over the github-action / gitlab attestations — defense in depth alongside this behavioral check.
SHA-pinning is necessary, not sufficient

A pin protects you from a different artifact. It does nothing when the tag you pinned to is force-pushed to new code. Behavioral + content evidence, gated by a signed policy, catches the attack the pin can't see.