Skip to content

fix(cli): support cosign v3 bundle format for signature verification#546

Merged
Aureliolo merged 3 commits into
mainfrom
fix/cosign-v3-bundle-verification
Mar 18, 2026
Merged

fix(cli): support cosign v3 bundle format for signature verification#546
Aureliolo merged 3 commits into
mainfrom
fix/cosign-v3-bundle-verification

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Fixes synthorg start / synthorg update failing with "no cosign signatures found" because the CLI only looked for cosign v2 signature artifacts, but cosign-installer v4.1.0 installs cosign v3 which uses a different storage format.

Root cause

cosign v3 stores signatures as OCI referrer artifacts with:

  • Artifact type: application/vnd.dev.sigstore.bundle.v0.3+json (not application/vnd.dev.cosign.simplesigning.v1+json)
  • Bundle location: raw layer content (not dev.sigstore.cosign/bundle annotation)
  • Registry storage: referrers fallback tag sha256-<digest> (not .sig tag)

The signatures were being pushed to GHCR correctly all along -- the CLI just wasn't looking for the right format.

CLI changes

  • Match both v3 (application/vnd.dev.sigstore.bundle.v0.3+json) and v2 (application/vnd.dev.cosign.simplesigning.v1+json) artifact types in OCI referrer discovery
  • Extract v3 bundles from layer content directly
  • Keep v2 annotation-based extraction as fallback
  • Add isCosignSignatureArtifact helper for clean type matching
  • Forward-compatible: when GHCR adds native OCI referrers API, remote.Referrers() will use it automatically

CI changes

  • Remove --registry-referrers-mode=oci-1-1 and COSIGN_EXPERIMENTAL from all 3 cosign sign commands -- cosign v3 already pushes to the referrers fallback tag by default
  • Plain cosign sign --yes is the correct invocation

Test plan

  • go vet ./... passes
  • go test ./... passes
  • go build ./... passes
  • golangci-lint run passes
  • After merge + next release: synthorg start verifies cosign signatures successfully
  • Verified v0.3.3 signature exists in GHCR at referrers fallback tag sha256-0230fa... with artifact type application/vnd.dev.sigstore.bundle.v0.3+json

Closes #532

cosign v3 (installed by cosign-installer v4.1.0) stores signatures as
OCI referrer artifacts using the new bundle format
(application/vnd.dev.sigstore.bundle.v0.3+json) with the bundle as
raw layer content -- not as .sig tags or annotation-based bundles.

CLI changes:
- Match both v3 bundle artifact type and v2 simplesigning artifact type
  in OCI referrer discovery
- Extract v3 bundles from layer content (not annotations)
- Keep v2 annotation-based extraction as fallback
- Add isCosignSignatureArtifact helper for clean type matching

CI changes:
- Remove --registry-referrers-mode=oci-1-1 and COSIGN_EXPERIMENTAL
  from cosign sign commands -- cosign v3 already pushes to the
  referrers fallback tag by default, no flags needed
- Plain `cosign sign --yes` is the correct invocation for cosign v3

Closes #532
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 18, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: af491881-5441-4b8f-92f1-8b33758cf584

📥 Commits

Reviewing files that changed from the base of the PR and between 63ea242 and 3021e6d.

📒 Files selected for processing (1)
  • cli/internal/verify/cosign.go
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

cli/**/*.go: Go: run cd cli && golangci-lint run before committing CLI code
Go: run cd cli && go vet ./... to check for common Go mistakes

Files:

  • cli/internal/verify/cosign.go
🔇 Additional comments (5)
cli/internal/verify/cosign.go (5)

43-47: Nice centralization of artifact filtering.

Keeping the cosign allowlist in one helper reduces drift between discovery and verification as the supported formats evolve.

Also applies to: 102-103


90-109: Good fit for the referrer discovery path.

In go-containerregistry v0.21.3, remote.Referrers tries the referrers API first, falls back to the sha256-... tag schema, and treats a missing fallback tag as an empty index. That matches the new “no signatures found” flow well. (github.com)


