Skip to content

fix(notary): validate claim response formats#405

Merged
jeremi merged 1 commit into
release/1.0from
codex/1.0-352-claim-format-refresh
Jul 17, 2026
Merged

fix(notary): validate claim response formats#405
jeremi merged 1 commit into
release/1.0from
codex/1.0-352-claim-format-refresh

Conversation

@jeremi

@jeremi jeremi commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

  • close claim evaluation formats to canonical Registry Stack claim-result JSON plus optional CCCEV
  • keep SD-JWT exclusively as a credential output profile, not an evaluation response format
  • require canonical stored registry-backed evaluation before direct or OID4VCI credential rendering
  • align subject-access configuration, Registryctl generation, OpenAPI, maintained docs, diagnostics, and tests

Root cause

The pre-1.0 configuration mixed two trust surfaces: formats used to evaluate registry-backed claims and formats used to render credentials. That allowed application/dc+sd-jwt to appear as a claim evaluation format even though issuance needs a canonical stored registry evaluation first.

User and developer impact

  • omitted claim formats keeps the canonical default
  • explicit empty, unknown, CCCEV-only, or SD-JWT evaluation format lists fail with actionable configuration diagnostics
  • subject_access.allowed_formats governs evaluation renderers only
  • credential_profiles continues to govern SD-JWT credential output
  • direct issuance and OID4VCI continue issuing SD-JWT, but only from canonical registry-backed evaluation data
  • source-free credential issuance remains rejected

This is the current-base replacement for stale draft PR #352 and targets the temporary release/1.0 integration branch.

Validation

  • Registryctl project authoring: 88 passed
  • Notary core: 347 passed
  • Notary server all-features: 435 unit passed, 6 ignored; CEL 18; offline 7; compatibility 10; standalone HTTP 109; subject-access guard 7
  • Doctor CLI: 25 passed
  • OpenAPI baseline and Notary CI preflight
  • locked workspace check and strict workspace Clippy
  • formatting and diff checks
  • independent current-base review: no findings
  • one DCO-signed commit on exact parent 6abefa9eb01e2a7df0088ac65a46502d7f67dbda; Cargo.lock unchanged

Merge only after the full remote matrix is green on exact head 500340828a03f1dc41994cc2d88cd9e0088579cf.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi marked this pull request as ready for review July 17, 2026 12:00
@jeremi
jeremi merged commit a72483f into release/1.0 Jul 17, 2026
16 checks passed
@jeremi
jeremi deleted the codex/1.0-352-claim-format-refresh branch July 17, 2026 12:01

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 500340828a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +254 to +256
for format in &claim.formats {
if format != FORMAT_CLAIM_RESULT_JSON && format != FORMAT_CCCEV_JSONLD {
return Err(EvidenceConfigError::UnsupportedClaimFormat {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop selecting SD-JWT as an evaluation format

After this loop rejects application/dc+sd-jwt for every claim, credential-enabled deployments still expose it as an enabled format because runtime/catalog.rs inserts FORMAT_SD_JWT_VC whenever credential profiles exist, and api/request.rs feeds all enabled ids into evaluation content negotiation. A caller sending Accept: application/dc+sd-jwt, application/vnd.registry-notary.claim-result+json;q=0.5 will have negotiation select SD-JWT and then fail with FormatUnsupported, even though the canonical evaluation response is acceptable; the catalog/negotiation path needs to exclude credential-only formats for evaluation responses.

Useful? React with 👍 / 👎.

Comment on lines 859 to +862
let supported_by_claim = allowed_claims
.iter()
.any(|claim| claim.formats.iter().any(|candidate| candidate == format));
let supported_by_profile = allowed_profiles
.iter()
.any(|profile| profile.format == *format);
if !supported_by_claim && !supported_by_profile {
if !supported_by_claim {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require canonical format for subject-access issuance

When subject_access.allowed_operations.issue_credential is true, this validation still accepts an allow-list such as only application/ld+json; profile="cccev" because it only checks that some allowed claim supports the format. The OID4VCI credential path now creates its internal evaluation with FORMAT_CLAIM_RESULT_JSON, and require_subject_access_evaluation_for_operation denies that request if the canonical format is not in subject_access.allowed_formats, so the configuration loads successfully but all subject-access credential issuance is rejected with FormatDenied; validate that the canonical evaluation format is allowed whenever issuance is enabled.

Useful? React with 👍 / 👎.

Comment on lines 1035 to +1038
let supported_by_claim = allowed_claims
.iter()
.any(|claim| claim.formats.iter().any(|candidate| candidate == format));
let supported_by_profile = allowed_profiles
.iter()
.any(|profile| profile.format == *format);
if !supported_by_claim && !supported_by_profile {
if !supported_by_claim {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require canonical format for delegated credential issuance

For delegated relationships that list credential_profiles, this still accepts allowed_formats containing only the CCCEV renderer as long as an allowed claim supports it. Direct delegated credential issuance now requires the stored evaluation to be application/vnd.registry-notary.claim-result+json, so such a configuration loads but cannot produce an issuable delegated evaluation: canonical evaluation is denied by the relationship allow-list, while CCCEV evaluation is later rejected before signing. Require the canonical format whenever delegated credential profiles are configured.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant