Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(webhook): verify OCI 1.1 attestation signatures before trusting them#1971

Open
evilgensec wants to merge 2 commits into
sigstore:mainfrom
evilgensec:fix/oci11-attestation-bypass
Open

fix(webhook): verify OCI 1.1 attestation signatures before trusting them#1971
evilgensec wants to merge 2 commits into
sigstore:mainfrom
evilgensec:fix/oci11-attestation-bypass

Conversation

@evilgensec
Copy link
Copy Markdown

Summary

When enable-oci11=true, discoverAttestationsOCI11 fetched referrer attestations and returned them via processAttestationArtifact without ever invoking a cryptographic verifier. The caller ValidatePolicyAttestationsForAuthority treats len(verifiedAttestations) > 0 as a successful authority check, so any image carrying an OCI 1.1 referrer whose ArtifactType contains "in-toto" was admitted regardless of whether the embedded DSSE envelope carried any valid signature, certificate, or transparency-log entry.

The path was introduced in #1894 and is present in v0.15.x / main.

This PR:

  • Routes the discovered attestations through cosign.VerifyImageAttestation, which runs the same signature / cert / identity / transparency-log / claim checks as the legacy attestation path.
  • Fixes processAttestationArtifact to hand the full DSSE envelope to static.NewAttestation (instead of an ad-hoc JSON wrapper that cosign.verifyOCIAttestation could not parse), and to reject envelopes that are missing payloadType / payload / signatures.
  • Adds TestValidAttestationsOCI11_RejectsForgedDSSE, a regression test that drives validAttestations end-to-end with a forged DSSE envelope and asserts the call returns an error rather than treating the envelope as verified. The existing TestDiscoverAttestationsOCI11* tests now also exercise the verification hook via a passthrough stub.

Reproduction (without this fix)

# ClusterImagePolicy requiring SLSA provenance from a specific key:
apiVersion: policy.sigstore.dev/v1alpha1
kind: ClusterImagePolicy
metadata: { name: require-google-provenance }
spec:
  images: [{ glob: "registry.example.com/**" }]
  authorities:
  - name: google-cloud-build-key
    key: { data: "-----BEGIN PUBLIC KEY-----..." }
    attestations:
    - { name: require-provenance, predicateType: https://slsa.dev/provenance/v1 }
---
# ConfigMap enables OCI 1.1 storage discovery
data: { enable-oci11: "true" }

An attacker with push rights to the registry pushes:

  1. A malicious image registry.example.com/evil@sha256:<D>.
  2. An OCI 1.1 referrer manifest whose subject digest is <D>, artifactType: "application/vnd.dsse.envelope.v1+json+in-toto", with a single layer whose body is a DSSE envelope:
{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "<base64 of an in-toto statement with predicateType https://slsa.dev/provenance/v1>",
  "signatures": [ { "sig": "ATTACKER_FORGED_SIGNATURE" } ]
}

The webhook returns Allowed even though the DSSE signature was never verified against the configured public key.

After this PR, the same flow returns an error from cosign.VerifyImageAttestation and the admission is rejected.

Test plan

  • go test ./pkg/webhook/... — all existing tests pass, including the OCI 1.1 discovery tests
  • TestValidAttestationsOCI11_RejectsForgedDSSE — new regression test passes on this branch and FAILS on main
  • TestProcessAttestationArtifact/DSSE_envelope_missing_payloadType — new sub-test ensures malformed envelopes are rejected
  • go build ./... clean
  • gofmt -l clean

Copilot AI review requested due to automatic review settings May 21, 2026 11:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a security gap in the OCI 1.1 referrers attestation-discovery path by ensuring discovered DSSE attestations are cryptographically verified (cosign verification) before being treated as “verified” policy attestations.

Changes:

  • Route OCI 1.1-discovered attestations through cosign.VerifyImageAttestation (with in-toto subject claim verification) instead of returning them as implicitly trusted.
  • Correct OCI 1.1 DSSE handling by passing the full DSSE envelope to static.NewAttestation and rejecting structurally invalid envelopes (missing payloadType/payload/signatures).
  • Add an end-to-end regression test to prevent reintroducing the forged/unsigned DSSE bypass; update existing OCI 1.1 discovery tests to stub the new verification hook.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
pkg/webhook/validation.go Verifies OCI 1.1-discovered attestations with cosign; tightens DSSE envelope parsing to use full envelope and reject malformed inputs.
pkg/webhook/validator_test.go Updates DSSE envelope fixtures/expectations; adds a passthrough verifier stub so OCI 1.1 discovery tests exercise the new verification hook.
pkg/webhook/validation_oci11_security_test.go Adds regression coverage ensuring forged/unsigned OCI 1.1 DSSE envelopes are not accepted as verified attestations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/webhook/validation_oci11_security_test.go
When EnableOCI11=true, discoverAttestationsOCI11 fetched in-toto
referrer artifacts and returned them via processAttestationArtifact
without ever invoking a cryptographic verifier. The caller
ValidatePolicyAttestationsForAuthority treated len(result) > 0 as a
successful attestation check, so any image accompanied by an OCI 1.1
referrer whose ArtifactType contained "in-toto" was admitted regardless
of whether its DSSE envelope had any valid signature, certificate, or
transparency-log entry. The fix wraps the discovered attestations into
an oci.Signatures and runs them through cosign.VerifyImageAttestation,
which performs the same checks as the legacy attestation path
(signature, certificate, identity, transparency log, claim).

processAttestationArtifact now hands the full DSSE envelope as the
attestation payload (instead of an ad-hoc JSON wrapper that
verifyOCIAttestation could not parse) and rejects malformed envelopes
that omit payloadType/payload/signatures, since those cannot represent
a real attestation.

Signed-off-by: evilgensec <[email protected]>
validAttestations falls through to cosign.VerifyImageAttestations when
discoverAttestationsOCI11 returns an error. Without stubbing the legacy
path, the regression test could touch the real registry and depend on
network/DNS for its outcome. Stub cosignVerifyAttestations to a clear
sentinel so the test is deterministic and so the only way a non-empty
attestation list can be returned is the OCI 1.1 branch -- which is
exactly what we want to assert does not accept a forged envelope.

Signed-off-by: evilgensec <[email protected]>
@evilgensec evilgensec force-pushed the fix/oci11-attestation-bypass branch from 5cd5f2d to afade29 Compare June 2, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants