Skip to content

schema validation#2403

Open
victornicolet wants to merge 3 commits into
mainfrom
victornicolet/schema-try-validate
Open

schema validation#2403
victornicolet wants to merge 3 commits into
mainfrom
victornicolet/schema-try-validate

Conversation

@victornicolet

Copy link
Copy Markdown
Contributor

Description of changes

Implements the try_validate function for ValidatorSchemas to enforce that Schema::decode validates just as much as schema parsing or deserialization from JSON.

Issue #, if available

Checklist for requesting a review

The change in this PR is (choose one, and delete the other options):

  • A change (breaking or otherwise) that only impacts unreleased or experimental code.

I confirm that this PR (choose one, and delete the other options):

  • Does not update the CHANGELOG because my change does not significantly impact released code.

I confirm that cedar-spec (choose one, and delete the other options):

  • Does not require updates because my change does not impact the Cedar formal model or DRT infrastructure.
  • Requires updates, and I have made / will make these updates myself. (Please include in your description a timeline or link to the relevant PR in cedar-spec, and how you have tested that your updates are correct.)
  • Requires updates, but I do not plan to make them in the near future. (Make sure that your changes are hidden behind a feature flag to mark them as experimental.)
  • I'm not sure how my change impacts cedar-spec. (Post your PR anyways, and we'll discuss in the comments.)

I confirm that docs.cedarpolicy.com (choose one, and delete the other options):

  • Does not require updates because my change does not impact the Cedar language specification.

@github-actions

Copy link
Copy Markdown

Coverage Report

Head Commit: 934d593bc1bfe6de675a8d171bf34f6fd6ce246d

Base Commit: 7754f634b9ed3bbf45f44e1551aed9a4bcf3770e

Download the full coverage report.

Coverage of Added or Modified Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 96.18%

Status: PASSED ✅

Details
File Status Covered Coverage Missed Lines
cedar-policy-core/src/validator/schema.rs 🟢 113/113 100.00%
cedar-policy-core/src/validator/schema/err.rs 🟡 12/17 70.59% 939-943
cedar-policy-core/src/validator/schema/namespace_def.rs 🟢 1/1 100.00%

Coverage of All Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 87.90%

Status: PASSED ✅

Details
Package Status Covered Coverage Base Coverage
cedar-language-server 🟢 4722/5102 92.55% 92.55%
cedar-policy 🟡 4476/5600 79.93% 79.93%
cedar-policy-cli 🟡 1251/1643 76.14% 76.14%
cedar-policy-core 🟢 24696/28000 88.20% 88.14%
cedar-policy-formatter 🟢 914/1088 84.01% 84.01%
cedar-policy-symcc 🟢 6777/7274 93.17% 93.17%
cedar-wasm 🔴 0/28 0.00% 0.00%

