Skip to content

feat(evidence): aicr evidence sign — sign an existing pushed bundle#1446

Merged
mchmarny merged 1 commit into
mainfrom
feat/evidence-sign-existing
Jun 24, 2026
Merged

feat(evidence): aicr evidence sign — sign an existing pushed bundle#1446
mchmarny merged 1 commit into
mainfrom
feat/evidence-sign-existing

Conversation

@mchmarny

Copy link
Copy Markdown
Member

Summary

Adds aicr evidence sign <pointer> — the sign-existing leg that completes the two-phase publish flow. A contributor pushes an unsigned bundle and commits the pointer (evidence publish --no-sign); this command signs it later (the only step that needs Fulcio/Rekor egress), attaches the Sigstore referrer, and patches the pointer's signer block in place.

Motivation / Context

Completes the CLI/verifier work for the evidence-gate pilot. This is the second of the two CLI PRs and completes #1434; it is the step the fork-based CI signing workflow (#1433) will call.

Fixes: #1434
Related: #1431, #1433 (fork workflow consumes this)

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Documentation update

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • Docs/examples (docs/, examples/)
  • Other: pkg/evidence/{attestation,verifier}

Implementation Notes

  • MaterializedBundle now carries MediaType/Size (populated from the pulled manifest descriptor) — the subject descriptor needed to attach a referrer, which a pointer (digest only) can't supply.
  • attestation.SignExisting reads the predicate from the pulled bundle (no re-emit), signs with ambient OIDC, attaches the Sigstore Bundle as an OCI referrer, then patches the pointer's signer block and writes it back. Mirroring Publish, it returns only an error and writes its artifact as a side effect — the registry-bound happy path isn't unit-testable.
  • evidence sign fails closed via assertUnsignedSignable: refuses anything that isn't a single, already-pushed, not-yet-signed attestation (never re-signs over an existing signature).
  • Refactors: buildPointerInputsFromOutcome reuses the new PointerSignerFromSignature (shared zero-Rekor rule); WritePointer delegates to a new WritePointerFile (exact-path write).

Testing

golangci-lint run -c .golangci.yaml ./pkg/cli/... ./pkg/evidence/...   # 0 issues
go test -race ./pkg/cli/... ./pkg/evidence/...                          # ok

New unit tests: pointer-state guards (assertUnsignedSignable), SignExisting input guards, signature→pointer-signer mapping, exact-path pointer writes. Registry/Fulcio-bound happy path not unit-tested (consistent with Publish).

Risk Assessment

  • Low — New subcommand + additive MaterializedBundle fields; existing paths unchanged.

Rollout notes: Backwards-compatible. Depends conceptually on the --no-sign PR (produces the unsigned pointers this consumes) but has no compile dependency on it.

Checklist

  • Tests pass locally (make test with -race)
  • Linter passes (make lint)
  • I did not skip/disable tests to make CI green
  • I added/updated tests for new functionality
  • I updated docs if user-facing behavior changed
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)

@mchmarny mchmarny requested a review from a team as a code owner June 24, 2026 12:56
@mchmarny mchmarny added the theme/supply-chain SLSA, SBOM, Sigstore, and provenance verification label Jun 24, 2026
@mchmarny mchmarny self-assigned this Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds the signing-only evidence sign workflow. It extends MaterializedBundle with OCI descriptor fields, adds WritePointerFile and PointerSignerFromSignature, implements SignExisting for signing an already-pushed bundle and updating the pointer in place, wires the new CLI subcommand with pointer-state validation, adds tests, and documents the command.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • NVIDIA/aicr#873: Shares the attestation signing and pointer signer-mapping path that this PR centralizes in PointerSignerFromSignature.
  • NVIDIA/aicr#1300: Also adds CLI-side keyless signing disclosure and confirmation flow used by evidence sign.

Suggested labels

