Skip to content

Conversation

@Iamrodos
Copy link
Contributor

@Iamrodos Iamrodos commented Dec 4, 2025

This PR adds a validate_policies(policies, schema) function that validates Cedar policies against a schema, exposing the cedar-policy Validator API to Python users.

Motivation

Currently, validating Cedar policies against a schema requires shelling out to the Cedar CLI (cedar validate). This is inconvenient and adds an external dependency. By exposing the Validator API directly, users can:

  • Validate policies programmatically in Python
  • Integrate validation into CI/CD pipelines before deployment
  • Catch policy errors (typos in entity types, invalid actions, type mismatches, unsafe access to optional attributes) before they cause silent failures at runtime

See the Cedar validation documentation for details on what the validator checks.

Usage

from cedarpy import validate_policies, ValidationResult

policies = '''
    permit(
        principal == User::"alice",
        action == Action::"view",
        resource
    );
'''

schema = '''{ "": { "entityTypes": { "User": {}, ... }, "actions": { ... } } }'''

result = validate_policies(policies, schema)

if result.validation_passed:
    print("Policies are valid!")
else:
    for error in result.errors:
        print(f"Error: {error}")

Design Decisions

The validate_policies function returns a ValidationResult for all error types (including parse errors) rather than raising ValueError. This differs from functions like format_policies which raise on invalid input.

Rationale: The purpose of a validator is to check validity. A validator that throws exceptions for invalid input wouldn't be useful as a validator. Users expect to call validate_policies() and check the result, not wrap it in try/except.

Testing Approach

Three layers of testing ensure correctness:

  1. New unit tests (tests/unit/test_validate.py) - 20 tests covering the API, error handling, schema formats, and edge cases.

  2. Rust CLI parity tests - 8 of the unit tests use the same policy and schema files as the Rust CLI tests in third_party/cedar/cedar-policy-cli/sample-data/. This ensures our validation produces the same results as cedar validate.

  3. Integration test validation - The existing integration tests now call validate_policies when the test definition specifies shouldValidate: true. This adds validation coverage to 22 additional test scenarios from the official Cedar integration test suite.


Implementation assisted by AI tooling; design decisions, testing strategy, and code review performed by the author.

Add validate_policies(policies, schema) function that validates Cedar
policies against a schema, exposing the cedar-policy Validator API.

Changes:
- Add validate_policies function in Rust (src/lib.rs)
- Add ValidationResult and ValidationError Python classes
- Add validate_policies Python wrapper function
- Add 20 unit tests including Rust CLI parity tests
- Add schema validation to integration tests (shouldValidate field)
- Update README with validation documentation

The function returns a ValidationResult with validation_passed boolean
and a list of ValidationError objects. Supports both JSON and Cedar
schema syntax formats.
@Iamrodos
Copy link
Contributor Author

Iamrodos commented Dec 4, 2025

Thanks for reviewing this and the consideration to add. I my CI pipeline I have to do a lot of work to validate my policies and introduce new dependacies.

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable
    - name: Cache Cedar CLI
      uses: actions/cache@v4
      with:
        path: ~/.cargo/bin/cedar
        key: cedar-cli-${{ env.CEDAR_CLI_VERSION }}-${{ runner.os }}
    - name: Install Cedar CLI
      run: |
        if ! command -v cedar &> /dev/null; then
          cargo install cedar-policy-cli --version "${{ env.CEDAR_CLI_VERSION }}"
        else
          echo "Cedar CLI already installed (from cache)"
        fi
    - name: Validate Cedar policies
      run: |
        # Generate schema and validate policies (run from source directory)
        cd source && uv run python -m nc_auth_entities > /tmp/emu-schema.cedarschema
        cedar validate --schema /tmp/emu-schema.cedarschema --policies policies.cedar

If we get validate_policies supported in cedarpy this can be simplified a lot. It can all be done with python.

@skuenzli
Copy link
Contributor

skuenzli commented Dec 4, 2025

Thank you for the PR @Iamrodos! I am at re:Invent now. I skimmed the code and the approach looks good. I will review it closely soon.

@Iamrodos
Copy link
Contributor Author

Iamrodos commented Dec 4, 2025

@skuenzli Must say I am glad to not go to re:Invent anymore. I think I went for 9 or 10 years in a row. I think my first year was 2012. Have fun!

/// List of validation errors
errors: Vec<ValidationErrorSer>,
}

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

self.assertTrue(validation_result.validation_passed,
f"Expected policies to validate but got errors: "
f"{[str(e) for e in validation_result.errors]}")

Copy link
Contributor

Choose a reason for hiding this comment

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

👍


serde_json::to_string(&result).unwrap()
}

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

self.assertTrue(
any("BadType" in msg for msg in error_messages),
f"Expected error about 'BadType', got: {error_messages}"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

👍



if __name__ == '__main__':
unittest.main()
Copy link
Contributor

Choose a reason for hiding this comment

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

🙌

@skuenzli
Copy link
Contributor

skuenzli commented Dec 5, 2025

This looks great! I love the extensive test cases.

Thank you @Iamrodos

@skuenzli skuenzli merged commit 3d97be3 into k9securityio:main Dec 5, 2025
7 checks passed
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.

2 participants