high

CVE

CVE-2026-48815

CWE

CWE-347

Affected Surface

  • Node.js build, release, admission, or artifact-validation services that call `sigstore.verify()` or `createVerifier()` with `certificateOIDs` on `sigstore <= 4.1.0`
  • JavaScript trust-policy code that expected Fulcio or workload-identifying certificate-extension OIDs to restrict which certificates may sign bundles
  • Dependency graphs that resolve `sigstore` transitively and delegate verification to that library without independently checking certificate extensions

The last several supply-chain incidents have taught teams not to confuse provenance with trust. CVE-2026-48815 adds a quieter lesson: do not confuse a documented policy knob with an enforced policy boundary. In sigstore <= 4.1.0, the public certificateOIDs option accepted by sigstore.verify() was parsed at the API boundary and then dropped before the actual verification policy was built. If your JavaScript verification gate relied on certificate-extension OIDs to say “only this signer shape is acceptable,” that constraint was not doing any work.

This matters because OID checks are exactly where many teams try to narrow a general “valid Sigstore certificate” into a more useful “valid certificate for this workflow, issuer, or workload identity.” Without that check, a bundle that satisfies the remaining signature, issuer, and SAN rules can still pass even when the required certificate extensions are absent or mismatched.

Publicly confirmed affected package and fix boundary

The current public record is consistent across the advisory, NVD, and upstream patch:

PackageAffected rangeFixed versionWhat breaks
sigstore<= 4.1.04.1.1certificateOIDs constraints accepted by the API but never enforced

The 4.1.1 release also ships @sigstore/verify@3.1.1, which closes a separate transparency-log timestamp trust gap fixed in the same release train. That second issue is not CVE-2026-48815, but it is a good reason not to stop at a partial bump if you maintain custom verification wrappers.

The vulnerable path accepts OIDs and then loses them

The upstream advisory’s proof of concept is unusually clean because it shows the failure without any cryptography at all. Policy construction alone is enough to prove the bug:

const { createVerificationPolicy } = require("sigstore/dist/config");

const policy = createVerificationPolicy({
  certificateIssuer: "https://issuer.example",
  certificateIdentityEmail: "victim@example.com",
  certificateOIDs: {
    "1.2.3.4": "required-value",
  },
});

console.log("certificateOIDs" in policy, JSON.stringify(policy));
// false {"subjectAlternativeName":"victim@example.com","extensions":{"issuer":"https://issuer.example"}}

That output is the whole bug in one line. The caller asked for three signer constraints:

  1. issuer
  2. subject identity
  3. required certificate extension OID/value pairs

But the resulting policy only contains the first two. The OID map never reaches the verifier.

In practical terms, the vulnerable flow looks like this:

application policy
-> sigstore.verify({ certificateOIDs: ... })
-> createVerificationPolicy(...)
-> policy contains SAN + issuer only
-> verifier never receives required OID pairs
-> signer passes if remaining checks succeed

That is why the impact is not merely “missing metadata.” It is a trust-policy bypass.

What the 4.1.1 patch actually changed

The fix is also refreshingly direct. Upstream added explicit OID translation during policy construction:

if (options.certificateOIDs) {
  policy.oids = Object.entries(options.certificateOIDs).map(
    ([oid, value]) => ({
      oid: { id: oid.split(".").map(Number) },
      value: Buffer.from(value),
    })
  );
}

Then the verifier started enforcing those OIDs against the signer’s certificate identity:

if (policy.oids) {
  verifyOIDs(policy.oids, identity.oids);
}

And the enforcement routine is exactly what defenders expected the old code to be doing all along:

export function verifyOIDs(
  policyOIDs: ObjectIdentifierValuePair[],
  signerOIDs: ObjectIdentifierValuePair[] = []
): void {
  for (const policyOID of policyOIDs) {
    const match = signerOIDs.find(
      (signerOID) =>
        oidEquals(policyOID.oid?.id, signerOID.oid?.id) &&
        policyOID.value.equals(signerOID.value)
    );

    if (!match) {
      throw new PolicyError({
        code: "UNTRUSTED_SIGNER_ERROR",
        message: `invalid certificate extension - missing OID ${oid}`,
      });
    }
  }
}

Technically, that is the important distinction between the vulnerable and fixed states:

  • before 4.1.1: OID intent exists only in caller input
  • after 4.1.1: OID intent is serialized into policy and compared against certificate extensions during verification

Why this matters for AppSec teams

sigstore is often discussed as a signing and provenance primitive, but the operational failure here sits in the verification policy layer. Many internal release gates, binary-admission checks, and dependency-validation systems do not merely want “a valid certificate from the general ecosystem.” They want:

this issuer
AND this signer identity
AND these certificate extension values

If the OID portion of that policy disappears, the trust boundary becomes wider than the code reviewer, platform engineer, or auditor thinks it is.

That is especially relevant after incidents like AsyncAPI’s July 14 compromise and the earlier AntV Mini Shai-Hulud wave, where “validly published” and “safe to trust” diverged sharply. CVE-2026-48815 is not the same kind of compromise, but it hits the same operational assumption: a green verification path may be enforcing less than you think.

Detection and scoping

Start by identifying both the dependency and the call sites that rely on OID policy:

npm ls sigstore @sigstore/verify --all

rg -n "sigstore\\.verify\\(|createVerifier\\(|certificateOIDs" \
  src lib scripts packages .github

If your organization has multiple JavaScript package managers in use, also inspect lockfiles directly:

rg -n "\"sigstore\"|\"@sigstore/verify\"" \
  package-lock.json npm-shrinkwrap.json pnpm-lock.yaml yarn.lock .

Then answer the question that matters more than simple dependency presence:

did the code only resolve sigstore?
or did it actively use certificateOIDs as part of a trust decision?

That distinction determines whether you are looking at:

  • a latent vulnerable dependency with no OID-based verification path, or
  • an actual policy-bypass condition in a live verification control

If you do use OID restrictions, inspect for patterns like:

await sigstore.verify(bundle, payload, {
  certificateIssuer: "...",
  certificateIdentityEmail: "...",
  certificateOIDs: {
    "1.3.6.1.4.1.57264.1.9": "expected-value",
  },
});

Any such call on sigstore <= 4.1.0 should be treated as if the certificateOIDs block was never there.

Response guidance

If your JavaScript verification path depends on certificateOIDs:

  1. upgrade to sigstore@4.1.1 or later everywhere the verifier can run
  2. if the dependency is transitive, use your package manager’s override or resolution mechanism until the upstream parent releases a clean graph
  3. re-run artifact or bundle verification for any recent decisions that depended on OID-bound trust policy
  4. review internal documentation and controls to make sure they do not claim OID enforcement for historical runs performed on vulnerable versions

For many teams, step three is the one most likely to be skipped. But this bug is not just a future-risk patching problem. If your gate accepted signatures by relying on OID restrictions that were silently dropped, then prior allow decisions may need to be revisited with the fixed verifier.

The key lesson from CVE-2026-48815 is simple: artifact verification is only as strong as the narrowest policy condition that actually survives into the verifier. In this case, JavaScript callers could express an OID-bound signer policy, review it in code, and even assume it was part of the trust model, while the library quietly validated something broader.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References