Safeguard
Best Practices

Verifying open source package provenance with SLSA and Sigstore

A maintainer account takeover hid a backdoor in xz-utils for years. SLSA provenance and Sigstore signing are how you catch the next one before it ships.

Safeguard Research Team
Research
7 min read

On March 29, 2024, Andres Freund — a Microsoft engineer benchmarking PostgreSQL on Debian Sid — noticed SSH logins taking roughly 500 milliseconds instead of the usual 100ms, and pulled on that thread until he'd uncovered CVE-2024-3094: a remote-code-execution backdoor built into xz-utils versions 5.6.0 and 5.6.1 by "Jia Tan," a co-maintainer who had spent roughly two years earning commit access through patient social engineering. The backdoor lived in the build system, not in code a human reviewer would ever read, which is exactly why it survived. It echoed a pattern from six years earlier: in September 2018, event-stream maintainer Dominic Tarr handed publish rights to a stranger who added the malicious flatmap-stream@0.1.1 dependency, targeting the Copay Bitcoin wallet and going undetected for roughly two and a half months until developer Ayrton Sparling flagged it. Both incidents share a root cause — a trusted identity was compromised or socially engineered, and nothing downstream could tell the difference between code from that identity and code from an attacker. SLSA provenance and Sigstore signing exist to close exactly that gap. This post walks through how they work, what they would and wouldn't have caught in these two cases, and how to start requiring them in your own build pipeline.

What does SLSA provenance actually prove?

SLSA (Supply-chain Levels for Software Artifacts, an OpenSSF-hosted specification) doesn't prove code is safe — it proves an artifact was built by a specific, described process from specific, described inputs. A provenance attestation says: this binary came from commit X, built by build system Y, using build steps Z, from these materials. The current SLSA v1.0 Build Track defines three levels of increasing rigor. Level 1 just requires a provenance document to exist. Level 2 requires a tamper-resistant, hosted build service (a managed CI runner, not a laptop). Level 3 requires an isolated, parameterless build environment where the build script itself can't be manipulated by the person who triggered it. A fully hermetic, network-isolated Build Level 4 — with mandatory two-person review — was part of SLSA's pre-1.0 model and remains on the project's roadmap as a future addition, so treat it as an aspirational bar rather than something the current spec certifies. The xz-utils backdoor is the canonical illustration of why the existing higher levels matter: the malicious payload was injected via crafted test files and a build-time script that only executed during the actual release build, not during a normal source checkout — something reviewable at L3 but invisible to a source-only code review at L1.

Why couldn't code review alone have caught the xz-utils backdoor?

Because the payload wasn't in code anyone would review — it was smuggled through binary test fixtures and a modification to the build's configure script that altered how liblzma linked into sshd on Debian and Fedora builds specifically. A reviewer reading the Git history would see innocuous-looking test data and a build tweak, not an obviously malicious diff. This is precisely the failure mode hermetic, reproducible builds are designed to catch: if a build is hermetic (no network access, no unpinned inputs) and its provenance is verifiable, a downstream consumer or auditor can, in principle, rebuild the artifact and confirm the binary matches what the declared source and build steps should have produced. SLSA doesn't retroactively unmask a hidden social-engineering campaign, but a provenance requirement forces exactly the kind of build-environment transparency that made this backdoor structurally harder to hide — and it gives defenders an audit trail (which build ran, on what inputs) that a naked npm or PyPI download never carries.

How does Sigstore remove the key-management burden that used to block signing?

Traditional code signing failed to reach mass adoption in open source because it required maintainers to generate, protect, and rotate long-lived private keys — a burden most volunteer maintainers understandably skipped. Sigstore, also an OpenSSF project, solves this with keyless signing: Fulcio issues a short-lived X.509 certificate bound to a workload's OIDC identity (a GitHub Actions job, a GitLab CI pipeline) rather than a key the maintainer has to store, and Rekor logs every signing event to a public, append-only transparency log that anyone can audit. The practical effect: a CI job proves "I am the official build for this repository" using an identity token it already has, signs the artifact, and the signature is anchored publicly and permanently. If an attacker gets one maintainer's npm password — the event-stream failure mode — they still can't forge a Fulcio certificate tied to the legitimate repository's CI identity without also compromising that pipeline, which is a meaningfully higher bar than a single stolen credential.

What has adoption actually looked like, and does it work in practice?

npm's Sigstore-backed provenance went to public beta in April 2023 and reached general availability on September 26, 2023, requiring npm CLI 9.5.0 or later and a --provenance flag on npm publish from a supported CI provider. According to Sigstore's own published adoption figures from the beta period, more than 3,800 projects — including 134 classified as "high impact" — adopted provenance attestations, collectively covering more than 500 million total downloads of provenance-enabled versions. That's meaningful reach, but it's still a minority of the several million packages on npm, and provenance is opt-in per publish: a compromised maintainer account can still publish an unsigned or non-attested version unless the registry or a downstream consumer enforces the requirement. The gap between "the feature exists" and "the ecosystem requires it" is exactly where most supply-chain risk still lives in mid-2026.

How do you actually roll this out without breaking your build?

Start by requiring provenance for what you publish before requiring it for what you consume — you control your own CI identity, so this is the low-risk first step. For npm packages, that's npm publish --provenance from GitHub Actions or another OIDC-supported CI provider; npm's registry then surfaces the Sigstore attestation and Rekor link on the package page automatically. For containers, use cosign sign (or a platform equivalent) so signatures land in the OCI registry and can be checked with cosign verify at admission time. Safeguard implements this pattern directly: its safeguard sign CLI signs npm and container artifacts through Sigstore Fulcio/Rekor by default, attaches SLSA Provenance v1 and CycloneDX SBOM attestations in in-toto format, and its Kubernetes admission controller can enforce a minimum SLSA level (default L3) and a signer allow-list at deploy time, denying admission on failure. On the consumption side, run safeguard verify --artifact <image> --expect-signer "sigstore:vendor-identity" --expect-slsa-level 3 against third-party dependencies in CI, so an unsigned or wrong-identity artifact fails the pipeline before it ever reaches production — the exact control point that would have blocked both flatmap-stream and a build-tampered xz-utils from reaching a downstream deploy unnoticed.

What should a team target first: L2 or L3?

For most teams, SLSA Level 3 is the realistic, high-value target within the current spec — Level 1 provenance without a tamper-resistant build service doesn't meaningfully raise the bar against a compromised maintainer identity. L2 is a reasonable floor (any hosted CI runner like GitHub Actions or GitLab CI gets you there with minimal changes), but L3's isolated, parameterless build environment is what specifically defeats the "inject something the build script does but the diff doesn't show" pattern behind xz-utils. Safeguard's own runner maps L2 to standard hosted CI and L3 to reusable workflows or a dedicated build VM, and also offers a hermetic build mode paired with branch protection for teams that want to get ahead of where the spec's still-unratified Build Level 4 is headed — treat L3 as the default policy for anything in your deploy path, and reserve the hermetic-build conversation for your highest-value release artifacts rather than every internal package.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.