127-133: Clear separation between v3 and v2 verification paths.

Routing by artifact type here keeps the layer-based and annotation-based extraction rules isolated and easier to test independently.


147-160: Good hardening on v3 bundle reads.

In go-containerregistry v0.21.3, Uncompressed() peeks the blob and returns the raw stream unchanged when it is not gzip or zstd, so this remains compatible with raw JSON bundle layers. The extra-byte limit cleanly bounds memory use. (github.com)


173-197: Legacy v2 fallback stays nicely isolated.

Trying manifest annotations first, then layer annotations, keeps the older extraction rules intact without mixing them into the v3 layer-based path.


📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Signature verification now supports cosign v3 (layer-based) and v2 (annotation-based) bundle formats with automatic detection and fallback.
  • Chores

    • Simplified container image signing commands in CI and removed an experimental environment variable.
  • Tests

    • Added unit tests for signature detection and v3/v2 verification error cases.

Walkthrough

Adds OCI-referrer-based cosign signature discovery and verification, adding explicit support for cosign v3 bundles (layer) and cosign v2 bundles (annotations), updates tests, and removes COSIGN_EXPERIMENTAL / registry-referrers flag usage from CI signing steps.

Changes

Cohort / File(s) Summary
CI configuration
.github/workflows/docker.yml
Removed COSIGN_EXPERIMENTAL env and removed --registry-referrers-mode=oci-1-1 from cosign sign steps; simplified sign commands.
Cosign verification logic
cli/internal/verify/cosign.go
Added support for cosign v3 bundle artifact and v2 bundle artifact types, introduced isCosignSignatureArtifact, verifyCosignV3Bundle, verifyCosignV2Bundle, bundle size guard, updated referrer-based traversal and error messages, and new artifact-type constants.
Tests
cli/internal/verify/cosign_test.go
Replaced test artifact types to use v2 constant, added tests for isCosignSignatureArtifact and v3 invalid-bundle scenarios, and adjusted existing signature-bundle test cases.

Sequence Diagram

sequenceDiagram
    participant CLI as Verifier (CLI)
    participant Referrers as OCI Referrers API
    participant Registry as Registry (image / manifest)
    participant V3 as V3 Bundle Handler
    participant V2 as V2 Bundle Handler

    CLI->>Referrers: Request referrers for image digest
    Referrers-->>CLI: Return referrer descriptors

    loop each descriptor
        CLI->>CLI: isCosignSignatureArtifact(desc)?
        alt desc.type == cosignV3BundleArtifactType
            CLI->>Registry: Fetch referrer image (layers)
            Registry-->>CLI: Image + layer content
            CLI->>V3: Read first layer (bundle) and verify against digest
            V3-->>CLI: verification result
        else desc.type == cosignV2ArtifactType
            CLI->>Registry: Fetch manifest / annotations
            Registry-->>CLI: Manifest + annotations
            CLI->>V2: Extract per-source bundle from annotations and verify
            V2-->>CLI: verification result(s)
        else
            CLI-->>CLI: skip non-cosign artifact
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR 543 — Conflicts with CI signing changes; that PR adds COSIGN_EXPERIMENTAL / registry referrers flags which this change removes.
  • PR 539 — Also modifies CLI cosign verification to use OCI referrers; closely related to the referrer-based verification work.
  • PR 533 — Introduced initial OCI referrer discovery and ErrNoCosignSignatures handling that this update extends with v2/v3 bundle support.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for cosign v3 bundle format in signature verification, which is the primary objective of this PR.
Description check ✅ Passed The description is well-related to the changeset, explaining the root cause, CLI changes, CI changes, and test plan in detail.
Linked Issues check ✅ Passed The PR successfully addresses all coding objectives from issue #532: implements OCI referrers-based cosign signature discovery, supports both v3 and v2 bundle formats, removes deprecated .sig tag handling, and updates tests accordingly.
Out of Scope Changes check ✅ Passed All changes are within scope: cosign.go implements v3/v2 bundle support, cosign_test.go adds corresponding tests, and docker.yml removes redundant cosign flags per issue #532 requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cosign-v3-bundle-verification
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/cosign-v3-bundle-verification
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where the CLI failed to verify image signatures generated by Cosign v3 due to changes in its signature storage format. By updating the verification logic, the CLI can now correctly identify and process signatures from both Cosign v2 and v3, ensuring robust and future-proof image integrity checks. This enhancement resolves the "no cosign signatures found" error, improving the reliability and security of synthorg start and synthorg update operations.

