feat(verify): KMS/public-key bundle verification (aicr verify --key)#1238
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds native verification for key-signed bundles: a verification core (VerifyStatementWith + interfaces), keyless and key-based VerificationIdentity implementations, centralized KMS URI resolution with timeout/error classification, PEM public-key loading with a size cap, verifier wiring toggled by VerifyOptions.Key, CLI flag/docs updates for --key, and unit/integration tests covering KMS/PEM and verification behaviors. Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/bundler/verifier/verifier_test.go (1)
364-375:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winCheck
Close()error for the writable temp file.
f.Close()is currently ignored in the oversized-file test. Please assert the close result so write flush failures don’t get silently dropped.Proposed fix
- f.Close() + if closeErr := f.Close(); closeErr != nil { + t.Fatal(closeErr) + }As per coding guidelines, writable file handles must check
Close()errors.🤖 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/bundler/verifier/verifier_test.go` around lines 364 - 375, In the oversized-file test in verifier_test.go, don't ignore the result of f.Close(): after the write loop, call err = f.Close() (or closeErr := f.Close()) and assert it (e.g., t.Fatal or t.Fatalf with the error) so any flush/close failures are surfaced; locate the writable temp file variable f and replace the bare f.Close() with an error check that fails the test on non-nil error.Source: Coding guidelines
🤖 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 2464-2465: Update the `--key` CLI option description to explicitly
state that using a KMS URI (awskms://, gcpkms://, azurekms://) will perform
network/KMS provider calls to resolve the public key (i.e., verification is not
purely offline), note potential failure modes (network errors, permission/access
denied from the KMS, and unresolved keys) and recommend fallback to a local PEM
file when offline; ensure the same clarification is added for the duplicate
`--key` occurrence noted near the `--format` option so both descriptions mention
network calls and failure modes.
In `@pkg/bundler/attestation/keyverifyidentity.go`:
- Around line 56-74: Thread the parent context into loadPEMPublicKey and enforce
a bounded deadline: change loadPEMPublicKey to accept ctx (e.g.,
loadPEMPublicKey(ctx context.Context, keyRef string)), and from
NewKeyVerificationIdentity call it with a context.WithTimeout(ctx, <reasonable
duration>) and defer cancel(); ensure the timeout-wrapped context is used for
the file open/read so hung NFS/FUSE/pipe I/O is bounded. Also update the other
call site referenced (lines ~111-124) to use the new signature and apply the
same context.WithTimeout pattern; import time and remember to call cancel().
- Around line 48-55: NewKeyVerificationIdentity is incorrectly treating
hashivault:// refs as local PEM paths; update the KMS-scheme handling so
hashivault:// is treated as a KMS URI and routed through the shared KMS
resolution path. Specifically, ensure the KMS detection used by
NewKeyVerificationIdentity (via isKMSURI in kmsidentity.go) recognizes the
"hashivault" scheme, and make any necessary updates to resolveKMSPublicKey (or
its provider mapping) so hashivault:// URIs are resolved via the KMS resolution
seam rather than file-read logic; this will keep signing/verifying parity and
prevent hashivault refs from falling into the PEM code path.
In `@pkg/bundler/attestation/kmsidentity.go`:
- Around line 54-66: isKMSURI currently does a case-sensitive prefix check so
mixed-case KMS URIs (e.g., "GCPKMS://...") are treated as local paths; change
the check to be case-insensitive by normalizing the input (e.g., lowercasing
ref) before comparing against kmsURISchemes in isKMSURI so any scheme case will
match the known schemes (keep references to isKMSURI and kmsURISchemes).
In `@pkg/bundler/attestation/kmspublickey.go`:
- Around line 58-76: The KMS call error paths currently map all errors to
ErrCodeUnavailable; detect context deadline/cancel errors from the kms.Get and
sv.PublicKey failures (e.g., via errors.Is(err, context.DeadlineExceeded) or
errors.Is(err, context.Canceled)) and return errors.WrapWithContext(...,
errors.ErrCodeTimeout, ...) instead of ErrCodeUnavailable for those cases,
preserving the existing message and context map (ctxKeySigningKey: keyURI) for
both the kms.Get error branch and the sv.PublicKey error branch.
In `@pkg/bundler/verifier/verifier.go`:
- Around line 376-379: The readBoundedFile call in verifier.go currently wraps
all errors as ErrCodeInternal (see the data, err := readBoundedFile(bundlePath,
...) block and similar blocks at other locations), which clobbers structured
pkg/errors codes like ErrCodeInvalidRequest; change the error handling to check
whether err already has a structured code (i.e., is a pkg/errors coded error)
and if so return it unchanged, otherwise wrap it with
errors.Wrap(errors.ErrCodeInternal, "failed to read sigstore bundle:
"+bundlePath, err); apply the same pattern to the other bounded-read sites
referenced (lines around 401-404 and 426-429) so existing coded errors are
propagated and only uncoded errors are re-wrapped.
---
Outside diff comments:
In `@pkg/bundler/verifier/verifier_test.go`:
- Around line 364-375: In the oversized-file test in verifier_test.go, don't
ignore the result of f.Close(): after the write loop, call err = f.Close() (or
closeErr := f.Close()) and assert it (e.g., t.Fatal or t.Fatalf with the error)
so any flush/close failures are surfaced; locate the writable temp file variable
f and replace the bare f.Close() with an error check that fails the test on
non-nil error.
🪄 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: 14a9f56b-b501-4a3e-b92b-8dfb7e7870fc
📒 Files selected for processing (16)
docs/user/cli-reference.mdpkg/bundler/attestation/keylessverifyidentity.gopkg/bundler/attestation/keyverifyidentity.gopkg/bundler/attestation/keyverifyidentity_test.gopkg/bundler/attestation/kmsidentity.gopkg/bundler/attestation/kmspublickey.gopkg/bundler/attestation/kmspublickey_test.gopkg/bundler/attestation/verifying.gopkg/bundler/attestation/verifying_test.gopkg/bundler/attestation/verifytransparency.gopkg/bundler/verifier/verifier.gopkg/bundler/verifier/verifier_test.gopkg/cli/bundle.gopkg/cli/bundle_verify.gopkg/cli/bundle_verify_test.gopkg/defaults/timeouts.go
Coverage Report ✅
Coverage BadgeMerging this branch changes the coverage (1 decrease, 2 increase)
Coverage by fileChanged files (no unit tests)
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. |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
docs/user/cli-reference.md (1)
2464-2465:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpdate the section summary to remove the “fully offline” contradiction.
The new
--keydocumentation correctly explains KMS/network behavior, but it conflicts with the earlier statement at Line 2450 (“Verification is fully offline — no network calls are made.”). Please align that summary with the current behavior so users don’t get contradictory guidance.As per coding guidelines, “Document configuration boundaries and failure modes in documentation” and “Clearly distinguish current behavior from future intent in documentation.”
Suggested doc tweak
-Verify the integrity and attestation chain of a bundle. Verification is fully offline — no network calls are made. +Verify the integrity and attestation chain of a bundle. +Verification is offline when using local material (for example, a PEM key and a warmed trusted-root cache). +Using `--key` with a KMS URI may require network calls to the configured KMS provider.Also applies to: 2507-2510
🤖 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 `@docs/user/cli-reference.md` around lines 2464 - 2465, The summary sentence that currently claims "Verification is fully offline — no network calls are made." must be updated to reflect that using --key with KMS URIs (awskms://, gcpkms://, azurekms://) can perform network calls and may fail due to network/KMS auth issues; change the summary to state that verification is offline for local PEM public-key files but will contact KMS for awskms://, gcpkms:// or azurekms:// keys and document the potential failure modes (network, auth, KMS unavailability) and how it differs from --certificate-identity-regexp; apply the same wording change to the corresponding summary elsewhere in the file that covers lines around the second instance.Source: Coding guidelines
🤖 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 2507-2510: Remove the empty line inside the blockquote that starts
with "**`--key` network behavior:**" so there are no blank lines between quoted
paragraphs (fixing markdownlint MD028); edit the blockquote in
docs/user/cli-reference.md to either join the paragraphs without an intervening
blank line or split into two separate blockquotes, ensuring no blank line
remains within any single blockquote.
In `@pkg/bundler/attestation/keyverifyidentity.go`:
- Around line 49-51: Update the exported comment for the key-based
VerificationIdentity to accurately reflect supported KMS schemes: include
"hashivault://" alongside "awskms:// | gcpkms:// | azurekms://" or replace the
explicit list with a generic reference to the shared scheme-detection used by
the --key handling (mentioning keyRef and the key-based VerificationIdentity
factory). Locate the comment around the key-based VerificationIdentity/keyRef
description in keyverifyidentity.go and edit it so maintainers/users aren't
misled about supported schemes.
In `@pkg/bundler/attestation/verifying.go`:
- Around line 124-133: The function loadSigstoreBundleBytes is treating
malformed/invalid caller-supplied bundle bytes as internal errors; change the
error codes used when protojson.Unmarshal fails and when bundle.NewBundle
returns an error from errors.ErrCodeInternal to errors.ErrCodeInvalidRequest so
these are classified as client/request validation failures; update the two
errors created around protojson.Unmarshal and bundle.NewBundle to wrap with
ErrCodeInvalidRequest while keeping the existing messages and error wrapping.
In `@pkg/bundler/verifier/verifier.go`:
- Around line 82-85: Update the VerifyOptions.Key documentation comment in
verifier.go to accurately reflect supported KMS URI schemes: either add
"hashivault://" to the listed schemes (awskms:// | gcpkms:// | azurekms:// |
hashivault://) or replace the hardcoded list with a brief note referencing the
shared KMS detection logic used by the bundler so the comment stays correct if
supported schemes change; modify the comment that describes VerifyOptions.Key to
point to the shared detection/registry or include hashivault:// as appropriate.
---
Duplicate comments:
In `@docs/user/cli-reference.md`:
- Around line 2464-2465: The summary sentence that currently claims
"Verification is fully offline — no network calls are made." must be updated to
reflect that using --key with KMS URIs (awskms://, gcpkms://, azurekms://) can
perform network calls and may fail due to network/KMS auth issues; change the
summary to state that verification is offline for local PEM public-key files but
will contact KMS for awskms://, gcpkms:// or azurekms:// keys and document the
potential failure modes (network, auth, KMS unavailability) and how it differs
from --certificate-identity-regexp; apply the same wording change to the
corresponding summary elsewhere in the file that covers lines around the second
instance.
🪄 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: 5458b410-ab0c-4de0-9422-c30907f55797
📒 Files selected for processing (16)
docs/user/cli-reference.mdpkg/bundler/attestation/keylessverifyidentity.gopkg/bundler/attestation/keyverifyidentity.gopkg/bundler/attestation/keyverifyidentity_test.gopkg/bundler/attestation/kmsidentity.gopkg/bundler/attestation/kmspublickey.gopkg/bundler/attestation/kmspublickey_test.gopkg/bundler/attestation/verifying.gopkg/bundler/attestation/verifying_test.gopkg/bundler/attestation/verifytransparency.gopkg/bundler/verifier/verifier.gopkg/bundler/verifier/verifier_test.gopkg/cli/bundle.gopkg/cli/bundle_verify.gopkg/cli/bundle_verify_test.gopkg/defaults/timeouts.go
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
pkg/bundler/verifier/verifier_test.go (1)
364-376:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winCheck the writable file
Close()error in the oversized-file test.At Line 375,
f.Close()ignores writeback/flush failure and can mask test setup failures.Suggested fix
- f.Close() + if closeErr := f.Close(); closeErr != nil { + t.Fatal(closeErr) + }As per coding guidelines, “Writable file handles must check Close() error; use closeErr := f.Close() and propagate if prior error is nil”.
🤖 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/bundler/verifier/verifier_test.go` around lines 364 - 376, The test writes an oversized file but ignores the f.Close() error; update the test in verifier_test.go to capture and check closeErr after f.Close() (e.g., closeErr := f.Close()) and if closeErr != nil and there is no prior fatal error, call t.Fatal or t.Fatalf to fail the test; reference the os.File variable f and ensure the Close() error is propagated/checked according to the writable-handle guideline.Source: Coding guidelines
pkg/cli/bundle_verify.go (1)
17-24:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReject explicit empty
--keyto prevent silent downgrade to keyless verification.At Line 133,
--keyis accepted as-is. If a caller passes an empty value (--key ''), the flow degrades to keyless verification instead of failing fast.Suggested fix
import ( "context" "encoding/json" "fmt" "io" "log/slog" "path/filepath" + "strings" @@ verifyOpts := &verifier.VerifyOptions{} @@ - verifyOpts.Key = cmd.String("key") + if cmd.IsSet("key") { + keyRef := strings.TrimSpace(cmd.String("key")) + if keyRef == "" { + return errors.New(errors.ErrCodeInvalidRequest, "--key must not be empty") + } + verifyOpts.Key = keyRef + }Also applies to: 124-134
♻️ Duplicate comments (1)
docs/user/cli-reference.md (1)
2507-2510:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove blank line inside blockquote (markdownlint MD028).
Line 2510 contains a blank line inside the blockquote, which violates markdownlint rule MD028 (no-blanks-blockquote). This is a duplicate of a previous review comment.
📝 Proposed fix
> **`--key` network behavior:** Resolving a **KMS URI** (`awskms://`, `gcpkms://`, `azurekms://`) makes network calls to the KMS provider to fetch the public key, so credentials for that provider must be available in the environment. A **local PEM** public-key file is read from disk with no provider calls; export it once with `cosign public-key --key <kms-uri>` (or your provider's console) and verify anywhere. -> > Note that resolving the key is only part of verification. By default the bundle's Rekor transparency-log entry is also checked. Its inclusion proof is embedded in the bundle, so no live Rekor call is made, but the check needs the Sigstore trusted root: that root is loaded from the local cache when present and otherwise fetched over the network, so run `aicr trust update` once to pre-populate it. A local PEM key therefore makes verification fully offline only when the trusted-root cache is already warm. Verification that drops the transparency-log requirement entirely, for true air-gapped use, is tracked in [`#1154`](https://github.com/NVIDIA/aicr/issues/1154).🤖 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 `@docs/user/cli-reference.md` around lines 2507 - 2510, In the docs/user/cli-reference.md blockquote that starts with "**`--key` network behavior:**", remove the blank line inside the blockquote (the empty line after the first paragraph) so the entire blockquote has no internal blank lines (resolving markdownlint MD028); locate the paragraph by its opening text and delete the stray empty line to match the previous corrected instance.
🤖 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 2186-2190: The directional reference is wrong: change the phrase
"See the [`aicr verify`](`#aicr-verify`) flags above" to reference the section
below. Update the text in the paragraph referencing the `aicr verify` anchor
(the link text `aicr verify` / anchor `#aicr-verify`) so it says "See the [`aicr
verify`](`#aicr-verify`) flags below" (or reword to "See the [`aicr
verify`](`#aicr-verify`) section" without "above") to accurately point to the
later `aicr verify` section.
In `@pkg/bundler/attestation/keylessverifyidentity.go`:
- Around line 42-49: The TrustedMaterial method on keylessVerificationIdentity
accepts a context but never checks it before calling trust.GetTrustedMaterial;
update the method signature to name the context parameter (e.g., ctx) and
immediately honor cancellation by checking ctx.Err() or selecting on ctx.Done()
and returning a wrapped context.Canceled/Err if cancelled, before invoking
trust.GetTrustedMaterial so you avoid doing work after cancellation; keep the
existing error wrapping behavior for trust.GetTrustedMaterial failures.
In `@pkg/bundler/attestation/keyverifyidentity.go`:
- Around line 56-67: The stored identity in NewKeyVerificationIdentity is using
the caller-provided keyRef (preserving casing) which can cause mismatches; when
isKMSURI(keyRef) is true, canonicalize the URI scheme before saving by passing
keyRef through normalizeURIScheme (or equivalent) and set
keyVerificationIdentity.identity to that normalized value; also ensure the
signing-side NewKMSIdentity uses the same normalizeURIScheme normalization so
both signing (NewKMSIdentity) and verification (NewKeyVerificationIdentity)
store identical canonical URIs.
In `@pkg/bundler/attestation/verifying_test.go`:
- Around line 130-151: Add a positive test case to TestSignerIdentityFromResult
that exercises the successful extraction path: in the tests slice, add an entry
where result is a *verify.VerificationResult containing a non-nil Signature
(verify.SignatureVerificationResult) whose Certificate has a SAN (e.g. DNS or
URI field populated) and set want to the expected SAN string; this will call
signerIdentityFromResult and verify it returns the populated SAN instead of an
empty string. Ensure you reference the existing test harness
(TestSignerIdentityFromResult) and the types verify.VerificationResult,
verify.SignatureVerificationResult, and signerIdentityFromResult when adding the
new case.
In `@pkg/bundler/attestation/verifying.go`:
- Around line 156-166: The current containsCertChainError function is too broad
because the plain "x509" token matches unrelated errors; remove the generic
"x509" entry from staleIndicators and instead only treat x509-related messages
as stale-root when they include a specific certificate chain indicator (e.g.,
require both "x509" and "signed by unknown authority" or another explicit
phrase). Update containsCertChainError (and its loop over staleIndicators) so
the generic "x509" is not matched alone and add a separate conditional that
returns true only when the lowercased error string contains "x509" plus a
specific chain-failure phrase like "signed by unknown authority" to avoid
mislabeling expiry and other non-root issues.
In `@pkg/cli/bundle_verify_test.go`:
- Around line 69-77: The test TestBundleVerifyCmd_KeyFlagPlumbing currently uses
an empty directory so it never reaches the key-verification branch; update the
fixture used in that test (and the similar case at lines 97-113) to point to a
bundle directory that contains valid checksums and includes a bundle attestation
file so verification proceeds to attestation validation, then keep the
verifyOpts.Key = cmd.String("key") plumbing and assert that the returned error
is the key-resolution/verification error (i.e., from resolving/validating the
key) rather than a missing-checksums error; locate the test by
TestBundleVerifyCmd_KeyFlagPlumbing and the verifyOpts.Key assignment to modify
the fixture and adjust the expected error assertions accordingly.
---
Outside diff comments:
In `@pkg/bundler/verifier/verifier_test.go`:
- Around line 364-376: The test writes an oversized file but ignores the
f.Close() error; update the test in verifier_test.go to capture and check
closeErr after f.Close() (e.g., closeErr := f.Close()) and if closeErr != nil
and there is no prior fatal error, call t.Fatal or t.Fatalf to fail the test;
reference the os.File variable f and ensure the Close() error is
propagated/checked according to the writable-handle guideline.
---
Duplicate comments:
In `@docs/user/cli-reference.md`:
- Around line 2507-2510: In the docs/user/cli-reference.md blockquote that
starts with "**`--key` network behavior:**", remove the blank line inside the
blockquote (the empty line after the first paragraph) so the entire blockquote
has no internal blank lines (resolving markdownlint MD028); locate the paragraph
by its opening text and delete the stray empty line to match the previous
corrected instance.
🪄 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: 7eec5ac1-8613-4f2b-8b82-1b66fbf68ca6
📒 Files selected for processing (16)
docs/user/cli-reference.mdpkg/bundler/attestation/keylessverifyidentity.gopkg/bundler/attestation/keyverifyidentity.gopkg/bundler/attestation/keyverifyidentity_test.gopkg/bundler/attestation/kmsidentity.gopkg/bundler/attestation/kmspublickey.gopkg/bundler/attestation/kmspublickey_test.gopkg/bundler/attestation/verifying.gopkg/bundler/attestation/verifying_test.gopkg/bundler/attestation/verifytransparency.gopkg/bundler/verifier/verifier.gopkg/bundler/verifier/verifier_test.gopkg/cli/bundle.gopkg/cli/bundle_verify.gopkg/cli/bundle_verify_test.gopkg/defaults/timeouts.go
mchmarny
left a comment
There was a problem hiding this comment.
Solid PR — the composer-shaped interface refactor (VerificationIdentity × VerifyTransparencyPolicy → VerifyStatementWith) cleanly isolates the key-based path, the keyless path is preserved through the same composer with no behavior change, and the new tests cover the identity surface well (PEM happy path, bad PEM, oversize, missing file, unknown KMS scheme). CI is fully green including all three GPU jobs.
Three minor findings inline; nothing blocks merge. One out-of-diff observation: pkg/cli/bundle_verify.go:138 wraps verifier.Verify's structured return with ErrCodeInternal, flattening the ErrCodeUnauthorized/ErrCodeInvalidRequest codes the verifier already carries — pre-existing on main, worth a separate cleanup but out of scope here.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/cli/bundle_verify.go (1)
133-139:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve verifier error codes for
--keyfailures.Line 138 currently reclassifies all
verifier.Verify(...)failures asErrCodeInternal. With--key, user-input failures (bad PEM path, invalid/unsupported key URI) should keep their original structured codes; otherwise CLI exit semantics are wrong for automation.Based on learnings: if an error is already structured/coded, propagate it as-is and prefer
errors.PropagateOrWrap(...)over unconditional re-wrap. As per coding guidelines, keep structured error classification intact.Proposed fix
result, err := verifier.Verify(ctx, absDir, verifyOpts) if err != nil { - return errors.Wrap(errors.ErrCodeInternal, "bundle verification failed", err) + return errors.PropagateOrWrap(err, errors.ErrCodeInternal, "bundle verification failed") }🤖 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/bundle_verify.go` around lines 133 - 139, The current error handling after calling verifier.Verify(ctx, absDir, verifyOpts) always wraps errors with errors.ErrCodeInternal, losing structured verifier error codes for --key failures; change the return to use errors.PropagateOrWrap(err, errors.ErrCodeInternal, "bundle verification failed") (or the equivalent PropagateOrWrap API used in the codebase) so that if the verifier returned a coded/structured error it's propagated as-is, otherwise it is wrapped with ErrCodeInternal and the "bundle verification failed" message; reference verifyOpts.Key and verifier.Verify to locate the call site and replace the unconditional errors.Wrap usage accordingly.Sources: Coding guidelines, Learnings
🤖 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.
Outside diff comments:
In `@pkg/cli/bundle_verify.go`:
- Around line 133-139: The current error handling after calling
verifier.Verify(ctx, absDir, verifyOpts) always wraps errors with
errors.ErrCodeInternal, losing structured verifier error codes for --key
failures; change the return to use errors.PropagateOrWrap(err,
errors.ErrCodeInternal, "bundle verification failed") (or the equivalent
PropagateOrWrap API used in the codebase) so that if the verifier returned a
coded/structured error it's propagated as-is, otherwise it is wrapped with
ErrCodeInternal and the "bundle verification failed" message; reference
verifyOpts.Key and verifier.Verify to locate the call site and replace the
unconditional errors.Wrap usage accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 3a868c9f-d2af-4a5b-893e-f2bb33312711
📒 Files selected for processing (16)
docs/user/cli-reference.mdpkg/bundler/attestation/keylessverifyidentity.gopkg/bundler/attestation/keyverifyidentity.gopkg/bundler/attestation/keyverifyidentity_test.gopkg/bundler/attestation/kmsidentity.gopkg/bundler/attestation/kmspublickey.gopkg/bundler/attestation/kmspublickey_test.gopkg/bundler/attestation/verifying.gopkg/bundler/attestation/verifying_test.gopkg/bundler/attestation/verifytransparency.gopkg/bundler/verifier/verifier.gopkg/bundler/verifier/verifier_test.gopkg/cli/bundle.gopkg/cli/bundle_verify.gopkg/cli/bundle_verify_test.gopkg/defaults/timeouts.go
#1152) Add `aicr verify --key <kms-uri-or-pem>` to verify a key-signed bundle attestation against its public key, the verification counterpart to #407's KMS-backed signing. Accepts a KMS key URI (awskms:// | gcpkms:// | azurekms://) or a local PEM public-key file. Implemented as the verification dual of the signing-side interface composition: two small interfaces in pkg/bundler/attestation, VerificationIdentity and VerifyTransparencyPolicy, composed by VerifyStatementWith (mirroring SigningIdentity x TransparencyPolicy -> SignStatementWith). The keyless path is refactored to flow through the same composer; a key-based identity combines the public-good trusted root (so the Rekor tlog still verifies, since KMS signing uploads by default) with the caller's public key, using verify.WithKey(). KMS public-key resolution is shared with the signing path via resolveKMSPublicKey. --key swaps only the bundle-attestation verification; the binary attestation stays NVIDIA-keyless-pinned and still runs, so --key and --certificate-identity-regexp coexist (they verify different attestations). The no-downgrade property is enforced by sigstore-go itself: a Fulcio-cert bundle cannot verify under WithKey(). Private trust root (#1153) and offline/skip-tlog (#1154) slot in later as additional implementations of the two interfaces. Successful end-to-end crypto verification with live KMS + Rekor is covered by integration test #1214. Fixes #1152
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
pkg/bundler/verifier/verifier_test.go (1)
364-376:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winCheck
Close()error on the writable temp file.
TestReadBoundedFile_OversizedFilewrites tofbut ignoresf.Close()errors, which can mask flush/write failures and make the test outcome misleading.Suggested fix
- f.Close() + if closeErr := f.Close(); closeErr != nil { + t.Fatal(closeErr) + }As per coding guidelines, “Writable file handles must check Close() error; use closeErr := f.Close() and propagate if prior error is nil”.
🤖 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/bundler/verifier/verifier_test.go` around lines 364 - 376, The test TestReadBoundedFile_OversizedFile currently ignores the error returned by f.Close(); change the code to capture and check the Close() error (e.g., closeErr := f.Close()) and fail the test if Close() returns an error (or propagate it if a previous error variable is used), ensuring any flush/write failures on the temp file are detected; update references around the variable f in verifier_test.go accordingly.Source: Coding guidelines
pkg/cli/bundle_verify.go (1)
124-134:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReject explicit empty
--keyto prevent silent fallback to keyless verification.When
--keyis explicitly set to empty/whitespace,verifyOpts.Keybecomes empty and verification silently uses the keyless branch. That can bypass the caller’s intended key-pinned verification mode.Suggested fix
import ( "context" "encoding/json" "fmt" "io" "log/slog" "path/filepath" + "strings" @@ verifyOpts := &verifier.VerifyOptions{} @@ - verifyOpts.Key = cmd.String("key") + keyRef := strings.TrimSpace(cmd.String("key")) + if cmd.IsSet("key") && keyRef == "" { + return errors.New(errors.ErrCodeInvalidRequest, "--key must not be empty") + } + verifyOpts.Key = keyRef🤖 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/bundle_verify.go` around lines 124 - 134, Detect and reject an explicitly empty or whitespace `--key` instead of letting it silently fall back to keyless verification: after reading the flag value from cmd.String("key"), use strings.TrimSpace on that value and if it is empty AND the flag was explicitly provided (using the CLI's flag-present check, e.g., cmd.IsSet("key") or equivalent), return an error; only assign verifyOpts.Key when the trimmed value is non-empty. This change affects the verifyOpts assignment in the VerifyOptions construction (verifyOpts, VerifyOptions, verifyOpts.Key, cmd.String("key")).
♻️ Duplicate comments (1)
docs/user/cli-reference.md (1)
2507-2511:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix MD028: blank line inside blockquote.
There is a blank line inside the blockquote (appears to be around line 2509), which triggers
MD028 (no-blanks-blockquote). This is the same issue flagged in past review comments.📝 Proposed fix
Remove the blank
>line between the two paragraphs in the blockquote, joining them directly:> **`--key` network behavior:** Resolving a **KMS URI** (`awskms://`, `gcpkms://`, `azurekms://`) makes network calls to the KMS provider to fetch the public key, so credentials for that provider must be available in the environment. A **local PEM** public-key file is read from disk with no provider calls; export it once with `cosign public-key --key <kms-uri>` (or your provider's console) and verify anywhere. -> > Resolving the key is only part of verification. By default the bundle's Rekor transparency-log entry is also checked. Its inclusion proof is embedded in the bundle, so no live Rekor call is made, but the check needs the Sigstore trusted root: that root is loaded from the local cache when present and otherwise fetched over the network, so run `aicr trust update` once to pre-populate it. A local PEM key therefore makes verification fully offline only when the trusted-root cache is already warm. Verification that drops the transparency-log requirement entirely, for true air-gapped use, is tracked in [`#1154`](https://github.com/NVIDIA/aicr/issues/1154).Alternatively, split into two separate blockquotes if the logical separation is needed.
🤖 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 `@docs/user/cli-reference.md` around lines 2507 - 2511, The blockquote that starts with "**`--key` network behavior:**" contains an empty ">" line between paragraphs triggering MD028; remove that blank blockquote marker (or merge the two paragraphs into one) so there is no blank ">" line between them, or alternatively split them into two separate blockquotes if you want distinct quotes; ensure the resulting text no longer contains an isolated ">" line inside that blockquote.Source: Linters/SAST tools
🤖 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.
Outside diff comments:
In `@pkg/bundler/verifier/verifier_test.go`:
- Around line 364-376: The test TestReadBoundedFile_OversizedFile currently
ignores the error returned by f.Close(); change the code to capture and check
the Close() error (e.g., closeErr := f.Close()) and fail the test if Close()
returns an error (or propagate it if a previous error variable is used),
ensuring any flush/write failures on the temp file are detected; update
references around the variable f in verifier_test.go accordingly.
In `@pkg/cli/bundle_verify.go`:
- Around line 124-134: Detect and reject an explicitly empty or whitespace
`--key` instead of letting it silently fall back to keyless verification: after
reading the flag value from cmd.String("key"), use strings.TrimSpace on that
value and if it is empty AND the flag was explicitly provided (using the CLI's
flag-present check, e.g., cmd.IsSet("key") or equivalent), return an error; only
assign verifyOpts.Key when the trimmed value is non-empty. This change affects
the verifyOpts assignment in the VerifyOptions construction (verifyOpts,
VerifyOptions, verifyOpts.Key, cmd.String("key")).
---
Duplicate comments:
In `@docs/user/cli-reference.md`:
- Around line 2507-2511: The blockquote that starts with "**`--key` network
behavior:**" contains an empty ">" line between paragraphs triggering MD028;
remove that blank blockquote marker (or merge the two paragraphs into one) so
there is no blank ">" line between them, or alternatively split them into two
separate blockquotes if you want distinct quotes; ensure the resulting text no
longer contains an isolated ">" line inside that blockquote.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: df715a54-77b7-4897-b2f5-0df935a2d9c6
📒 Files selected for processing (16)
docs/user/cli-reference.mdpkg/bundler/attestation/keylessverifyidentity.gopkg/bundler/attestation/keyverifyidentity.gopkg/bundler/attestation/keyverifyidentity_test.gopkg/bundler/attestation/kmsidentity.gopkg/bundler/attestation/kmspublickey.gopkg/bundler/attestation/kmspublickey_test.gopkg/bundler/attestation/verifying.gopkg/bundler/attestation/verifying_test.gopkg/bundler/attestation/verifytransparency.gopkg/bundler/verifier/verifier.gopkg/bundler/verifier/verifier_test.gopkg/cli/bundle.gopkg/cli/bundle_verify.gopkg/cli/bundle_verify_test.gopkg/defaults/timeouts.go
Summary
Adds
aicr verify --key <kms-uri-or-pem>, verifying a key-signed bundle attestation against its public key. This is the verification counterpart to #407's KMS-backed signing, accepting a KMS key URI (awskms://|gcpkms://|azurekms://) or a local PEM public-key file.Motivation / Context
Epic #1149 (Closed Supply Chain, V1) requires that every signing mode have a matching verification path in the same environment. KMS-backed signing (#407) shipped, but
aicr verifycould only verify keyless bundles against the public-good trusted root, so a KMS-signed bundle was unverifiable by the toolchain. This closes that asymmetry for the key-based path.Fixes: #1152
Related: #1149 (epic), #407 (KMS signing, the counterpart), #1153 / #1154 (private trust root / offline verify, future siblings), #1214 (KMS integration test)
Type of Change
Component(s) Affected
cmd/aicr,pkg/cli)pkg/bundler,pkg/component/*)docs/,examples/)Implementation Notes
Implemented as the verification dual of the signing-side interface composition. Two small interfaces in
pkg/bundler/attestation—VerificationIdentity(trust anchors + signer-matching policy) andVerifyTransparencyPolicy(tlog requirement) — are composed byVerifyStatementWith, mirroringSigningIdentity×TransparencyPolicy→SignStatementWith.keyVerificationIdentitycombines the public-good trusted root (so the Rekor tlog still verifies, since KMS signing uploads by default) with the caller's public key, usingverify.WithKey(). KMS public-key resolution is shared with the signing path via an extractedresolveKMSPublicKey; local PEM is loaded with a bounded read againstdefaults.MaxPublicKeyPEMBytes.--keyswaps only the bundle attestation verification. The binary attestation still runs and stays NVIDIA-keyless-pinned, so--keyand--certificate-identity-regexpcoexist (they verify different attestations). This diverges from the issue's literal "mutually exclusive" wording; rationale is in the issue thread.WithKey()("expected key signature, not certificate").Testing
make test-coverage: full unit suite green, module total 75.7% ≥ 75% threshold.make lint: golangci-lint 0 issues, vet / license headers / AGENTS.md-sync clean.pkg/bundler/attestation78.3% → 76.0% (the uncovered lines are live-KMS resolution and the sigstore crypto-success path, which require live KMS + Rekor and are covered by integration test Integration-test KMS-backed bundle signing via LocalStack (AWS) #1214);pkg/bundler/verifier64.0% → 65.1%.e2e(KWOK/kind cluster) andgrypescan.Risk Assessment
pkg/bundler/{attestation,verifier}andpkg/cli.Rollout notes: Additive and backwards compatible. The keyless verification path is preserved (refactored through the new composer, behavior unchanged);
--keyis opt-in. No migration required.Checklist
make testwith-race)make lint)git commit -S)