size/L

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning It adds sign-existing support and tests, but not the linked issue's no-sign publish flow or verifier pending-state behavior. Add the missing --no-sign publish path and update evidence verify to treat unsigned pointers as pending rather than invalid.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the new aicr evidence sign command and its sign-existing bundle workflow.
Description check ✅ Passed The description is directly about the new sign-existing evidence flow and related implementation details.
Out of Scope Changes check ✅ Passed The changes appear scoped to the evidence sign flow, supporting pointer and descriptor plumbing, docs, and tests.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/evidence-sign-existing

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/user/cli-reference.md`:
- Around line 2654-2656: Add the missing blank line before the fenced synopsis
block in the evidence sign section so the Markdown follows the
blanks-around-fences rule. Update the nearby “Synopsis” content in the docs to
place an empty line between the bold label and the ```shell fence, keeping the
existing `aicr evidence sign <pointer> [flags]` snippet unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c5cef9c4-7262-46e1-9ba9-d6de74321df0

📥 Commits

Reviewing files that changed from the base of the PR and between e927f39 and d94a597.

📒 Files selected for processing (9)
  • docs/user/cli-reference.md
  • pkg/cli/evidence.go
  • pkg/cli/evidence_sign.go
  • pkg/cli/evidence_sign_test.go
  • pkg/evidence/attestation/emit.go
  • pkg/evidence/attestation/pointer.go
  • pkg/evidence/attestation/sign.go
  • pkg/evidence/attestation/sign_test.go
  • pkg/evidence/verifier/fetch.go

Comment thread docs/user/cli-reference.md
@mchmarny mchmarny force-pushed the feat/evidence-sign-existing branch from d94a597 to 47b2d6d Compare June 24, 2026 13:06
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Coverage Report ✅