Highlights

  • Cosign v3 Bundle Format Support: Implemented support for Cosign v3 bundle format, which stores signatures as raw layer content with a new artifact type (application/vnd.dev.sigstore.bundle.v0.3+json).
  • Backward Compatibility: Maintained backward compatibility for Cosign v2 signatures, which store bundles in manifest or layer annotations (application/vnd.dev.cosign.simplesigning.v1+json).
  • Signature Artifact Helper: Introduced a new helper function, isCosignSignatureArtifact, to streamline the identification of supported Cosign signature artifact types.
  • CI Configuration Alignment: Adjusted CI configurations by removing --registry-referrers-mode=oci-1-1 and COSIGN_EXPERIMENTAL flags from cosign sign commands, aligning with Cosign v3's default behavior.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/docker.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly adds support for the cosign v3 bundle format, which is a necessary fix for compatibility with newer versions of cosign. The changes are well-implemented: new constants clearly define the v2 and v3 artifact types, and the logic is cleanly separated into verifyCosignV3Bundle and verifyCosignV2Bundle functions. The addition of the isCosignSignatureArtifact helper and its corresponding test case is a good practice. My review includes one minor suggestion to make the code slightly more concise. Overall, this is a solid improvement.

Comment thread cli/internal/verify/cosign.go Outdated
Comment on lines +41 to +48
switch desc.ArtifactType {
case cosignV3BundleArtifactType:
return true
case cosignV2ArtifactType:
return true
default:
return false
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This function's logic can be simplified to a single return statement, which is more concise and idiomatic in Go.

	return desc.ArtifactType == cosignV3BundleArtifactType || desc.ArtifactType == cosignV2ArtifactType

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cli/internal/verify/cosign_test.go (1)

129-218: 🧹 Nitpick | 🔵 Trivial

Consider adding a test for v3 bundle format verification.

TestVerifyCosignSignatureInvalidBundle only exercises the v2 code path (annotation-based bundles). Since this PR adds v3 support where the bundle is stored as layer content, a corresponding test for v3 format would improve coverage of the new verifyCosignV3Bundle function.

🧪 Sketch for v3 bundle test
func TestVerifyCosignSignatureV3InvalidBundle(t *testing.T) {
	repo := "test/image"
	sigDigest := "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"

	// v3 stores bundle as raw layer content, not in annotations
	invalidBundleContent := []byte(`{"invalid": "v3 bundle"}`)
	configJSON := `{}`

	sigManifest := ociManifest{
		SchemaVersion: 2,
		MediaType:     "application/vnd.oci.image.manifest.v1+json",
		Config: ociDescriptor{
			MediaType: "application/vnd.oci.image.config.v1+json",
			Digest:    "sha256:44136fa355b311bfa616a15e4e5e6d84e4f455ce82fb1ed83b0a7f9e2c3d4a5b",
			Size:      len(configJSON),
		},
		Layers: []ociLayerDescriptor{
			{
				MediaType: "application/vnd.dev.sigstore.bundle.v0.3+json",
				Digest:    "sha256:0000000000000000000000000000000000000000000000000000000000000002",
				Size:      len(invalidBundleContent),
			},
		},
	}
	// ... setup mock server to return layer blob content ...
	// ... assert verification fails with appropriate error ...
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/internal/verify/cosign_test.go` around lines 129 - 218, Add a new unit
test exercising the v3 bundle code path: create
TestVerifyCosignSignatureV3InvalidBundle that mirrors
TestVerifyCosignSignatureInvalidBundle but uses a layer with MediaType
"application/vnd.dev.sigstore.bundle.v0.3+json" and places the invalid bundle
JSON in the layer blob (not in annotations); build the ociManifest (using
ociManifest and ociLayerDescriptor) and referrer index, mock the HTTP server to
return the manifest and the layer blob for the v3 digest, construct an ImageRef
and call VerifyCosignSignature expecting a non-nil error (assert the error
message contains "cosign signature"), so verifyCosignV3Bundle is exercised and
failure for invalid v3 bundle is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/internal/verify/cosign.go`:
- Around line 38-49: The isCosignSignatureArtifact helper uses a switch
returning true for two artifact types; simplify it by returning a boolean
expression that checks whether desc.ArtifactType equals
cosignV3BundleArtifactType or cosignV2ArtifactType (reference function
isCosignSignatureArtifact(desc v1.Descriptor) and constants
cosignV3BundleArtifactType, cosignV2ArtifactType) so the function becomes a
single-line boolean return instead of a switch.
- Around line 129-136: The dispatch currently assumes descriptors reaching this
block are filtered by isCosignSignatureArtifact in findCosignSignatures so it
treats any non-v3 ArtifactType as v2; add a brief defensive comment above the
dispatch clarifying that invariant and either return an explicit error if
desc.ArtifactType is neither cosignV3BundleArtifactType nor the expected v2 type
or assert the invariant (e.g., panic/log and return error) before calling
verifyCosignV3Bundle/verifyCosignV2Bundle; reference desc.ArtifactType,
cosignV3BundleArtifactType, verifyCosignV3Bundle, verifyCosignV2Bundle,
isCosignSignatureArtifact, and findCosignSignatures when making the change.

---

Outside diff comments:
In `@cli/internal/verify/cosign_test.go`:
- Around line 129-218: Add a new unit test exercising the v3 bundle code path:
create TestVerifyCosignSignatureV3InvalidBundle that mirrors
TestVerifyCosignSignatureInvalidBundle but uses a layer with MediaType
"application/vnd.dev.sigstore.bundle.v0.3+json" and places the invalid bundle
JSON in the layer blob (not in annotations); build the ociManifest (using
ociManifest and ociLayerDescriptor) and referrer index, mock the HTTP server to
return the manifest and the layer blob for the v3 digest, construct an ImageRef
and call VerifyCosignSignature expecting a non-nil error (assert the error
message contains "cosign signature"), so verifyCosignV3Bundle is exercised and
failure for invalid v3 bundle is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9dfacb9c-b8ea-46ec-a7aa-5c31ed14d485

📥 Commits

Reviewing files that changed from the base of the PR and between a32cb1b and 3cab245.

📒 Files selected for processing (3)
  • .github/workflows/docker.yml
  • cli/internal/verify/cosign.go
  • cli/internal/verify/cosign_test.go
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: CLI Build (darwin, arm64)
  • GitHub Check: CLI Build (linux, arm64)
  • GitHub Check: CLI Build (windows, amd64)
  • GitHub Check: CLI Test (ubuntu-latest)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: CLI Test (macos-latest)
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

cli/**/*.go: Go: run cd cli && golangci-lint run before committing CLI code
Go: run cd cli && go vet ./... to check for common Go mistakes

Files:

  • cli/internal/verify/cosign_test.go
  • cli/internal/verify/cosign.go
cli/**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go: use testing.F for fuzz testing; organize fuzz targets in test files with FuzzTarget naming

Files:

  • cli/internal/verify/cosign_test.go
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).

Applied to files:

  • .github/workflows/docker.yml
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Dependabot: auto-updates Docker image digests and versions daily.

Applied to files:

  • .github/workflows/docker.yml
🔇 Additional comments (6)
.github/workflows/docker.yml (1)

217-217: Good update: signing commands now match cosign v3 behavior.

The switch to plain cosign sign --yes <image>@${DIGEST} is correct here, and keeping the digest guard preserves safety before signing.

Also applies to: 384-384, 551-551

cli/internal/verify/cosign.go (3)

17-30: LGTM! Well-documented constants for both bundle formats.

The artifact type constants correctly reflect cosign's documented media types for v3 bundles and v2 simplesigning payloads.


138-162: LGTM! Proper resource handling for v3 bundle extraction.

The deferred close on the reader prevents resource leaks, and the error handling is comprehensive. Reading the entire bundle into memory is acceptable since Sigstore bundles are small JSON documents.


164-197: LGTM! Clear separation of v2 bundle verification logic.

The function correctly checks manifest-level annotations first, then falls back to layer annotations, with proper error aggregation when bundles exist but fail verification.

cli/internal/verify/cosign_test.go (2)

145-154: LGTM! Correct usage of v2 artifact type constant.

The layer MediaType correctly uses cosignV2ArtifactType since this test validates the v2 annotation-based bundle format.


220-237: LGTM! Good test coverage for the helper function.

The test covers both positive cases (v3 and v2 artifact types) and negative cases (empty, unrelated types). Table-driven approach is idiomatic.

Comment thread cli/internal/verify/cosign.go
Comment on lines +129 to +136
// Try v3 bundle format first (bundle is raw layer content).
if desc.ArtifactType == cosignV3BundleArtifactType {
return verifyCosignV3Bundle(img, ref.Digest, sev, certID)
}

// Fall back to v2 format (bundle in annotations).
return verifyCosignV2Bundle(img, ref.Digest, sev, certID)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Verify that the v3/v2 dispatch logic is correct given filtered descriptors.

The function assumes descriptors are pre-filtered by isCosignSignatureArtifact, so only v3 or v2 types reach here. If ArtifactType is not v3, it falls through to v2 verification. This is correct given the current filtering in findCosignSignatures, but consider adding a defensive check or comment to clarify this invariant.

🛡️ Optional: Add defensive comment
 	// Try v3 bundle format first (bundle is raw layer content).
 	if desc.ArtifactType == cosignV3BundleArtifactType {
 		return verifyCosignV3Bundle(img, ref.Digest, sev, certID)
 	}
 
-	// Fall back to v2 format (bundle in annotations).
+	// Fall back to v2 format (bundle in annotations).
+	// Note: Only v2 or v3 artifact types reach here due to filtering in findCosignSignatures.
 	return verifyCosignV2Bundle(img, ref.Digest, sev, certID)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Try v3 bundle format first (bundle is raw layer content).
if desc.ArtifactType == cosignV3BundleArtifactType {
return verifyCosignV3Bundle(img, ref.Digest, sev, certID)
}
// Fall back to v2 format (bundle in annotations).
return verifyCosignV2Bundle(img, ref.Digest, sev, certID)
}
// Try v3 bundle format first (bundle is raw layer content).
if desc.ArtifactType == cosignV3BundleArtifactType {
return verifyCosignV3Bundle(img, ref.Digest, sev, certID)
}
// Fall back to v2 format (bundle in annotations).
// Note: Only v2 or v3 artifact types reach here due to filtering in findCosignSignatures.
return verifyCosignV2Bundle(img, ref.Digest, sev, certID)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/internal/verify/cosign.go` around lines 129 - 136, The dispatch currently
assumes descriptors reaching this block are filtered by
isCosignSignatureArtifact in findCosignSignatures so it treats any non-v3
ArtifactType as v2; add a brief defensive comment above the dispatch clarifying
that invariant and either return an explicit error if desc.ArtifactType is
neither cosignV3BundleArtifactType nor the expected v2 type or assert the
invariant (e.g., panic/log and return error) before calling
verifyCosignV3Bundle/verifyCosignV2Bundle; reference desc.ArtifactType,
cosignV3BundleArtifactType, verifyCosignV3Bundle, verifyCosignV2Bundle,
isCosignSignatureArtifact, and findCosignSignatures when making the change.

- Simplify isCosignSignatureArtifact from switch to single return
- Add TestVerifyCosignSignatureV3InvalidBundle covering the v3 code
  path (bundle as layer content, not annotations)

Addresses Gemini and CodeRabbit review feedback.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/internal/verify/cosign.go`:
- Around line 149-152: The current io.ReadAll(reader) call that produces
bundleJSON can exhaust memory for large malicious layers; replace it by reading
via io.LimitReader to impose a maximum bundle size (e.g., define const
maxBundleSize = 1<<20 or suitable value) and use
io.ReadAll(io.LimitReader(reader, maxBundleSize)); after reading, detect if the
stream was truncated/limit reached (e.g., attempt to read one extra byte or
check if len(bundleJSON) == maxBundleSize) and return a clear error like "bundle
too large" if exceeded. Update the code around bundleJSON and reader in
cosign.go to use the limit and an explicit error path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 86a84f37-2143-40e9-a7f7-2a50945fa673

📥 Commits

Reviewing files that changed from the base of the PR and between 3cab245 and 63ea242.

📒 Files selected for processing (2)
  • cli/internal/verify/cosign.go
  • cli/internal/verify/cosign_test.go
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: CLI Test (macos-latest)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

cli/**/*.go: Go: run cd cli && golangci-lint run before committing CLI code
Go: run cd cli && go vet ./... to check for common Go mistakes

Files:

  • cli/internal/verify/cosign_test.go
  • cli/internal/verify/cosign.go
cli/**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go: use testing.F for fuzz testing; organize fuzz targets in test files with FuzzTarget naming

Files:

  • cli/internal/verify/cosign_test.go
🧬 Code graph analysis (1)
cli/internal/verify/cosign_test.go (2)
cli/internal/verify/verify.go (1)
  • ImageRef (28-33)
cli/internal/verify/cosign.go (1)
  • VerifyCosignSignature (53-73)
🔇 Additional comments (7)
cli/internal/verify/cosign.go (4)

122-129: LGTM!

The v3/v2 dispatch correctly handles both formats. Since findCosignSignatures pre-filters descriptors via isCosignSignatureArtifact, only valid artifact types reach this point.


17-30: LGTM! Constants align with cosign v3 and v2 formats.

The artifact type constants correctly distinguish between cosign v3 bundle format (application/vnd.dev.sigstore.bundle.v0.3+json) and the legacy v2 simplesigning format. Documentation is clear.


38-42: LGTM!

The helper is concise and correctly identifies both v3 and v2 cosign signature artifact types.


157-190: LGTM!

The v2 bundle extraction correctly tries manifest-level annotations first, then layer annotations, with proper error aggregation. The fallback error message is clear.

cli/internal/verify/cosign_test.go (3)

147-171: LGTM!

Test correctly updated to use the cosignV2ArtifactType constant, ensuring the test exercises the v2 annotation-based verification path.


220-237: LGTM!

Good table-driven test covering both positive cases (v3 and v2 artifact types) and negative cases (empty OCI, in-toto, empty string). Test is concise and idiomatic.


239-329: LGTM!

Excellent test for the v3 bundle extraction path. The mock registry correctly simulates:

  • A referrer index with cosignV3BundleArtifactType
  • Layer content containing the bundle (not in annotations)
  • Invalid bundle JSON to trigger the error path

This complements TestVerifyCosignSignatureInvalidBundle and ensures both v3 and v2 bundle extraction paths are tested.

Comment thread cli/internal/verify/cosign.go Outdated
Limit io.ReadAll to 1MB+1 via io.LimitReader when reading cosign v3
bundle from registry layer. Prevents a malicious registry from serving
an arbitrarily large blob. Typical bundles are ~10KB.
@Aureliolo Aureliolo merged commit 6115eff into main Mar 18, 2026
44 checks passed
@Aureliolo Aureliolo deleted the fix/cosign-v3-bundle-verification branch March 18, 2026 15:12
Aureliolo added a commit that referenced this pull request Mar 18, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.4](v0.3.3...v0.3.4)
(2026-03-18)


### Bug Fixes

* **cli:** support cosign v3 bundle format for signature verification
([#546](#546))
([6115eff](6115eff)),
closes [#532](#532)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(cli): cosign signature verification fails with MANIFEST_UNKNOWN

1 participant