/// which is not allowed.
#[error(transparent)]
#[diagnostic(transparent)]
EnumEntityInHierarchy(#[from] schema_errors::EnumEntityInHierarchyError),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note this is new API. We could have a new error type for what try_validate, but those enums do represent "schema errors" so I think they fit here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR implements ValidatorSchema::try_validate() to enforce well-formedness checks on schemas constructed programmatically (including protobuf Schema::decode), bringing validation behavior in line with schema parsing/JSON deserialization expectations.

Changes:

  • Implemented ValidatorSchema::try_validate() with explicit checks for hierarchy well-formedness, referenced types, extension types, transitive closure recomputation, and RFC 70 shadowing checks.
  • Improved unknown-extension-type error construction by centralizing fuzzy-suggestion generation on UnknownExtensionTypeError.
  • Updated/expanded protobuf schema decoding tests to assert that invalid schemas are rejected (instead of previously succeeding due to a no-op try_validate).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
cedar-policy/src/proto/validator.rs Updates protobuf-schema decode tests to require rejection of invalid schemas (now that validation is implemented).
cedar-policy-core/src/validator/schema/namespace_def.rs Refactors unknown-extension-type error construction to use a shared helper for suggested replacements.
cedar-policy-core/src/validator/schema/err.rs Adds new schema error variants + new error types; introduces UnknownExtensionTypeError::new_with_suggestion.
cedar-policy-core/src/validator/schema.rs Implements try_validate() and supporting well-formedness checks (hierarchy, references, extensions, TC recompute, RFC 70).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cedar-policy/src/proto/validator.rs Outdated
Comment thread cedar-policy/src/proto/validator.rs Outdated
Comment on lines +1172 to +1178
for descendant in action.descendants() {
if !self.action_ids.contains_key(descendant) {
return Err(ActionInvariantViolationError {
euids: NonEmpty::singleton(descendant.clone()),
}
.into());
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will add a new enum variant, we shouldn't suggest filing an issue when protobuf decode fails.

/// Iterate over all leaf types reachable from entity attributes, tags, and action contexts,
/// calling `visit` on each leaf type. Recursion through sets and records is handled
/// automatically.
fn for_each_type(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It doesn't matter too much, but I like the pattern used by ExprIterator to return an iterator instead of passing in a closure

@github-actions

Copy link
Copy Markdown

Coverage Report

Head Commit: 617fd74b7f416bece19d59093e74599415911709

Base Commit: 7754f634b9ed3bbf45f44e1551aed9a4bcf3770e

Download the full coverage report.

Coverage of Added or Modified Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 92.14%

Status: PASSED ✅

Details
File Status Covered Coverage Missed Lines
cedar-policy-core/src/validator/schema.rs 🟢 98/98 100.00%
cedar-policy-core/src/validator/schema/err.rs 🔴 19/30 63.33% 457, 466-470, 979-983
cedar-policy-core/src/validator/schema/namespace_def.rs 🟢 1/1 100.00%
cedar-policy-core/src/validator/types.rs 🟢 11/11 100.00%

Coverage of All Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 87.89%

Status: PASSED ✅

Details
Package Status Covered Coverage Base Coverage
cedar-language-server 🟢 4722/5102 92.55% 92.55%
cedar-policy 🟡 4476/5600 79.93% 79.93%
cedar-policy-cli 🟡 1251/1643 76.14% 76.14%
cedar-policy-core 🟢 24699/28009 88.18% 88.14%
cedar-policy-formatter 🟢 914/1088 84.01% 84.01%
cedar-policy-symcc 🟢 6777/7274 93.17% 93.17%
cedar-wasm 🔴 0/28 0.00% 0.00%

/// of an action.
#[error(transparent)]
#[diagnostic(transparent)]
UndeclaredActions(#[from] schema_errors::UndeclaredActionsError),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How were these issues handled before in schema validation if we don't have a schema error for them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is another error type ActionNotDefined, but it's built with types available during the translation of Fragments where names are resolved on the normal parsing path.
Similarly there's already existing TypesNotDefined and UndeclaredEntityTypes variants for errors.
Another solution would be to change the underlying type of ActionNotDefined to support different ways of constructing it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A moral justification is that there is a difference between the two: ActionNotDefined occurs when definitions are being resolved while building the schema, while UndeclaredActions occurs when some action is used in an already built schema, but it's not declared (by this schema).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The types should all be opaque to the public API, so supporting both kinds in ActionNotDefined sound reasonable

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a DRT opportunity between the two validations: validating an already build schema vs building the same schema from fragments

impl_diagnostic_from_method_on_nonempty_field!(types, loc);
}

/// Undeclared actions error: an action's descendants list references

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor typo? "list references actions" -> "references actions"

Signed-off-by: Victor Nicolet <victornl@amazon.com>
Signed-off-by: Victor Nicolet <victornl@amazon.com>
/// Returns an iterator over all leaf types reachable from entity attributes, tags, and
/// action contexts. Leaf types are all types except `Set` and `Record`, which are
/// traversed into automatically.
fn leaf_types(&self) -> TypeIterator<'_> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If a type is referenced from multiple contexts, it will be iterated over it multiple times, right? I doubt it will be an issue since computing TC is probably the real bottleneck, but if performance for validation becomes an issue maybe this is an optimization we could do?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's correct; it's easy enough to add Itertools::unique when calling leaf_types so I'll add that and a doc comment.

@victornicolet victornicolet force-pushed the victornicolet/schema-try-validate branch from 617fd74 to ffa63fe Compare June 12, 2026 20:58
Signed-off-by: Victor Nicolet <victornl@amazon.com>
@victornicolet victornicolet force-pushed the victornicolet/schema-try-validate branch from ffa63fe to 0f6e539 Compare June 12, 2026 21:31
@github-actions

Copy link
Copy Markdown

Coverage Report

Head Commit: 0f6e5395b086cdb13e82ee0bcd5ad5286b5a6912

Base Commit: 7459598abe9e481cee389fb132634d0377452c87

Download the full coverage report.

Coverage of Added or Modified Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 87.14%

Status: PASSED ✅

Details
File Status Covered Coverage Missed Lines
cedar-policy-core/src/validator/schema.rs 🟢 98/98 100.00%
cedar-policy-core/src/validator/schema/err.rs 🔴 12/30 40.00% 453-455, 457, 459-462, 466-470, 979-983
cedar-policy-core/src/validator/schema/namespace_def.rs 🟢 1/1 100.00%
cedar-policy-core/src/validator/types.rs 🟢 11/11 100.00%

Coverage of All Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 87.89%

Status: PASSED ✅

Details
Package Status Covered Coverage Base Coverage
cedar-language-server 🟢 4722/5102 92.55% --
cedar-policy 🟢 4530/5644 80.26% --
cedar-policy-cli 🟡 1257/1649 76.23% --
cedar-policy-core 🟢 24687/28011 88.13% --
cedar-policy-formatter 🟢 914/1088 84.01% --
cedar-policy-symcc 🟢 6777/7274 93.17% --
cedar-wasm 🔴 0/28 0.00% --

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.

4 participants