Skip to content

feat(EC-1957): verify base image release signatures - #1780

Open
joejstuart wants to merge 9 commits into
conforma:mainfrom
joejstuart:EC-1957
Open

feat(EC-1957): verify base image release signatures#1780
joejstuart wants to merge 9 commits into
conforma:mainfrom
joejstuart:EC-1957

Conversation

@joejstuart

@joejstuart joejstuart commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add signature-based base image verification using ec.sigstore.verify_image() with signing_identities rule data (named entry rh-release)
  • Deprecation warning when allowed_registry_prefixes is configured without signing_identities
  • Validation: signing_identities structure, entry types, sigstore opts (via sigstore.validate), and missing rh-release key
  • allowed_registry_prefixes format always validated (type, uniqueness); minItems requirement relaxed when a valid signing identity is present
  • Comprehensive tests covering signature verification, deprecation warnings, validation, and edge cases

Resolves: EC-1957

Test plan

  • make ci passes (1025/1025 tests, 0 lint violations)
  • Review signature verification with a real key against a signed image
  • Verify deprecation warning appears in policy evaluation output

🤖 Generated with Claude Code

…istries

Add a third path in _image_ref_permitted that verifies a base image's
release signature using ec.sigstore.verify_image() with a
release_public_key from rule data. This provides stronger cryptographic
assurance than registry prefix matching.

- Add signature verification clause to _image_ref_permitted
- Relax allowed_registries_provided to pass when only release_public_key
  is configured
- Add deprecation warn rule when prefixes are used without a release key
- Add schema validation for release_public_key (must be a string)
- Update example rule_data.yml with release_public_key entry
- Regenerate docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:11 AM UTC · Completed 2:20 AM UTC
Commit: 87c4a29 · View workflow run →

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

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

Base image release policies now support signature verification through release_public_key, retain digest and registry-prefix checks, validate the new configuration, warn on deprecated prefix matching, and update related examples and documentation.

Changes

Base image signature policy

Layer / File(s) Summary
Signature verification and rule-data validation
policy/release/base_image_registries/base_image_registries.rego, example/data/rule_data.yml
Adds release-key configuration, signature-based image permission, key type validation, and warnings for registry-prefix-only configuration.
Signature and deprecation test coverage
policy/release/base_image_registries/base_image_registries_test.rego
Tests successful and failed signature verification, key-only configuration, deprecation warnings, invalid key types, and Sigstore option handling.
Policy documentation and navigation
antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc, antora/docs/modules/ROOT/pages/release_policy.adoc, antora/docs/modules/ROOT/partials/release_policy_nav.adoc, design/sigstore-verification.md
Documents updated configuration requirements, deprecated prefix matching, ruleset navigation entries, and Sigstore verification behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RuleData
  participant BaseImagePolicy
  participant Sigstore
  participant PolicyResult
  RuleData->>BaseImagePolicy: provide release_public_key
  BaseImagePolicy->>Sigstore: verify image signature
  Sigstore-->>BaseImagePolicy: return verification result
  BaseImagePolicy->>PolicyResult: permit or deny base image
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: verifying base image release signatures.
Description check ✅ Passed The description explains the change, provides context through the ticket, and includes a test plan with completed and pending checks.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 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 `@antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc`:
- Around line 12-16: Update all specified base-image documentation to describe
registry-prefix, snapshot-digest, and release-signature verification. In
antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc lines
12-16, include release_public_key in the solution; in lines 24-28, rename the
rule/title and solution to cover all three verification paths. Update the
permitted-image labels in antora/docs/modules/ROOT/pages/release_policy.adoc
lines 35-38, 119-122, and 345-348, and the corresponding navigation label in
antora/docs/modules/ROOT/partials/release_policy_nav.adoc lines 22-26.

In `@antora/docs/modules/ROOT/pages/release_policy.adoc`:
- Around line 362-363: Update the visible label for the xref targeting
github_certificate__gh_workflow_ref to “GitHub Certificate Checks: GitHub
Workflow Ref”; leave the github_certificate__gh_workflow_repository link label
unchanged.

In `@policy/release/base_image_registries/base_image_registries.rego`:
- Around line 235-238: Update the _rule_data_errors rule to distinguish an
absent release key from a present value before type validation, so an explicitly
provided empty array is rejected as a non-string while a missing key remains
allowed. Validate every present release key value with the existing string
check, and add a regression case covering release_public_key: [].
🪄 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: CHILL

Plan: Enterprise

Run ID: 7be5a78f-860d-4e49-8d34-07fcc03095e5

📥 Commits

Reviewing files that changed from the base of the PR and between e562e23 and 769d8d6.

📒 Files selected for processing (6)
  • antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc
  • antora/docs/modules/ROOT/pages/release_policy.adoc
  • antora/docs/modules/ROOT/partials/release_policy_nav.adoc
  • example/data/rule_data.yml
  • policy/release/base_image_registries/base_image_registries.rego
  • policy/release/base_image_registries/base_image_registries_test.rego

Comment thread antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc Outdated
Comment thread antora/docs/modules/ROOT/pages/release_policy.adoc Outdated
Comment thread policy/release/base_image_registries/base_image_registries.rego Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [backward-incompatible] policy/release/base_image_registries/base_image_registries.rego:196 — Existing consumers providing allowed_registry_prefixes without signing_identities will now receive a new warning-severity deny result on every evaluation. Consumers that treat all deny results equally (e.g., counting deny results or failing on any deny regardless of severity) will see a behavioral change. This is an intentional deprecation nudge and well-behaved consumers filtering by severity should not break, but the change is worth noting as it affects all current users of the prefix-based approach.

