[#4253 2/8] feat(pe): PD validators (Validate + ValidateSelectionKeys)#4281
Draft
stevenvegt wants to merge 8 commits into
Draft
[#4253 2/8] feat(pe): PD validators (Validate + ValidateSelectionKeys)#4281stevenvegt wants to merge 8 commits into
stevenvegt wants to merge 8 commits into
Conversation
Assisted-by: AI
Contributor
|
Coverage Impact ⬆️ Merging this pull request will increase total coverage on Modified Files with Diff Coverage (3)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
This was referenced May 27, 2026
Draft
13 tasks
credential_selection keys are validated against the union of field ids across the supplied presentation definitions (one for single-VP, two for two-VP), by name only. All unknown keys are reported, sorted, in a typed UnknownSelectionKeysError so the API layer can build the OAuth invalid_request description without string parsing. Assisted-by: AI
A field id used across descriptors binds one value, so every filter carrying the id must be able to accept that value at once. Filters are modelled as accepted-value sets following the matcher's actual semantics (enum shadows other keywords; const and pattern compare as strings): declared types must agree, and whenever a const or enum pins a finite candidate set, the intersection filtered through every declared pattern must be non-empty. Only pattern-versus-pattern is deferred to request time. Also checks id uniqueness within a constraints object and input descriptor id uniqueness. All conflicts are aggregated, sorted, in a typed PDValidationError. Assisted-by: AI
Single-filter checks following the matcher's actual semantics: a const on a non-string type and an empty enum can never match; a pattern that does not compile, or carries more than one capture group, errors at request time. Constraints the matcher silently ignores (const or pattern shadowed by enum, a non-string type next to enum, a pattern on a non-string type) are reported as ignored_constraint: the filter is weaker than its author declared. Filter parsing now records unsupported JSON Schema keywords (minimum, allOf, ...) instead of dropping them silently; parsing stays lenient so remote presentation definitions keep working, and only Validate reports them. Assisted-by: AI
Structural mistakes that fail every request at runtime now fail at validation instead: a descriptor group not covered by any submission requirement, unknown rules, from/from_nested violations, negative or inverted bounds, and a pick rule demanding credentials from a group no descriptor carries. Nested requirements are walked recursively. Groups without any submission requirements stay untouched: the matcher ignores them in all-required mode. Assisted-by: AI
An array whose elements all fail the filter falls through the type switch, and the pattern check asserted the value to be a string: a crafted or unlucky combination of a pattern filter and a non-matching array claim (e.g. a filter on $.type) crashed the matcher per request. Non-string values are now a plain non-match. Assisted-by: AI
The Validate godoc carries the matcher's filter truth table (enum shadows other keywords, const and pattern are string-only, numeric values cannot be value-constrained) so the rules the validator encodes are stated next to the code that enforces them. The policy deployment page gets a validation section for operators and policy authors: what is checked at startup, an example aggregated error, and authoring guidance (issue filterable claims as strings, declare ids, keep same-id filters compatible). Assisted-by: AI
This was referenced Jul 16, 2026
Unrecognized filter type values (e.g. "integer", valid JSON Schema but not evaluated by the matcher) are now flagged; annotation keywords (description, title, examples, ...) are no longer captured as unsupported, so they cannot fail a valid PD. FieldIDConflict is renamed to ValidationConflict with a Subject field: it names field ids, descriptor ids, groups or submission requirement positions, and a field called FieldID holding four kinds of identifier misleads every consumer. The docs section is promoted to a top-level heading, Error() methods gained doc comments, and tests were added for three-way intersections, dead filters in shared ids, duplicate participation, the stable error string, the production parse path for unsupported keywords, and the array/pattern guard shapes. Assisted-by: AI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Parent PRD
#4253
Item 2 of 8. Independent of item #1 (branches off the feature branch; can proceed in parallel).
Summary
Add two pure validators to
vcr/pe:Validate(Policy 7, load-time PD consistency) andValidateSelectionKeys(Policy 1,credential_selectionkey validation). Both are pure functions with typed errors and aggregate-all reporting. No caller is wired in this PR — the loader (policy/,discovery/) consumesValidatein item #7, and the API handlers consumeValidateSelectionKeysin item #6.Implementation Spec
func Validate(pd PresentationDefinition) error— Policy 7Semantic checks that JSON Schema cannot express. The PD has already passed schema validation (
v2.Validate, which checks structure: required fields, types,additionalProperties, etc.) before this runs;Validateis a second, cross-field semantic pass on the parsedPresentationDefinition.Constraints.Fields, no two fields may share the sameid(PEX requirement; not expressible in the JSON schema).idvalue-set consistency. For every fieldidappearing on two or more field-objects across descriptors, the value it binds to must satisfy every field's filter at once, so the intersection of the fields' allowed-value sets must be non-empty. Each filter is modelled as a value set:const c→{c};enum [...]→ that finite set;type-only / no filter → the universe of that type;pattern→ a regex predicate.Filter.Typefor the id must be equal (mixed types can never agree; pins the type).constorenum: compute the intersection of all consts (as singletons) and all enum sets, then keep only values matching every declaredpattern. Empty result → conflict. This catches differing consts, const-not-in-enum, disjoint enums, const-fails-pattern, and no-enum-value-matches-pattern.type+ one or morepatterns (no const/enum), the intersection is undecidable (regexp2is not a regular language), so it is not checked at load. This degrades gracefully: at request time no credential can satisfy both patterns, the match fails, and theMatchReportfrom added baseline funcs from old repo #1 surfaces the no-eligible-candidate / binding conflict with the path and value.paths are always allowed (same id resolved from different JSONPaths on different credential types).func ValidateSelectionKeys(selection map[string]string, pds ...PresentationDefinition) error— Policy 1Typed errors
Typed errors let item #6 build the OAuth
invalid_requestdescription (and #7 log structured detail) without string-parsing; theError()strings stay human-readable for logs and operators.Deviations from spec (decided with the user during implementation, 2026-07-16)
FieldIDConflict{FieldID}becameValidationConflict{Subject}; thesubject can be a field id, an input descriptor id, a group, or a submission requirement
position.
PDValidationErrorgainedPDID.Kindis a typedConflictKindwith constants,extended with
invalid_pattern,ignored_constraintandsubmission_requirement.const failing its own pattern), patterns that error (non-compiling, more than one capture
group), and constraints the matcher silently ignores (const/pattern shadowed by enum,
pattern on non-string types, unrecognized type values such as "integer", unsupported JSON
Schema keywords such as "minimum").
Filterparsing stays lenient and records unsupportedkeywords for Validate; JSON Schema annotation keywords (description, title, ...) are exempt.
submission requirement, submission requirement sanity (unknown rule, from/from_nested,
bounds, rules demanding credentials from a group no descriptor carries).
matchFilterpanicked on array values with a string+pattern filter when noelement matched (reachable per request); now a plain non-match.
Validategodoc; a "Presentation definitionvalidation" section in docs/pages/deployment/policy.rst (describes behavior that item 7
wires into the loader; both land in the same release).
deliberately NOT addressed here; filed as Align presentation definition filter evaluation with JSON Schema semantics #4413.
Testing
Black-box
validate_test.go, no mocks or fixtures.Validate:PDValidationError.ValidateSelectionKeys: unknown key against a single PD; unknown key against a two-PD union; a key targeting only one side of a two-PD union passes; all-known passes; empty-string value validated by name; multiple unknown keys all listed and sorted.Acceptance Criteria
Validateenforces id-uniqueness-within-constraints and same-idvalue-set non-empty intersection (type/const/enum + concrete-value-vs-pattern); defers only pattern-vs-pattern; ignores path differences.ValidateSelectionKeysvalidates key names against the union of the supplied PDs; ignores values.vcr/pe); existingvcr/petests still green.go build ./...andgo test ./vcr/pe/...pass.