Metric Value
Coverage 77.2%
Threshold 75%
Status Pass
Coverage Badge
![Coverage](https://img.shields.io/badge/coverage-77.2%25-green)

Merging this branch will decrease overall coverage

Impacted Packages Coverage Δ 🤖
github.com/NVIDIA/aicr/pkg/cli 68.09% (-0.98%) 👎
github.com/NVIDIA/aicr/pkg/evidence/attestation 69.72% (-2.62%) 👎
github.com/NVIDIA/aicr/pkg/evidence/verifier 65.05% (ø)

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/NVIDIA/aicr/pkg/cli/evidence.go 100.00% (ø) 1 1 0
github.com/NVIDIA/aicr/pkg/cli/evidence_sign.go 3.85% (+3.85%) 26 (+26) 1 (+1) 25 (+25) 👍
github.com/NVIDIA/aicr/pkg/evidence/attestation/emit.go 41.49% (-2.96%) 94 (-5) 39 (-5) 55 👎
github.com/NVIDIA/aicr/pkg/evidence/attestation/pointer.go 71.88% (-4.60%) 32 (+15) 23 (+10) 9 (+5) 👎
github.com/NVIDIA/aicr/pkg/evidence/attestation/sign.go 43.14% (+43.14%) 51 (+51) 22 (+22) 29 (+29) 🌟
github.com/NVIDIA/aicr/pkg/evidence/verifier/fetch.go 54.55% (ø) 165 90 75

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/cli/evidence.go`:
- Around line 40-47: The top-level evidence usage summary is missing the new
sign subcommand, so update the usage/help text in the evidence command setup to
list sign alongside digest, publish, and verify. Make sure the string that feeds
aicr evidence --help is kept in sync with the Commands slice in the evidence
command definition, including evidenceSignCmd().

In `@pkg/evidence/attestation/emit.go`:
- Around line 389-392: The comment around PointerSignerFromSignature and the
helper’s own docstring are inaccurate about zero-Rekor handling; update both to
match the actual behavior that only RekorLogIndex values greater than 0 are
preserved and index 0 is treated the same as absent. Use the
PointerSignerFromSignature helper and the in.Signer assignment in emit.go to
locate the wording, and revise the text so it no longer claims index 0 is
distinguished from “no Rekor entry.”

In `@pkg/evidence/attestation/sign.go`:
- Around line 78-91: `SignExisting` in `pkg/evidence/attestation/sign.go` still
allows mutating `Attestations[0]` without enforcing the unsigned
single-attestation invariant. Add input validation in `SignExisting` (and/or its
helper path) to reject more than one attestation, reject any existing signer,
and require a non-empty `Bundle.Digest` before proceeding; keep the checks
alongside the existing `opts.Pointer`, `PointerPath`, and `Artifact` validations
so non-CLI callers cannot accidentally clobber provenance.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: cc3c4d4c-b44f-4ec7-b9cb-6d298c8c0bcc

📥 Commits

Reviewing files that changed from the base of the PR and between d94a597 and 47b2d6d.

📒 Files selected for processing (9)
  • docs/user/cli-reference.md
  • pkg/cli/evidence.go
  • pkg/cli/evidence_sign.go
  • pkg/cli/evidence_sign_test.go
  • pkg/evidence/attestation/emit.go
  • pkg/evidence/attestation/pointer.go
  • pkg/evidence/attestation/sign.go
  • pkg/evidence/attestation/sign_test.go
  • pkg/evidence/verifier/fetch.go

Comment thread pkg/cli/evidence.go
Comment thread pkg/evidence/attestation/emit.go
Comment thread pkg/evidence/attestation/sign.go Outdated
@mchmarny mchmarny force-pushed the feat/evidence-sign-existing branch from 47b2d6d to 4c0fb3e Compare June 24, 2026 14:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
pkg/cli/evidence.go (1)

28-43: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep the parent help text consistent with sign.

The summary paragraph still says only publish reaches the OCI registry and Sigstore, but sign now does too. aicr evidence --help will contradict itself until that sentence is updated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/cli/evidence.go` around lines 28 - 43, Update the parent help text in the
evidence CLI to match the current behavior of sign and publish. In the Cobra
command setup for the evidence command, adjust the Description sentence that
currently says only publish reaches the OCI registry and Sigstore so it also
mentions sign, keeping the summary consistent with the subcommand list and the
sign/publish flow.
♻️ Duplicate comments (1)
pkg/evidence/attestation/sign.go (1)

92-100: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject pointer/artifact digest mismatches before signing.

BuildArtifactStatement and AttachSigstoreBundleAsReferrer both use opts.Artifact.Digest, but the pointer you write back still keeps att.Bundle.Digest. Right now a non-CLI caller can pass a resolved descriptor for one artifact and a pointer for another; this function will sign/attach the former while leaving the pointer pointing at the latter.

🔧 Proposed fix
 	if opts.Artifact.Digest == "" || opts.Artifact.MediaType == "" || opts.Artifact.Size <= 0 {
 		return errors.New(errors.ErrCodeInvalidRequest,
 			"artifact descriptor (digest, mediaType, size) is required to sign an existing bundle")
 	}
+	if att.Bundle.Digest != opts.Artifact.Digest {
+		return errors.New(errors.ErrCodeConflict,
+			"pointer bundle.digest does not match resolved artifact digest")
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/evidence/attestation/sign.go` around lines 92 - 100, The signing flow in
sign.Bundle should validate that the pointer bundle digest matches
opts.Artifact.Digest before proceeding, since BuildArtifactStatement and
AttachSigstoreBundleAsReferrer currently sign the artifact descriptor while
leaving att.Bundle.Digest unchanged. Add a digest equality check in the existing
precondition block, returning an invalid request error when att.Bundle.Digest
and opts.Artifact.Digest differ, so only matching pointer/artifact pairs can be
signed and attached.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/evidence/attestation/pointer.go`:
- Around line 89-96: WritePointerFile currently uses os.WriteFile, which can
leave a partially written Pointer file if the process or filesystem fails
mid-write. Update WritePointerFile to write MarshalPointer(p) to a temp file in
the same directory, ensure the temp file is closed with Close() error checking,
then atomically replace the target with rename and preserve the existing error
wrapping in the WritePointerFile path.

---

Outside diff comments:
In `@pkg/cli/evidence.go`:
- Around line 28-43: Update the parent help text in the evidence CLI to match
the current behavior of sign and publish. In the Cobra command setup for the
evidence command, adjust the Description sentence that currently says only
publish reaches the OCI registry and Sigstore so it also mentions sign, keeping
the summary consistent with the subcommand list and the sign/publish flow.

---

Duplicate comments:
In `@pkg/evidence/attestation/sign.go`:
- Around line 92-100: The signing flow in sign.Bundle should validate that the
pointer bundle digest matches opts.Artifact.Digest before proceeding, since
BuildArtifactStatement and AttachSigstoreBundleAsReferrer currently sign the
artifact descriptor while leaving att.Bundle.Digest unchanged. Add a digest
equality check in the existing precondition block, returning an invalid request
error when att.Bundle.Digest and opts.Artifact.Digest differ, so only matching
pointer/artifact pairs can be signed and attached.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 1493e338-c6eb-43bc-af2e-32930b8f8e33

📥 Commits

Reviewing files that changed from the base of the PR and between 47b2d6d and 4c0fb3e.

📒 Files selected for processing (9)
  • docs/user/cli-reference.md
  • pkg/cli/evidence.go
  • pkg/cli/evidence_sign.go
  • pkg/cli/evidence_sign_test.go
  • pkg/evidence/attestation/emit.go
  • pkg/evidence/attestation/pointer.go
  • pkg/evidence/attestation/sign.go
  • pkg/evidence/attestation/sign_test.go
  • pkg/evidence/verifier/fetch.go

Comment thread pkg/evidence/attestation/pointer.go
@mchmarny mchmarny force-pushed the feat/evidence-sign-existing branch from 4c0fb3e to 1d7a945 Compare June 24, 2026 14:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/evidence/attestation/sign.go (1)

92-108: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject pointer/descriptor digest mismatches before signing.

SignExisting requires both digests but never verifies they match. A non-CLI caller can sign and attach a referrer for opts.Artifact.Digest while patching a pointer whose bundle.digest still names a different artifact, corrupting the pointer’s provenance.

Proposed fix
 	if reference == "" || att.Bundle.Digest == "" {
 		return errors.New(errors.ErrCodeInvalidRequest,
 			"pointer has no pushed bundle to sign (bundle.oci/bundle.digest are empty)")
 	}
 	if opts.Artifact.Digest == "" || opts.Artifact.MediaType == "" || opts.Artifact.Size <= 0 {
 		return errors.New(errors.ErrCodeInvalidRequest,
 			"artifact descriptor (digest, mediaType, size) is required to sign an existing bundle")
 	}
+	if att.Bundle.Digest != opts.Artifact.Digest {
+		return errors.New(errors.ErrCodeConflict,
+			"pointer bundle.digest does not match resolved artifact digest")
+	}
 
 	bundle, _, err := loadOnDiskBundle(opts.BundleDir)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/evidence/attestation/sign.go` around lines 92 - 108, SignExisting
currently checks that both the pointer bundle digest and the artifact descriptor
digest are present, but it does not verify they refer to the same artifact
before signing. In sign.go, update SignExisting to compare att.Bundle.Digest
against opts.Artifact.Digest (using the existing digest normalization style in
this flow) and return an invalid-request error when they differ, before
loadOnDiskBundle and BuildArtifactStatement are used. Keep the check close to
the existing validation for reference, digest, and artifact descriptor so
mismatches are rejected before any signing or referrer attachment happens.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/cli/evidence_sign.go`:
- Around line 98-102: The evidence signing command currently only reads
cmd.Args().First() in evidence_sign.go, so extra positional arguments are
ignored and can lead to signing the wrong pointer. Update the argument
validation in the sign command to enforce exactly one positional argument before
proceeding, and return an invalid-request error when the arg count is not one so
the ArgsUsage contract for a single <pointer> is respected.

In `@pkg/evidence/attestation/sign_test.go`:
- Around line 61-70: The negative-path tests in SignExisting currently only
check for a non-nil error, which can miss regressions in validation behavior.
Update the table-driven cases in sign_test.go to include the expected structured
error code for each scenario, then assert the returned error with errors.Is(...)
in the SignExisting test cases rather than only calling t.Fatalf on nil. Use the
existing SignExisting, SignExistingOptions, and the test case fields around
tt.name, tt.pointer, and tt.path to locate and extend the assertions.

---

Duplicate comments:
In `@pkg/evidence/attestation/sign.go`:
- Around line 92-108: SignExisting currently checks that both the pointer bundle
digest and the artifact descriptor digest are present, but it does not verify
they refer to the same artifact before signing. In sign.go, update SignExisting
to compare att.Bundle.Digest against opts.Artifact.Digest (using the existing
digest normalization style in this flow) and return an invalid-request error
when they differ, before loadOnDiskBundle and BuildArtifactStatement are used.
Keep the check close to the existing validation for reference, digest, and
artifact descriptor so mismatches are rejected before any signing or referrer
attachment happens.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5eb583a7-cd90-4082-a629-de0d9566d9d3

📥 Commits

Reviewing files that changed from the base of the PR and between 4c0fb3e and 1d7a945.

📒 Files selected for processing (9)
  • docs/user/cli-reference.md
  • pkg/cli/evidence.go
  • pkg/cli/evidence_sign.go
  • pkg/cli/evidence_sign_test.go
  • pkg/evidence/attestation/emit.go
  • pkg/evidence/attestation/pointer.go
  • pkg/evidence/attestation/sign.go
  • pkg/evidence/attestation/sign_test.go
  • pkg/evidence/verifier/fetch.go

Comment thread pkg/cli/evidence_sign.go
Comment thread pkg/evidence/attestation/sign_test.go
@mchmarny mchmarny force-pushed the feat/evidence-sign-existing branch from 1d7a945 to ec33792 Compare June 24, 2026 14:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/evidence/attestation/sign.go (1)

92-109: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject pointer/artifact digest mismatches before signing.

Line 107 signs opts.Artifact.Digest, but the pointer written back still keeps att.Bundle.Digest. If those digests diverge, this path attaches a referrer to one artifact while the committed pointer still references another, which breaks later verification and leaves provenance inconsistent across layers.

Proposed fix
 	reference := att.Bundle.OCI
 	if reference == "" || att.Bundle.Digest == "" {
 		return errors.New(errors.ErrCodeInvalidRequest,
 			"pointer has no pushed bundle to sign (bundle.oci/bundle.digest are empty)")
 	}
 	if opts.Artifact.Digest == "" || opts.Artifact.MediaType == "" || opts.Artifact.Size <= 0 {
 		return errors.New(errors.ErrCodeInvalidRequest,
 			"artifact descriptor (digest, mediaType, size) is required to sign an existing bundle")
 	}
+	if att.Bundle.Digest != opts.Artifact.Digest {
+		return errors.New(errors.ErrCodeConflict,
+			"pointer bundle.digest does not match the resolved artifact digest")
+	}
 
 	bundle, _, err := loadOnDiskBundle(opts.BundleDir)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/evidence/attestation/sign.go` around lines 92 - 109, Reject
pointer/artifact digest mismatches in the signing flow by validating that
opts.Artifact.Digest matches att.Bundle.Digest before BuildArtifactStatement or
any referrer attachment in sign.go; if they differ, return an invalid request
error and avoid signing. Keep the check near the existing bundle/artifact
validation in the sign path so the pointer updated by the caller remains
consistent with the artifact actually signed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/cli/evidence_sign.go`:
- Around line 167-188: Move the signable-pointer validation out of the CLI
layer: `assertUnsignedSignable` in `pkg/cli/evidence_sign.go` duplicates the
invariant already enforced by `attestation.SignExisting`. Add or export a
validator from `pkg/evidence/attestation` (near `SignExisting` in `sign.go`) and
make the CLI call that shared check instead of maintaining its own business
rule. Keep `pkg/cli` focused on wiring, and update any CLI tests to exercise the
shared attestation validation path.

In `@pkg/evidence/attestation/sign.go`:
- Around line 145-148: Avoid updating opts.Pointer.Attestations[0].Signer before
WritePointerFile succeeds, because signPointer currently mutates the caller’s
in-memory pointer too early. In signPointer, keep the patched signer in a
temporary local copy, write that copy through WritePointerFile, and only assign
back to opts.Pointer after the write completes successfully. Make sure the retry
path and the att.Signer nil guard remain based on the original pointer state
until persistence is confirmed.

---

Duplicate comments:
In `@pkg/evidence/attestation/sign.go`:
- Around line 92-109: Reject pointer/artifact digest mismatches in the signing
flow by validating that opts.Artifact.Digest matches att.Bundle.Digest before
BuildArtifactStatement or any referrer attachment in sign.go; if they differ,
return an invalid request error and avoid signing. Keep the check near the
existing bundle/artifact validation in the sign path so the pointer updated by
the caller remains consistent with the artifact actually signed.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 9925b991-ad2e-40a7-8b15-115725b41a5d

📥 Commits

Reviewing files that changed from the base of the PR and between 1d7a945 and ec33792.

📒 Files selected for processing (9)
  • docs/user/cli-reference.md
  • pkg/cli/evidence.go
  • pkg/cli/evidence_sign.go
  • pkg/cli/evidence_sign_test.go
  • pkg/evidence/attestation/emit.go
  • pkg/evidence/attestation/pointer.go
  • pkg/evidence/attestation/sign.go
  • pkg/evidence/attestation/sign_test.go
  • pkg/evidence/verifier/fetch.go

Comment thread pkg/cli/evidence_sign.go Outdated
Comment thread pkg/evidence/attestation/sign.go Outdated
Add the sign-existing leg that completes the two-phase publish flow:
a contributor pushes an unsigned bundle and commits the pointer
(`evidence publish --no-sign`), then this command signs it later —
the only step that needs Fulcio/Rekor egress, so it runs in CI.

`aicr evidence sign <pointer>`:
- Loads + validates the committed pointer, failing closed unless it is
  a single, already-pushed, not-yet-signed attestation (never re-signs).
- Pulls the referenced bundle (verifier.MaterializeBundle) to reconstruct
  the predicate the signature binds and to resolve the artifact's full
  descriptor — MaterializedBundle now carries MediaType/Size, which a
  pointer (digest only) cannot supply.
- attestation.SignExisting signs the predicate with ambient OIDC, attaches
  the Sigstore Bundle as an OCI referrer of the existing artifact (no
  re-emit), then patches the pointer's signer block in place and writes it
  back. Like Publish, it returns only an error and writes its artifact as a
  side effect; the registry-bound happy path is not unit-testable.

Refactor: buildPointerInputsFromOutcome reuses the new
PointerSignerFromSignature helper (shared zero-Rekor rule). WritePointer
now delegates to a new WritePointerFile that writes to an exact path.

Completes #1434 (sign-existing); follow-up to the --no-sign PR.

Tests: pointer-state guards (assertUnsignedSignable: already-signed,
no-bundle, multi-attestation), SignExisting input guards, the
signature->pointer-signer mapping, and exact-path pointer writes.
@mchmarny mchmarny force-pushed the feat/evidence-sign-existing branch from ec33792 to f3256ca Compare June 24, 2026 14:52
@mchmarny mchmarny enabled auto-merge (squash) June 24, 2026 15:03
@mchmarny mchmarny merged commit a758de2 into main Jun 24, 2026
34 checks passed
@mchmarny mchmarny deleted the feat/evidence-sign-existing branch June 24, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cli area/docs size/XL theme/supply-chain SLSA, SBOM, Sigstore, and provenance verification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aicr evidence: two-phase push-without-sign + sign-existing support

2 participants