Low

  • [logic-error] policy/lib/sigstore/sigstore.rego:85_validate_keyless_rekor only checks for rekor_url in keyless verification, while _validate_key_rekor accepts rekor_url, rekor_public_key, or ignore_rekor. This asymmetry is likely intentional (keyless verification requires Rekor timestamps for short-lived Fulcio certificates), but the design doc does not document the distinction.

  • [fail-open] policy/release/base_image_registries/base_image_registries.rego:108 — The _image_ref_permitted else-if chain evaluates sigstore verification last. When allowed_registry_prefixes contains broad prefixes, images from those registries are permitted without signature verification even when signing_identities is configured. This is intentional for backward compatibility during migration; the deprecation warning alerts operators.

  • [fail-open] policy/release/base_image_registries/base_image_registries.rego:113 — When signing_identities exists but lacks the rh-release key, a warning is emitted but the system falls back to prefix-based matching without a hard failure.

  • [misleading-label] policy/release/base_image_registries/base_image_registries.rego — The failure_msg still reads Base image %q is from a disallowed registry but the rule now permits images via signature verification regardless of registry. Consider updating to Base image %q is not permitted.

  • [pattern-inconsistency] policy/release/base_image_registries/base_image_registries.rego:199 — The deprecation warning message omits the Rule data prefix used consistently by all other _rule_data_errors entries in this file and across the codebase.

  • [pattern-inconsistency] policy/release/base_image_registries/base_image_registries.rego:176 — Descriptive comment above the first _rule_data_errors rule was removed. This commenting pattern is established across the codebase for validation blocks.

  • [scope-creep] policy/release/base_image_registries/base_image_registries.rego — The rh-release name is hardcoded as a constant while signing_identities is designed as a generic named-identity map. This is a reasonable v1 choice but worth documenting the rationale.

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego:113_signing_identity relies on OPA's behavior that string-key indexing of an array is undefined (not an error). Technically correct but non-obvious to readers.

Previous run

Review

Findings

Low

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego — When signing_identities is configured as an object but does not contain the expected rh-release key (e.g., signing_identities: {"wrong-name": {...}}), _signing_identity is undefined. This causes not _signing_identity to be true, falling back to allowed_registry_prefixes validation. The user gets a message about missing registry prefixes rather than about the misnamed signing identity key. Consider adding a validation rule that fires when signing_identities is a non-empty object but does not contain the rh-release key.

  • [asymmetric-validation] policy/lib/sigstore/sigstore.rego:79_validate_keyless_rekor requires rekor_url for keyless verification and does not accept rekor_public_key as an alternative. In contrast, _validate_key_rekor accepts rekor_url, rekor_public_key, or ignore_rekor. This asymmetry may be intentional (keyless verification typically requires an online Rekor service), but is worth confirming.

  • [scope-drift-naming-mismatch] policy/release/base_image_registries/base_image_registries.rego — The PR body references release_public_key four times as the rule data key name, but the implementation uses signing_identities with a nested rh-release entry. The PR description should be updated to match the code.

  • [naming-convention] policy/release/base_image_registries/base_image_registries.rego:173 — The PR removes the descriptive comment # Verify allowed_registry_prefixes is a non-empty list of strings above the _rule_data_errors block. Many other release policy files have such comments above their _rule_data_errors blocks. Consider restoring the comment (updated for new logic) and adding analogous comments above each new _rule_data_errors block.

  • [error-handling-idiom] policy/release/base_image_registries/base_image_registries.rego:196 — The new _rule_data_errors blocks for signing_identities type validation use expected an object, got %s format rather than the established Rule data %s has unexpected format: %s wrapping library-provided messages. The format is close to the established pattern but slightly divergent.

  • [authorization-bypass] policy/release/base_image_registries/base_image_registries.rego:98 — When both allowed_registry_prefixes and signing_identities are configured, the prefix-matching branch runs first, permitting images without signature verification. This is an intentional migration path — the PR documents prefix matching as deprecated and includes a deprecation warning. Consider documenting in the design doc or METADATA that prefix matching takes precedence during migration.

  • [precedence-ordering] policy/release/base_image_registries/base_image_registries.rego:108 — The _image_ref_permitted rule evaluates prefix matching first, then snapshot digest, then sigstore verification. The METADATA says signature verification is "preferred" and prefix matching is "deprecated", yet the deprecated check runs first. This is a deliberate design choice for backward compatibility during migration.

Previous run (2)

Review

Findings

Medium

  • [test-inadequate] policy/release/base_image_registries/base_image_registries_test.regotest_no_release_key_is_noop does not override data.rule_data, so example/data/rule_data.yml (which now includes signing_identities.rh-release) is loaded. The test passes because the image registry.redhat.io/ubi matches allowed_registry_prefixes from example data, not because the "no release key" code path works correctly. The test does not exercise the scenario its name describes.
    Remediation: Override data.rule_data to explicitly exclude signing_identities, e.g., with data.rule_data as {"allowed_registry_prefixes": ["registry.redhat.io/"]}.

Low

  • [failure-msg-mismatch] policy/release/base_image_registries/base_image_registries.regofailure_msg remains Base image %q is from a disallowed registry but the rule now permits images via signature verification. The rule title was updated to "Base image is permitted" but the error message was not updated to match. An image that fails signature verification receives a misleading message about "disallowed registry."
    Remediation: Update failure_msg to Base image %q is not permitted.

  • [error-handling-idiom] policy/release/base_image_registries/base_image_registries.rego — Sigstore verification errors are silently swallowed in the _image_ref_permitted else-if chain. Users receive a generic "disallowed registry" message with no indication that signature verification was attempted and failed. Compare source_image.rego which surfaces per-error messages via some raw_error in info.errors.

  • [missing-test] policy/release/base_image_registries/base_image_registries_test.rego — No test for a signing_identities map present but without the rh-release key (e.g., {"signing_identities": {"some-other-name": {...}}}). In this case _signing_identity is undefined, falling back to registry prefix matching.

  • [validation-bypass] policy/release/base_image_registries/base_image_registries.rego — When a valid signing identity is configured, schema validation for allowed_registry_prefixes is skipped entirely due to the not _signing_identity guard. Prefix matching in _image_ref_permitted still runs using unvalidated data. Practical impact is minimal since malformed prefixes would fail startswith().

  • [code-organization] policy/release/base_image_registries/base_image_registries.rego_signing_identity rule is placed between _image_ref_permitted and _cyclonedx_base_images. Consider moving it adjacent to _signing_identities_key and _signing_identity_name at the bottom of the file.

  • [authorization-logic] policy/release/base_image_registries/base_image_registries.rego_image_ref_permitted checks prefix matching before signature verification. By design for backward-compatible migration, but means stronger signature-based verification is bypassed for images matching a prefix.

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego — When signing_identities is an empty object {}, _signing_identity is undefined and falls back silently to prefix validation. Behavior is correct but not explicitly validated.

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego — Rekor config validation can fire alongside schema errors for malformed input (e.g., public_key: 42), producing redundant error messages.

  • [design-smell] policy/release/base_image_registries/base_image_registries.rego — The signing identity name rh-release is hardcoded via _signing_identity_name. The map structure implies extensibility but only one entry is consulted.

  • [architectural-conflict] policy/release/base_image_registries/base_image_registries.rego — Two sigstore verification patterns now coexist: shared sigstore.opts (used by source_image.rego) and per-identity rule data (this PR). The design doc explains the rationale.

