fix(webhook): verify OCI 1.1 attestation signatures before trusting them#1971
Open
evilgensec wants to merge 2 commits into
Open
fix(webhook): verify OCI 1.1 attestation signatures before trusting them#1971evilgensec wants to merge 2 commits into
evilgensec wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
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.NewAttestationand rejecting structurally invalid envelopes (missingpayloadType/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.
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]>
5cd5f2d to
afade29
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When
enable-oci11=true,discoverAttestationsOCI11fetched referrer attestations and returned them viaprocessAttestationArtifactwithout ever invoking a cryptographic verifier. The callerValidatePolicyAttestationsForAuthoritytreatslen(verifiedAttestations) > 0as a successful authority check, so any image carrying an OCI 1.1 referrer whoseArtifactTypecontains"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:
cosign.VerifyImageAttestation, which runs the same signature / cert / identity / transparency-log / claim checks as the legacy attestation path.processAttestationArtifactto hand the full DSSE envelope tostatic.NewAttestation(instead of an ad-hoc JSON wrapper thatcosign.verifyOCIAttestationcould not parse), and to reject envelopes that are missingpayloadType/payload/signatures.TestValidAttestationsOCI11_RejectsForgedDSSE, a regression test that drivesvalidAttestationsend-to-end with a forged DSSE envelope and asserts the call returns an error rather than treating the envelope as verified. The existingTestDiscoverAttestationsOCI11*tests now also exercise the verification hook via a passthrough stub.Reproduction (without this fix)
An attacker with push rights to the registry pushes:
registry.example.com/evil@sha256:<D>.subjectdigest 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.VerifyImageAttestationand the admission is rejected.Test plan
go test ./pkg/webhook/...— all existing tests pass, including the OCI 1.1 discovery testsTestValidAttestationsOCI11_RejectsForgedDSSE— new regression test passes on this branch and FAILS onmainTestProcessAttestationArtifact/DSSE_envelope_missing_payloadType— new sub-test ensures malformed envelopes are rejectedgo build ./...cleangofmt -lclean