The @bitwarden/cli compromise and ua-parser-js happened the same way on the surface: someone published a malicious version to npm. "Supply chain attack" covers both. But structurally, they're different threats — and treating them as one risk means your mitigations work on one and leave the other wide open.
I covered the structural difference earlier today. This post is about what happens when you start measuring both surfaces at the package level — specifically, what proof-of-commitment now reports.
Two surfaces
Maintainer surface. A single person or small group controls the publish credentials. Compromise any of them — stolen token, phishing, coercion — and you control what goes to npm. The malicious release is legitimate by every build system measure. Provenance attestation will verify it faithfully, because the right pipeline ran.
Build surface. Even with the right person at the keyboard, an attacker can inject malicious code between source commit and published artifact — a compromised CI runner, a tampered npm pack step, a rogue dependency in the build. SLSA provenance closes this gap by creating a cryptographically verifiable link between artifact and source commit via a named, trusted build system.
These are independent attack vectors. A package with perfect provenance still has an exposed maintainer surface if it is controlled by a single account. A package with a large maintainer team still has an exposed build surface if nothing links the artifact to its source.
What the data looks like
I added hasProvenance tracking to proof-of-commitment (commit 1388c76, deployed May 4 2026). It reflects whether a package's latest release was published with SLSA provenance attestation. Here's a direct API check across five common packages:
curl -X POST https://poc-backend.amdal-dev.workers.dev/api/audit \
-H "Content-Type: application/json" \
-d '{"packages":["chalk","hono","axios","zod","express"]}'
Results (trimmed):
[
{ "name": "chalk", "maintainers": 1, "hasProvenance": false, "score": 75, "riskFlags": ["CRITICAL"] },
{ "name": "hono", "maintainers": 1, "hasProvenance": false, "score": 79, "riskFlags": ["CRITICAL"] },
{ "name": "axios", "maintainers": 1, "hasProvenance": true, "score": 86, "riskFlags": ["CRITICAL"] },
{ "name": "zod", "maintainers": 1, "hasProvenance": true, "score": 86, "riskFlags": ["CRITICAL"] },
{ "name": "express", "maintainers": 5, "hasProvenance": false, "score": 90, "riskFlags": [] }
]
Chalk and hono: single maintainer, no provenance. Both surfaces exposed independently. One compromised credential or one tampered build step is enough.
Axios and zod: single maintainer, provenance present. The build surface is largely closed — you can verify the artifact came from the expected commit via the expected pipeline. The maintainer surface is still fully open. Provenance will attest to a rogue release just as faithfully if the right credentials were used to trigger the build.
Express: five npm maintainers, no provenance. More accounts to compromise, but nothing linking published artifacts to source commits.
Why the independence matters
The CRITICAL flag signals concentrated maintainer control. Provenance does not change that signal — it addresses a separate risk. Adding provenance to chalk doesn't reduce the risk of a phished maintainer account. Distributing maintainers on express doesn't close the build surface.
The mistake is treating hasProvenance: true as a general safety indicator. It's a specific guarantee about artifact integrity: this binary came from this commit, through this build system. What it doesn't say: whether the commit should have been published, whether the person who triggered the build had authorization, or whether the package's ownership is stable.
If you're auditing dependencies, you need both signals. The /api/audit endpoint surfaces them together:
curl -sX POST https://poc-backend.amdal-dev.workers.dev/api/audit \
-H "Content-Type: application/json" \
-d '{"packages":["some-package"]}'
Two threat surfaces. Two independent things that have to be right. Provenance is necessary. It's not sufficient.
Previous: Two Types of npm Supply Chain Attack: What Catches Each
Top comments (0)