Previous run (3)

Review

Findings

Medium

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego:114 — When signing_identities is configured as {rh-release: {}}, _signing_identity resolves to {} (a defined but empty object in Rego). This causes: (1) not _signing_identity to be false, skipping allowed_registry_prefixes validation with minItems: 1, (2) ec.sigstore.verify_image called with empty options (likely returning success: false), and (3) the fourth _rule_data_errors rule's val != {} guard skipping validation of the empty entry. The user gets image denials with no hint about the misconfiguration.
    Remediation: Add validation that the rh-release object contains at least one verification-relevant field (e.g., public_key or certificate_identity), or remove the val != {} guard so empty objects trigger a validation error.

  • [validation-bypass] policy/release/base_image_registries/base_image_registries.rego:178 — The not _signing_identity guard on the allowed_registry_prefixes schema validation block means that when signing_identities IS configured, schema validation of allowed_registry_prefixes is completely skipped — even for malformed data like [1, 2, 3]. The startswith() call in the first _image_ref_permitted branch would silently fail on non-strings rather than producing a helpful validation error.
    Remediation: Always validate allowed_registry_prefixes format when the list is non-empty, regardless of signing_identities configuration. Scope the not _signing_identity guard to only skip the minItems: 1 requirement.

  • [error-handling-gap] policy/release/base_image_registries/base_image_registries.rego:110 — When ec.sigstore.verify_image returns {success: false, errors: [...]}, the individual error messages are silently discarded. The deny rule only reports "Base image is from a disallowed registry" with no indication of WHY signature verification failed (expired key, wrong identity, network error, etc.). Compare with source_image.rego which iterates info.errors to surface each verification error as a separate deny message.
    Remediation: Add a separate deny rule that surfaces sigstore verification errors when signing identity is configured but verification fails, following the source_image.rego pattern.

  • [failure-message-inconsistency] policy/release/base_image_registries/base_image_registries.rego:28 — The failure_msg still says Base image %q is from a disallowed registry but the rule now covers signature-based verification beyond registry matching. The rule title was updated ("Base image is permitted") and the description was updated, but the failure_msg was not. When an image fails signature verification, the message is misleading — the image may not be from a disallowed registry; its signature just didn't verify.
    Remediation: Update failure_msg to Base image %q is not permitted or similar. Note this is a contract-visible string change — coordinate with downstream consumers.

  • [authorization-model-change] policy/release/base_image_registries/base_image_registries.rego:108 — The _image_ref_permitted rule uses else if chaining with OR semantics: an image is permitted if it matches a registry prefix OR matches a snapshot digest OR has a valid signature. This is the intended design (the PR explicitly deprecates prefix matching in favor of signatures), but the trust model implications should be documented — key compromise scope now extends to all registries, not just those in the prefix list.
    Remediation: Document the OR semantics and trust model implications in the design doc or rule metadata.

Low

  • [comment-convention] policy/release/base_image_registries/base_image_registries.rego:178 — Descriptive comment above _rule_data_errors was removed. Other policy files precede _rule_data_errors with a short comment describing what is validated.
  • [hardcoded-config] policy/release/base_image_registries/base_image_registries.rego:235 — Only the rh-release identity name is consumed from the signing_identities map. Reasonable V1 design with the map structure providing future extensibility.
  • [insufficient-validation] policy/release/base_image_registries/base_image_registries.rego:220 — No schema validation of signing identity object contents (e.g., required public_key field). Consistent with existing conventions and fail-closed behavior limits risk.
  • [breaking-change-warning] policy/release/base_image_registries/base_image_registries.rego:197 — Existing configurations using only allowed_registry_prefixes will receive a new deprecation warning.
  • [architectural-coherence] policy/release/base_image_registries/base_image_registries.rego:108 — Two sigstore configuration patterns now exist (lib.sigstore.opts vs rule_data.signing_identities). The design doc addresses this distinction.
  • [rule-title-change] policy/release/base_image_registries/base_image_registries.rego — Rule titles changed; rule codes (short_name) are unchanged.
  • [internal-consistency] design/sigstore-verification.md:60 — Design doc does not address whether lib.sigstore.opts fields serve as schema reference for signing identity objects.
Previous run (4)

Review

Findings

Medium

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego:112 — When signing_identities.release is an empty object {}, _signing_identity is defined (empty objects are truthy in OPA), so allowed_registry_prefixes validation and deprecation warnings are both suppressed (not _signing_identity is false). Meanwhile, object.union(sigstore.opts, {}) returns sigstore.opts unchanged with public_key: "", causing all signature verifications to fail. The operator gets no validation error and no deprecation warning — yet all images not matching prefixes or digests are silently denied with the unhelpful "disallowed registry" message.
    Remediation: Add a _rule_data_errors rule checking whether _signing_identity is defined but lacks meaningful verification parameters (e.g., public_key is empty and no certificate fields are set).

  • [fail-open] policy/release/base_image_registries/base_image_registries.rego:178 — Schema validation for allowed_registry_prefixes (including minItems: 1) is suppressed when _signing_identity is defined, even if the signing identity object contains no usable sigstore fields (e.g., {"foo": "bar"}). While this is fail-closed at the policy level (images are denied, not permitted), the operator receives no configuration error alerting them to the misconfiguration. See also: [edge-case] finding above.
    Remediation: Validate that signing_identities.release contains at least one recognized sigstore field (e.g., public_key, certificate_identity) with a non-empty value before suppressing prefix validation.

  • [unvalidated-passthrough] policy/release/base_image_registries/base_image_registries.rego:108_signing_identity is merged onto sigstore.opts via object.union without restricting which fields can be overridden. This allows signing_identities.release entries to override deployment-wide defaults from default_sigstore_opts (e.g., setting ignore_rekor: true to disable transparency log verification). This override behavior is by design and tested (test_sigstore_opts_inherited), but operators should understand the precedence.
    Remediation: Document that signing_identities entries intentionally override deployment defaults via object.union, or consider validating entries contain only recognized sigstore option keys.

  • [scope-mismatch] PR description references release_public_key in four bullet points, but the implementation uses signing_identities with a nested release entry containing a public_key field. The PR description appears stale relative to the final implementation.
    Remediation: Update the PR body to replace references to release_public_key with signing_identities.

Low

  • [error-handling-gap] base_image_registries.rego:30 — The failure_msg "Base image %q is from a disallowed registry" is misleading when signature verification is the active denial reason. Consider a mechanism-agnostic message (e.g., "Base image %q is not permitted") or a separate deny rule surfacing info.errors from sigstore verification.
  • [design-direction] base_image_registries.rego — The else if chain evaluates signature verification last, but the METADATA description calls it "(preferred)". The word refers to the recommended configuration approach, not evaluation order — but this could be clearer in the docs.
  • [error-handling-idiom] base_image_registries.rego:199 — The deprecation warning uses a hardcoded literal string instead of sprintf with _rule_data_key/_signing_identities_key constants, unlike other error messages in this file.
  • [scope-creep] design/sigstore-verification.md — New top-level design/ directory does not exist on main and is not mentioned in AGENTS.md. Consider documenting this convention or placing the content in an existing location.
  • [pattern-inconsistency] base_image_registries.rego — Validation for signing_identities uses manual is_object type checks instead of the j.validate_schema approach used for allowed_registry_prefixes and across sibling packages.
Previous run (5)

Review

Findings

Medium

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego:184 — When release_public_key is defined, the schema validation for allowed_registry_prefixes is entirely skipped (the not _release_public_key guard). If a user configures both release_public_key AND a malformed allowed_registry_prefixes (e.g., containing integers or duplicates), the malformed prefixes are silently used without any validation error. The first branch of _image_ref_permitted still iterates over allowed_prefixes and calls startswith, so non-string values would silently fail to match rather than producing a clear validation error.
    Remediation: Consider removing the not _release_public_key guard from the schema validation rule, or adding a separate validation rule that always validates allowed_registry_prefixes format when the list is non-empty, regardless of whether release_public_key is set.

Low

  • [authorization-bypass] policy/release/base_image_registries/base_image_registries.rego:174 — When release_public_key is configured, the first branch of _image_ref_permitted still evaluates allowed_registry_prefixes and permits images based on prefix matching alone, without signature verification. Images matching stale or overly-broad prefix entries are permitted without signature checks. This is the intended backward-compatible migration design (the deprecation warning fires as advisory only), but operators who set both release_public_key and allowed_registry_prefixes may not realize prefix matching silently bypasses signature verification.

  • [test-inadequate] policy/release/base_image_registries/base_image_registries_test.rego:429 — The test test_no_release_key_is_noop does not actually test the ‘no release key’ scenario. It does not override data.rule_data, so the image registry.redhat.io/ubi@sha256:abc passes because it matches the default registry.redhat.io/ prefix, not because the absence of a release key is handled correctly. To genuinely test the no-key scenario, explicitly set with data.rule_data as {"allowed_registry_prefixes": ["registry.redhat.io/"]}.

  • [test-inadequate] example/data/rule_data.yml:36 — Adding release_public_key to the example rule data file changes the test environment for existing tests in the base_image_registries package. Existing tests that don’t mock ec.sigstore.verify_image now implicitly invoke the sigstore built-in for images that fail prefix and snapshot checks. In network-isolated test environments this call fails harmlessly, but the dependency is implicit and undocumented.

  • [input-validation] policy/release/base_image_registries/base_image_registries.rego:213 — The rule data validation for release_public_key checks that the value is a string but does not validate PEM format. A malformed key silently degrades the sigstore verification branch to always-deny without a diagnostic error surfaced through _rule_data_errors. Operators who misconfigure the key would see images denied with ‘disallowed registry’ messages rather than a clear configuration error.

  • [scope-coherence] policy/release/base_image_registries/base_image_registries.rego — The deprecation warning rule ships in the same PR that introduces the feature. Users upgrading to this policy version will immediately get deprecation warnings for their existing, previously-valid allowed_registry_prefixes configuration. Since the warning is advisory only (severity ‘warning’, not ‘failure’), this is a minor concern — but consider gating it behind a future effective_on date so users can adopt the new feature at their own pace.

  • [error-handling-idiom] policy/release/base_image_registries/base_image_registries.rego:199 — The deprecation warning message uses a freeform sentence, while other rule data validation messages in this codebase use the "Rule data %s has unexpected format: ..." pattern with the key name. This is a minor style inconsistency — the deprecation warning is semantically different from a schema validation error, so a different format is defensible.


Labels: PR adds sigstore signature verification feature with documentation updates

Previous run (6)

Review

Findings

Medium

  • [logic error - silent validation bypass] policy/release/base_image_registries/base_image_registries.rego:186 — When release_public_key is configured, the not _release_public_key guard on the schema validation rule for allowed_registry_prefixes causes all validation of the prefix list to be skipped entirely. If both release_public_key and a malformed allowed_registry_prefixes (e.g., [42, true, null]) are configured, no validation error is reported, silently hiding the misconfiguration. The intent is to make prefixes optional when a release key is present, but the guard also suppresses type/uniqueness validation when prefixes ARE provided alongside the key.
    Remediation: Split the rule — enforce minItems: 1 only when not _release_public_key, but always validate type/uniqueness constraints when the prefix list is non-empty.

  • [rule data validation pattern] policy/release/base_image_registries/base_image_registries.rego:213 — The release_public_key validation rule uses manual not is_string(val) type checking and a hand-crafted sprintf error message. Every other _rule_data_errors rule in the codebase (across attestation_type, cve, labels, schedule, trusted_task, etc.) uses j.validate_schema() with a JSON Schema definition, producing consistently formatted messages via the shared _severity helper. The manual approach also necessitates the non-obvious val != [] guard to handle rule_data.get()'s empty-list default for absent keys.
    Remediation: Use j.validate_schema(rule_data.get(_release_key_rule_data_key), {"$schema": "http://json-schema.org/draft-07/schema#", "type": "string", "minLength": 1}) to match established patterns.

Low

  • [constant naming convention] policy/release/base_image_registries/base_image_registries.rego:226 — The constant _release_key_rule_data_key drops "public" from the key name release_public_key it references. Consider _release_public_key_rule_data_key for clarity and consistency with the actual key name.

  • [test adequacy] policy/release/base_image_registries/base_image_registries_test.rego — No test covers the case where ec.sigstore.verify_image errors at runtime (network failure, malformed key) as opposed to returning {"success": false}. A test confirming fail-closed behavior on built-in errors would guard against regressions in this security-critical verification path.

Previous run (7)

Review

Findings

Medium

  • [test integrity] example/data/rule_data.yml:35 — Adding release_public_key to the shared test data file causes all existing tests that do not explicitly mock data.rule_data to have _release_public_key defined. Existing tests like test_disallowed_base_images will now reach the sigstore verification branch and call ec.sigstore.verify_image with a k8s:// secret reference in a no-network test environment (unshare -r -n). While the built-in should return failure (preserving test correctness), this introduces an implicit dependency on graceful failure behavior.
    Remediation: Either remove release_public_key from example/data/rule_data.yml and set it only via with data.rule_data in tests that need it, or mock ec.sigstore.verify_image in existing disallowed-image tests.

  • [architectural coherence] policy/release/base_image_registries/base_image_registries.rego:108 — The sigstore verification call constructs its own options object ({"public_key": _release_public_key, "ignore_rekor": false}) rather than using sigstore.opts from policy/lib/sigstore/sigstore.rego. Every other sigstore call in the codebase (source_image.rego, trust.rego) uses sigstore.opts, which centralizes defaults for certificate identity, OIDC issuer, and Rekor URL. Hardcoding options here creates a second source of truth and means deployments that configure data.config.default_sigstore_opts (e.g., custom rekor_url) will have that configuration silently ignored by this call.
    Remediation: Consider extending sigstore.opts or creating a variant that merges the release public key into the shared opts object.

Low

  • [test integrity] policy/release/base_image_registries/base_image_registries_test.rego:411test_no_release_key_is_noop may not exercise its intended code path if release_public_key is present in the shared test data. The image registry.redhat.io/ubi would match default allowed_registry_prefixes regardless.

  • [edge-case] policy/release/base_image_registries/base_image_registries.rego:113 — An empty string release_public_key passes type validation (it is a string) but is rejected by _release_public_key (key != ""), silently falling back to prefix-only mode with no user feedback.

  • [fail-open] policy/release/base_image_registries/base_image_registries.rego:174 — When release_public_key is configured, the not _release_public_key guard skips all allowed_registry_prefixes schema validation. Malformed prefixes won't match (fail-closed for admission), but data-quality errors are masked.

  • [input validation] policy/release/base_image_registries/base_image_registries.rego:110_release_public_key validates the value is a non-empty string but not its format (PEM block or k8s:// URI). A misconfigured value produces cryptic ec.sigstore.verify_image errors rather than a clear rule data validation message.

  • [scope-creep] design/sigstore-verification.md — Introduces a new design/ directory convention not present in the repository. The content is valuable but the convention is worth acknowledging.

  • [comment-conventions] policy/release/base_image_registries/base_image_registries.rego:182 — The diff removes the descriptive # Verify ... comment that preceded the _rule_data_errors rule. Every other sibling package follows this convention. The two new _rule_data_errors rules also lack comments.

  • [naming-convention] policy/release/base_image_registries/base_image_registries.rego:76 — The METADATA title "Allowed base image registry prefixes list or release public key was provided" is unusually long (77 chars) compared to other titles in the file (20–23 chars). Consider shortening to something like "Base image verification data provided."

Previous run (8)

Review — approve

Summary

This PR adds signature-based base image verification to the base_image_registries policy package using ec.sigstore.verify_image() with a new release_public_key rule data key. It also introduces a deprecation warning for the allowed_registry_prefixes approach, updates rule data validation so that either mechanism is sufficient, and includes 8 new tests covering the feature end-to-end. Documentation and example data are updated consistently.

Analysis

Correctness — The signature verification logic is structurally sound. The three-branch _image_ref_permitted rule correctly tries prefix matching first, then snapshot digest matching, and falls back to signature verification only when the simpler checks fail. This ordering is efficient and avoids unnecessary network calls. The _has_sig_errors helper correctly checks some _ in info.errors, which evaluates to false on an empty errors array (success) and true when errors are present. The guard not _release_public_key_provided on the first _rule_data_errors correctly allows deployments with only release_public_key configured (no allowed_registry_prefixes). The fail-closed behavior is correct: if ec.sigstore.verify_image returns errors or the built-in itself fails, the entire rule body fails and the image is denied.

Security — The ignore_rekor: false default enforces Rekor transparency log verification, which is the correct security posture for release signing. The public key is retrieved from rule data (which supports k8s:// secret references), keeping key material out of policy source. The _release_public_key_provided helper validates the key is a non-empty string before use. No secrets or credentials are exposed in the diff.

Test adequacy — Eight new tests comprehensively cover: successful signature verification, failed verification, the noop behavior when no key is configured, using only a release key without prefixes, the deprecation warning (presence, absence with key, absence without prefixes), and release_public_key format validation. The mocking approach (with ec.sigstore.verify_image as {"errors": [...]}) is consistent with the existing source_image_test.rego patterns.

Documentation — Antora docs, release policy pages, and nav partials are all updated. The new deprecation rule is documented with its warning message, code, and source link. The example rule_data.yml includes a well-documented release_public_key entry with usage comments.

Observations

ignore_rekor is hardcoded to false (low) — The sigstore options passed to ec.sigstore.verify_image hardcode "ignore_rekor": false. This enforces Rekor transparency log verification, which is a secure default. However, it makes the feature unusable in air-gapped environments where Rekor is not reachable. Consider adding an optional release_signature_ignore_rekor rule data key in a follow-up to provide a configurable escape hatch, similar to how sigstore.opts exposes this setting globally.
File: policy/release/base_image_registries/base_image_registries.rego (line ~139 in the new code)

allowed_registry_prefixes validation is silently skipped when release_public_key is present (low) — The not _release_public_key_provided guard on the first _rule_data_errors rule means that if both release_public_key and a malformed allowed_registry_prefixes are configured, the malformed prefix data is silently accepted without validation errors. This is by design (the prefixes are deprecated), and the malformed entries would harmlessly fail the startswith check without causing safety issues. However, operators may prefer to know about configuration errors even if the deprecated field is being phased out.
File: policy/release/base_image_registries/base_image_registries.rego (line ~217 in the new code)


Labels: PR adds new OPA/Rego policy feature for signature-based base image verification

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge feature labels Jul 16, 2026
@joejstuart
joejstuart marked this pull request as draft July 16, 2026 02:33
Move the registry prefix deprecation from a standalone warn rule into
_rule_data_errors with warning severity, and rename the deny rule title
to "Base image is permitted" since verification is no longer
registry-specific.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 2:58 AM UTC · Ended 2:59 AM UTC
Commit: 87c4a29 · View workflow run →

@joejstuart
joejstuart marked this pull request as ready for review July 16, 2026 02:59
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Review · Started 3:00 AM UTC
Commit: 87c4a29 · View workflow run →

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
unit-tests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
policy/lib/sigstore/sigstore.rego 100.00% <100.00%> (ø)
policy/lib/sigstore/sigstore_test.rego 100.00% <100.00%> (ø)
...e/base_image_registries/base_image_registries.rego 100.00% <100.00%> (ø)
...e_image_registries/base_image_registries_test.rego 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@joejstuart
joejstuart marked this pull request as draft July 16, 2026 12:33
…tries

Replace _has_sig_errors helper with direct info.success check via
object.get, matching the pattern used in trust.rego. Consolidate
_release_public_key_provided into _release_public_key to eliminate
duplicate rule_data.get calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 7:37 PM UTC · Ended 7:41 PM UTC
Commit: 87c4a29 · View workflow run →

@github-actions github-actions Bot added size: XL and removed size: L labels Jul 27, 2026
@joejstuart
joejstuart marked this pull request as ready for review July 27, 2026 19:41
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 7:42 PM UTC · Ended 7:48 PM UTC
Commit: 87c4a29 · View workflow run →

@qodo-for-conforma

Copy link
Copy Markdown

PR Summary by Qodo

Verify base image release signatures via Sigstore (release_public_key)

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Permit base images via Sigstore signature verification using rule data release_public_key.
• Deprecate registry-prefix allowlisting with a warning when used without a release key.
• Add schema validation and tests for new verification and rule-data behaviors.
Diagram

graph TD
  SBOM[("SBOM inputs")] --> POL["base_image_registries.rego"] --> DEC{"Base image permitted?"} --> DENY["deny results"]
  RD[("Rule data")] --> POL
  POL --> SIG{{"ec.sigstore.verify_image"}} --> DEC
  subgraph Legend
    direction LR
    _data[("Data source")] ~~~ _policy["Policy module"] ~~~ _ext{{"External builtin"}} ~~~ _dec{"Decision"} ~~~ _out["Output"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Make signature verification mandatory (remove prefix path now)
  • ➕ Simplifies policy and removes weaker allowlisting path immediately
  • ➕ Reduces ambiguity about the expected security posture
  • ➖ Breaking change for existing users relying on allowed_registry_prefixes
  • ➖ Forces key distribution/management before migration is feasible
2. Support keyless Sigstore verification (Fulcio identity + Rekor)
  • ➕ Avoids managing a long-lived release public key in rule data
  • ➕ Stronger provenance semantics when identity constraints are available
  • ➖ More complex policy inputs/UX (identity constraints, trust roots)
  • ➖ May not fit environments without keyless infrastructure/requirements
3. Make Rekor usage configurable (ignore_rekor toggle)
  • ➕ Better compatibility for air-gapped environments
  • ➕ Allows incremental rollout if Rekor connectivity is intermittent
  • ➖ More configuration surface area and testing permutations
  • ➖ Risk of users disabling transparency guarantees unintentionally

Recommendation: The PR’s approach (add signature-based verification gated by release_public_key and deprecate prefix allowlisting via warnings) is the best migration strategy because it improves security without breaking existing consumers. Consider a follow-up to make Rekor behavior configurable if air-gapped deployments are a target; otherwise keeping ignore_rekor: false is a sensible secure default.

Files changed (7) +258 / -21

Enhancement (1) +44 / -12
base_image_registries.regoPermit base images via Sigstore signature verification and add rule-data warnings/validation +44/-12

Permit base images via Sigstore signature verification and add rule-data warnings/validation

• Extends '_image_ref_permitted' with a signature verification path using 'ec.sigstore.verify_image()' and 'release_public_key'. Updates rule metadata and relaxes the 'allowed registries provided' rule so 'release_public_key' alone is sufficient; adds a warning when prefixes are configured without a release key and validates 'release_public_key' type.

policy/release/base_image_registries/base_image_registries.rego

Tests (1) +128 / -0
base_image_registries_test.regoAdd coverage for signature verification, deprecation warning, and release_public_key validation +128/-0

Add coverage for signature verification, deprecation warning, and release_public_key validation

• Adds tests for success/failure of signature verification, behavior when only a release key is provided, and deprecation warnings when prefixes are used without a key. Adds negative validation coverage for non-string 'release_public_key'.

policy/release/base_image_registries/base_image_registries_test.rego

Documentation (4) +79 / -9
release_base_image_registries.adocDocument signature-based base image verification and prefix deprecation +15/-3

Document signature-based base image verification and prefix deprecation

• Updates rule descriptions to reflect three base-image permission paths (signature, snapshot digest, registry prefix). Adds a new warning rule section describing the deprecation of 'allowed_registry_prefixes' without 'release_public_key'.

antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc

release_policy.adocWire new base-image rule titles and add deprecation warning to policy index +8/-5

Wire new base-image rule titles and add deprecation warning to policy index

• Renames the referenced base-image rule to include release-key support and adds the new registry-prefix deprecation warning to the rule list. Also fixes an xref ordering/labeling issue in the GitHub certificate section.

antora/docs/modules/ROOT/pages/release_policy.adoc

release_policy_nav.adocAdd navigation entry for registry-prefix deprecation warning +2/-1

Add navigation entry for registry-prefix deprecation warning

• Updates nav labels for the allowed-registries rule and includes a new nav item for the deprecation warning rule.

antora/docs/modules/ROOT/partials/release_policy_nav.adoc

sigstore-verification.mdAdd design note for Sigstore builtin typing pitfalls and safe access patterns +54/-0

Add design note for Sigstore builtin typing pitfalls and safe access patterns

• Introduces documentation explaining the return types of 'ec.sigstore.verify_image/verify_attestation' and why 'object.get()' is used to avoid OPA type-checking limitations. Captures recommended patterns used across the policy repo.

design/sigstore-verification.md

Other (1) +7 / -0
rule_data.ymlAdd example configuration for release_public_key +7/-0

Add example configuration for release_public_key

• Adds a commented example of 'release_public_key' showing both inline PEM and k8s:// secret reference usage, and clarifies that signature verification permits images regardless of registry prefixes.

example/data/rule_data.yml

Captures OPA type system constraints discovered through trial and error
when working with ec.sigstore.verify_image. The built-in's return type
is declared in ec-cli (internal/rego/sigstore/sigstore.go) and is not
visible from ec-policies — understanding it required reading the Go
source in the ec-cli repo. Key finding: direct field access and count()
both fail on the typed return; object.get() is the workaround.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qodo-for-conforma

qodo-for-conforma Bot commented Jul 27, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 15 rules

Grey Divider


Action required

1. Prefixes unchecked when key set ✓ Resolved 🐞 Bug ☼ Reliability
Description
When release_public_key is configured, schema validation for allowed_registry_prefixes is
skipped, but the policy still evaluates the prefix-matching path first and includes unguarded
operations like count(prefixes) and startswith(...), which assume correct types. As a result,
malformed rule data (e.g., allowed_registry_prefixes: 42) can trigger runtime/type errors instead
of producing structured validation output even when signature verification is enabled.
Code

policy/release/base_image_registries/base_image_registries.rego[R182-186]

_rule_data_errors contains error if {
+	not _release_public_key
	some e in j.validate_schema(
		rule_data.get(_rule_data_key),
		{
Relevance

●● Moderate

Team often accepts added rule_data validation (PRs 1574, 1740), but has rejected extra
type-safety/edge-case guards before (PR 1728).

PR-#1574
PR-#1740
PR-#1728

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The change described adds a guard that disables allowed_registry_prefixes schema validation when
_release_public_key is defined, yet _image_ref_permitted still iterates over
allowed_registry_prefixes and applies startswith(image_ref, allowed_prefix), which assumes an
array of string prefixes and can fail or behave unexpectedly with malformed data. Additionally, the
deprecation-warning/validation logic retrieves the raw value via rule_data.get(...) and calls
count(prefixes) > 0 without checking that prefixes is an array, which can cause a Rego
evaluation/type error on non-array inputs; this is contrasted with other policies (e.g., maven
repos) that explicitly guard count() with is_array(...) to avoid runtime failures on malformed
configuration.

policy/release/base_image_registries/base_image_registries.rego[98-111]
policy/release/base_image_registries/base_image_registries.rego[182-198]
policy/lib/rule_data/rule_data.rego[176-202]
policy/release/base_image_registries/base_image_registries.rego[200-208]
policy/release/maven_repos/maven_repos.rego[79-90]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When `_release_public_key` is set, `allowed_registry_prefixes` is no longer schema-validated, but the policy still evaluates logic that assumes `allowed_registry_prefixes` is an array of strings (including `count(prefixes)` and iterating prefixes with `startswith(...)`). This creates an execution path where malformed rule data (e.g., scalar/object instead of array) can raise runtime/type errors rather than returning a structured validation result, even during migration to signature-based verification.

## Issue Context
- `_image_ref_permitted` uses `some allowed_prefix in allowed_prefixes` and `startswith(image_ref, allowed_prefix)`, which assumes `allowed_prefixes` is an array and each `allowed_prefix` is a string.
- Validation for `allowed_registry_prefixes` is currently gated behind `not _release_public_key`, so legacy prefix configuration may go unchecked when signature verification is enabled.
- The deprecation-warning validation calls `count(prefixes)` on `prefixes := rule_data.get(...)` without a type guard; other repo policies (e.g., maven repos) guard `count()` with `is_array(...)` to avoid runtime failures on malformed configuration.

## Fix Focus Areas
- policy/release/base_image_registries/base_image_registries.rego[98-117]
- policy/release/base_image_registries/base_image_registries.rego[182-198]
- policy/release/base_image_registries/base_image_registries.rego[200-208]
- policy/release/maven_repos/maven_repos.rego[79-90]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Docs warning code mismatch ✓ Resolved 🐞 Bug ≡ Correctness
Description
Documentation claims the deprecation warning emits code
base_image_registries.registry_prefix_deprecated, but the implementation (and tests) emit it under
base_image_registries.allowed_registries_provided, which will mislead users/automation relying on
rule codes.
Code

antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc[R54-57]

+* Rule type: [rule-type-indicator warning]#WARNING#
+* WARNING message: `allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.`
+* Code: `base_image_registries.registry_prefix_deprecated`
+* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/base_image_registries/base_image_registries.rego#L99[Source, window="_blank"]
Relevance

●●● Strong

Team has merged multiple “docs must match policy behavior/codes” fixes (e.g., PRs 1387, 1676).

PR-#1387
PR-#1676
PR-#1443

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The docs explicitly list base_image_registries.registry_prefix_deprecated as the warning code,
while the policy/test suite shows the warning is produced under the allowed_registries_provided
rule code.

antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc[47-57]
policy/release/base_image_registries/base_image_registries.rego[75-97]
policy/release/base_image_registries/base_image_registries_test.rego[442-455]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The docs introduce a new warning section that specifies code `base_image_registries.registry_prefix_deprecated`, but the policy emits the warning via the existing `allowed_registries_provided` rule metadata/code.

### Issue Context
Tests assert the warning code is `base_image_registries.allowed_registries_provided`.

### Fix Focus Areas
- antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc[47-57]
- policy/release/base_image_registries/base_image_registries.rego[75-97]
- policy/release/base_image_registries/base_image_registries_test.rego[442-455]

### Suggested change
Pick one:
1) Update docs to use the actual emitted code (`base_image_registries.allowed_registries_provided`) and correct the source line reference.
2) Preferably, emit this warning from a dedicated deny rule with its own METADATA short_name `registry_prefix_deprecated` (and update tests accordingly) so the docs' code is real and stable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread policy/release/base_image_registries/base_image_registries.rego
Comment thread antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure · Started 7:49 PM UTC · Completed 8:08 PM UTC
Commit: 87c4a29 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 27, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 10:06 PM UTC · Ended 10:07 PM UTC
Commit: 87c4a29 · View workflow run →

Use the shared sigstore.opts config as defaults when building verification
options, with release_public_key overriding via object.union. This ensures
deployments with custom default_sigstore_opts (rekor_url, ignore_rekor, etc.)
have those settings respected by base image verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines +115 to +120
_signing_identity := identity if {
identities := rule_data.get(_signing_identities_key)
is_object(identities)
identity := object.get(identities, _signing_identity_name, {})
is_object(identity)
count(identity) > 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since we're already doing checks on the rule data format, this could be simpler I think:

_signing_identity := rule_data.get(_signing_identities_key)["release"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But we could also consider adding a rule_data rule, e.g.:

_signing_identity := rule_data.get_signing_identity("release")

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:37 PM UTC · Completed 6:57 PM UTC
Commit: 87c4a29 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

}
image.parse(image_ref).digest in allowed_digests
} else if {
opts := object.union(sigstore.opts, _signing_identity)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There was some discussion in slack about whether this object.union makes sense now.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 8:31 PM UTC · Ended 8:40 PM UTC
Commit: 87c4a29 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 8:41 PM UTC · Ended 8:47 PM UTC
Commit: 87c4a29 · View workflow run →

…irectly

Rename the signing_identities key from "release" to "rh-release" and
remove the object.union merge with sigstore.opts. Each signing identity
in rule data is now self-contained with the complete set of verification
options passed directly to ec.sigstore.verify_image. There are no useful
shared defaults since every verification method requires a distinct
combination of fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:48 PM UTC · Completed 9:08 PM UTC
Commit: 87c4a29 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:27 PM UTC · Completed 5:48 PM UTC
Commit: 87c4a29 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 5:53 PM UTC · Ended 6:00 PM UTC
Commit: 87c4a29 · View workflow run →

…gistries

Validate signing identity rule data to enforce valid sigstore
verification method combinations. Adds JSON schema validation for
types and allowed properties, plus semantic rules that enforce:
- must specify public_key or certificate identity fields
- keyless mode requires issuer and rekor_url
- key-based mode requires rekor_url, rekor_public_key, or ignore_rekor

The validation logic lives in the sigstore library (validate function
and opts_schema) so it is reusable by any sigstore consumer. The
base_image_registries policy wraps the errors with rule-data-specific
context.

Also adds rekor_public_key to the default sigstore opts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:01 PM UTC · Completed 6:17 PM UTC
Commit: 87c4a29 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Jul 31, 2026
…tion

- Always validate allowed_registry_prefixes format (type, uniqueness)
  even when signing_identities is configured; only skip minItems
  requirement when a valid signing identity object is present
- Add is_array guard to deprecation warning rule
- Add validation warning when signing_identities is configured but
  does not contain the expected rh-release key
- Fix test_no_release_key_is_noop to override data.rule_data explicitly
  so it exercises the prefix-only code path as intended

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@joejstuart

Copy link
Copy Markdown
Contributor Author

Response to bot review findings

Fixed in 786edf4:

  • [edge-case] missing rh-release key (fullsend) — Added a validation warning when signing_identities is a non-empty object but does not contain the expected rh-release key.
  • [validation-bypass] prefix validation skipped (fullsend + Qodo) — allowed_registry_prefixes format (type, uniqueness) is now always validated. Only the minItems: 1 requirement is relaxed when a valid signing identity object is present.
  • [test-inadequate] test_no_release_key_is_noop (fullsend) — Test now overrides data.rule_data explicitly to exercise the prefix-only code path.
  • [scope-drift-naming-mismatch] PR description (fullsend) — Updated PR description to match the implementation (signing_identities with nested rh-release).
  • [docs warning code mismatch] (Qodo, already resolved) — Confirmed the docs correctly use allowed_registries_provided.

By design / intentional:

  • [asymmetric-validation] keyless rekor — Intentional. Keyless verification fundamentally requires an online Rekor service (the signature is ephemeral and the certificate chain relies on the transparency log for trust). Key-based verification can use an offline rekor_public_key or ignore_rekor because the key itself provides a durable trust anchor.
  • [authorization-bypass] / [precedence-ordering] prefix matching first — Intentional migration design. Prefix matching runs first for backward compatibility. The deprecation warning guides users toward signature-based verification. "Preferred" in METADATA refers to the recommended configuration approach, not evaluation order.

Minor / acknowledged:

  • [naming-convention] removed comment — The _rule_data_errors block now has multiple rules with distinct validation concerns; a single comment wouldn't accurately describe all of them.
  • [error-handling-idiom] error format — The signing_identities validation uses manual type checks because the structure is a nested object, which doesn't fit the j.validate_schema pattern used for flat array/string validation. Sigstore opts validation is delegated to sigstore.validate().

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:28 PM UTC · Completed 9:46 PM UTC
Commit: 87c4a29 · View workflow run →

@fullsend-ai-review fullsend-ai-review 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.

See the review comment for full details.

}
}

_rule_data_errors contains error if {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] backward-incompatible

Existing consumers providing allowed_registry_prefixes without signing_identities will now receive a new warning-severity deny result on every evaluation. Consumers that treat all deny results equally will see a behavioral change. This is an intentional deprecation nudge.

_validate_key_rekor(identity) := [error] if {
_has_value(identity, "public_key")
object.get(identity, "ignore_rekor", false) != true
not _has_value(identity, "rekor_url")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] logic-error

_validate_keyless_rekor only checks for rekor_url in keyless verification, while _validate_key_rekor accepts rekor_url, rekor_public_key, or ignore_rekor. The asymmetry is likely intentional (keyless requires Rekor timestamps for Fulcio certificates) but is undocumented.

@@ -105,8 +106,13 @@ _image_ref_permitted(image_ref) if {
img := image.parse(component.containerImage)
}
image.parse(image_ref).digest in allowed_digests

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] fail-open

The _image_ref_permitted else-if chain evaluates sigstore verification last. Broad allowed_registry_prefixes bypass signature verification even when signing_identities is configured. Intentional for backward compatibility during migration.

info := ec.sigstore.verify_image(image_ref, _signing_identity)
object.get(info, "success", false) == true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] fail-open

When signing_identities exists but lacks the rh-release key, a warning is emitted but the system falls back to prefix-based matching without a hard failure.

_rule_data_errors contains error if {
prefixes := rule_data.get(_rule_data_key)
is_array(prefixes)
count(prefixes) > 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] pattern-inconsistency

The deprecation warning message omits the 'Rule data' prefix used consistently by all other _rule_data_errors entries.

Suggested fix: Prefix the message: 'Rule data allowed_registry_prefixes is configured without signing_identities...'

@@ -170,7 +176,6 @@ _cyclonedx_image_ref(component) := image_ref if {
image_ref := sbom.image_ref_from_purl(purl)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] pattern-inconsistency

Descriptive comment above the first _rule_data_errors rule was removed. This commenting pattern is established across the codebase for validation blocks.

info := ec.sigstore.verify_image(image_ref, _signing_identity)
object.get(info, "success", false) == true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] edge-case

_signing_identity relies on OPA's behavior that string-key indexing of an array is undefined (not an error). Technically correct but non-obvious.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed ready-for-merge All reviewers approved — ready to merge labels Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants