Skip to content

feat: add Cedar as a second policy engine#72

Merged
boorad merged 4 commits into
mainfrom
feat/cedar-support
May 6, 2026
Merged

feat: add Cedar as a second policy engine#72
boorad merged 4 commits into
mainfrom
feat/cedar-support

Conversation

@boorad
Copy link
Copy Markdown
Contributor

@boorad boorad commented May 6, 2026

Summary

Adds Cedar (the AWS-originated policy language) as a second supported policy engine alongside Rego/OPA. Architecture mirrors the existing Rego pipeline: per-rule cedar_template blocks, category-default stubs, a validate_cedar parser-based validator, and grouping into .cedar files. The CLI exposes engine selection via extract --engine {rego,cedar} and the MCP server gains engine-agnostic suggest_policy / validate_policy tools (the Rego-pinned tools stay as aliases).

Changes

  • src/cedar/{mod,grouping,templates,validator}.rs — Cedar emission and parser-based validation using the cedar-policy crate
  • src/cli.rs--engine {rego,cedar} on extract; --policy-prefix with --package-prefix retained as alias
  • src/commands/extract.rs — Cedar pipeline; canonicalize output_dir once per run instead of per-file
  • src/mcp/tools.rssuggest_policy / validate_policy engine-agnostic tools
  • src/scanner/matcher.rscedar_stub rendering, plus a Cedar-flavored derived-vars helper (cedar_roles_set) so multi-role rules emit a real Cedar set membership check
  • src/types.rsFinding.cedar_stub (serde-default for backward compat); Surface field for frontend/backend classification
  • rules/**/*.tomlcedar_template blocks added across all C#, Java, Python, Go, and TypeScript rules
  • docs/CEDAR_SUPPORT.md — design memo

Test plan

  • cargo build && cargo clippy --all-features -- -D warnings && cargo fmt --check
  • cargo test --all-features (444 tests, including new cedar grouping/templates/validator tests and a regression test that csharp-aspnet-authorize-roles expands Roles = "Admin,Manager" into principal.role in ["Admin", "Manager"])
  • Manual: cargo run -- scan tests/fixtures -f json | cargo run -- extract --engine cedar --output-dir /tmp/cedar-out
  • Manual: cargo run -- mcp and call suggest_policy / validate_policy with engine: "cedar"

Summary by CodeRabbit

  • New Features

    • Cedar policy engine: generate Cedar policies alongside existing Rego output
    • CLI option --engine cedar to select Cedar policy generation
    • Cedar policy validation during rule checks
    • Cedar templates added to 50+ authorization detection rules across C#, Go, Java, Python, and TypeScript
  • Documentation

    • Updated Cedar support design memo and decision log
  • Chores

    • Added cedar-policy v4 dependency

boorad added 3 commits May 6, 2026 09:42
Phase A of #27 — adds Cedar/AVP as a peer to Rego/OPA without breaking
the existing JSON or MCP API.

- `Finding::cedar_stub` parallel to `rego_stub` (serde-defaulted, BC)
- `[rule.<id>.cedar_template]` parallel to `rego_template`; 45 existing
  rules carry an inferred Cedar template, the rest fall through to a
  category-default Cedar stub so coverage stays at 100%
- new `src/cedar/` module: templates, validator (cedar-policy 4.x),
  per-source-file grouping with `.cedar` extension
- `zift extract --engine rego|cedar` (default rego); `--package-prefix`
  renamed to `--policy-prefix` with the old name kept as an alias
- new MCP tools `suggest_policy` / `validate_policy` taking an `engine`
  arg; `suggest_rego` / `validate_rego` retained as Rego-pinned aliases
  so existing agent host wiring keeps working
- `zift rules validate` now type-checks Cedar templates too
Phase B is now tracked as its own work item (#71). Removed the
'wait for real users' gate and the 'JSON-schema break needs a major
version bump' gate. The *_stub → policy_outputs migration ships with
a deserialization shim that reads the legacy fields, so it doesn't
need a coordinated version cut — that shim is part of Phase B's
scope, not a separate migration.
Several rule cedar_template blocks (mostly C#) hardcoded "TODO"
instead of substituting captured values, while their Rego siblings
expanded placeholders correctly. Output for those rules was
strictly worse than the category-default stub. Use the captured
placeholders, and add a cedar-flavored derived-vars helper
(`add_cedar_template_derived_values` producing `cedar_roles_set`)
so multi-role rules like `csharp-aspnet-authorize-roles` can emit
a real Cedar set membership check. Wrap Cedar regexes in OnceLock
and hoist `output_dir.canonicalize()` out of the per-file extract
loop.
@boorad boorad self-assigned this May 6, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

Adds Cedar policy engine support: new dependency, cedar module (templates, grouping, validator), CLI engine flag, cedar_template fields in rule TOMLs, cedar_stub on Finding, engine-dispatched extraction and validation, and updates across scanner, MCP tools, tests, and docs.

Changes

Cedar Policy Engine Integration

Layer / File(s) Summary
Dependency & Data Shape
Cargo.toml, src/types.rs
Adds cedar-policy = "4"; Finding gains cedar_stub: Option<String> with serde default.
Public API Export
src/lib.rs, src/cedar/mod.rs
Exports new cedar module and re-exports group_findings and render_template.
Template Utilities
src/cedar/templates.rs
New template rendering, quoted-literal extraction, default templates/stubs per AuthCategory, confidence wrapping, and unit tests; public APIs: render_template, extract_string_literals, default_template, generate_default_stub, apply_confidence_wrapping.
Validation
src/cedar/validator.rs
Adds ValidationResult, validate_cedar, and validate_template that parse Cedar PolicySet and test templates.
Grouping & Generation
src/cedar/grouping.rs
Adds CedarFile struct and group_findings to group findings by source file, derive labels/output paths, and build Cedar file content; includes tests.
Scanner Integration
src/scanner/matcher.rs
Renders cedar_template into cedar_stub, adds Cedar-derived variables (e.g., cedar_roles_set), and introduces generalized comma-separated quoting helper; tests updated to include cedar_stub.
Rules / TOML Wiring
src/rules/mod.rs, rules/*/*.toml
PatternRule gains cedar_template: Option<String>; TOML deserializes cedar_template via CedarTemplateToml; ~40+ rule TOMLs gain [rule.cedar_template] blocks (many languages), one file replaced rego→cedar.
CLI & Command Dispatch
src/cli.rs, src/commands/extract.rs, src/commands/rules.rs
ExtractArgs adds policy_prefix and engine: PolicyEngine{Rego,Cedar}; extract dispatches to extract_rego or extract_cedar; write helper write_policy_file added with TOCTOU-safe containment; rules validate now validates cedar_template.
MCP Tools & API
src/mcp/tools.rs
Adds engine-aware suggest_policy and validate_policy tools, PolicyEngineArg, includes cedar paths and tests; tool catalog expanded.
Output / Tests Update
src/output/{json,text}.rs, src/deep/*, src/rego/grouping.rs, tests/deep_*.rs
Test and helper initializers updated to include cedar_stub: None; deep and rego test fixtures adjusted; numerous new unit/integration tests for Cedar rendering and validation.
Docs
docs/CEDAR_SUPPORT.md
Documents Phase A/B, PolicyGenerator concept, migration notes (policy_outputs vs *_stub), and decision log entry (2026-05-06).

Sequence Diagram

sequenceDiagram
    participant CLI as CLI
    participant Scanner as Scanner/Matcher
    participant CedarMod as cedar::(templates/validator/grouping)
    participant FileSys as Output Files

    CLI->>Scanner: run scan with --engine=cedar
    Scanner->>Scanner: match patterns, capture vars
    Scanner->>CedarMod: render `cedar_template` → `cedar_stub`
    CedarMod->>CedarMod: validate_template(rendered)
    Scanner->>CedarMod: pass findings with cedar_stub
    CedarMod->>CedarMod: group_findings → per-file CedarFile(s)
    CedarMod->>FileSys: write_policy_file(per-file content)
    FileSys->>CLI: return summary (files written, validation warnings)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

🐰
A cedar sprout beside the rego tree I sow,
Two engines hum where single rivers flowed,
The scanner finds, the templates stitch with care,
Stubs become policy files laid out to share,
Hop, hop — a little rabbit cheers: “Now both can grow!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add Cedar as a second policy engine' directly and clearly describes the main change—adding Cedar as a supported policy engine alongside Rego.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cedar-support

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

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

This PR successfully adds Cedar policy engine support alongside Rego, implementing a well-architected parallel pipeline. The implementation is comprehensive with 444 passing tests and includes proper validation, template rendering, and MCP tool integration.

Key Strengths:

  • Clean architecture mirroring the existing Rego implementation
  • Comprehensive test coverage for Cedar templates, validation, and grouping
  • Proper TOCTOU-safe path traversal security checks maintained
  • Backward compatibility preserved with #[serde(default)] for new fields
  • Engine-agnostic MCP tools (suggest_policy, validate_policy)

The code is production-ready and no blocking issues were identified.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.


⚠️ This PR contains more than 30 files. Amazon Q is better at reviewing smaller PRs, and may miss issues in larger changesets.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/cedar/templates.rs (1)

18-21: 💤 Low value

Minor: Regex can match mismatched quotes.

The pattern ["']([^"']+)["'] will match strings with mismatched opening/closing quotes (e.g., "foo' or 'bar"). In practice this is unlikely to occur in real code snippets, but for correctness you could use backreferences or alternation.

🔧 Optional fix using alternation
 fn string_literal_re() -> &'static Regex {
     static RE: OnceLock<Regex> = OnceLock::new();
-    RE.get_or_init(|| Regex::new(r#"["']([^"']+)["']"#).unwrap())
+    RE.get_or_init(|| Regex::new(r#""([^"]+)"|'([^']+)'"#).unwrap())
 }

Note: This would require adjusting extract_string_literals to check both capture groups.

🤖 Prompt for 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.

In `@src/cedar/templates.rs` around lines 18 - 21, The regex in
string_literal_re() allows mismatched quotes (e.g., "foo') because it uses
["']([^"']+)["']; update the pattern to enforce matching open/close quotes (use
a backreference like (?P<q>["'])(?P<s>[^"']+)(?P=q) or alternation like
"([^"]+)"|'([^']+)' in the Regex::new call inside string_literal_re(), and then
update any consumer (e.g., extract_string_literals) to read the correct capture
group (either the named group "s" or the appropriate numbered group when using
alternation) so string values are extracted reliably with matching quotes.
🤖 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 `@rules/java/authorized-annotation.toml`:
- Around line 43-53: The Cedar template currently hardcodes principal.role ==
"TODO" which ignores the captured `@role_value`; update the [rule.cedar_template]
so the principal.role comparison uses the captured authorization value (the
`@Authorized`(...) capture, e.g. role_value) instead of the literal "TODO",
ensuring the template injects/quotes the role_value correctly when rendering;
look for the template string containing principal.role and replace the literal
with the template variable that maps to the captured `@role_value`.

In `@rules/java/has-role-call.toml`:
- Around line 39-49: The Cedar template in the [rule.cedar_template] block
currently hardcodes principal.role == "TODO"; change this to use the captured
role variable instead of the literal "TODO" by substituting the capture (the
`@role_value` captured by the rule) into the template so the condition reads
principal.role == <captured role value>, keeping the Cedar output consistent
with the Rego capture and the rule's `@role_value` identifier.

In `@rules/java/shiro-requires-roles-array.toml`:
- Around line 29-39: The Cedar template in [rule.cedar_template] uses a singular
capture `{{role_value}}` causing multi-role arrays to collapse; change the
capture/templating flow so all captured role_value entries are aggregated into a
single roles set (e.g., derive a `cedar_roles_set` from all role_value captures)
and render the condition using set membership (either
`{{cedar_roles_set}}.contains(principal.role)` or `principal.role in
[{{cedar_roles_set}}]`) instead of `principal.role == "{{role_value}}"`; follow
the approach used in the `csharp-aspnet-authorize-roles` rule where a
comma-separated roles string is turned into `cedar_roles_set` before template
rendering, or modify the capture step to produce a `roles` variable so the
template can directly use `cedar_roles_set`.

In `@rules/java/spring-roles-allowed-array.toml`:
- Around line 29-39: The Cedar template under rule.cedar_template currently uses
singular equality (principal.role == "{{role_value}}"); update the template in
the template string so the condition matches the multi-role derived value used
by the Rego sibling by replacing the equality check with a membership check
using the derived set placeholder (e.g., principal.role in
[{{cedar_roles_set}}]); ensure you reference the existing template symbol and
the cedar_roles_set placeholder when making the change so arrays of roles are
handled correctly.

In `@rules/python/has-perm-call.toml`:
- Around line 36-46: The Cedar template in the rule.cedar_template section is
using a hardcoded principal.role == "TODO" instead of the captured permission
variable; update the template to interpolate the extracted perm_name (the
captured permission) into the condition so generated stubs reflect the matched
permission (use the perm_name placeholder from your rule extraction logic inside
the template string where "TODO" currently appears).

---

Nitpick comments:
In `@src/cedar/templates.rs`:
- Around line 18-21: The regex in string_literal_re() allows mismatched quotes
(e.g., "foo') because it uses ["']([^"']+)["']; update the pattern to enforce
matching open/close quotes (use a backreference like
(?P<q>["'])(?P<s>[^"']+)(?P=q) or alternation like "([^"]+)"|'([^']+)' in the
Regex::new call inside string_literal_re(), and then update any consumer (e.g.,
extract_string_literals) to read the correct capture group (either the named
group "s" or the appropriate numbered group when using alternation) so string
values are extracted reliably with matching quotes.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 96beec8a-68ef-4eaf-8a3f-1c3928350014

📥 Commits

Reviewing files that changed from the base of the PR and between 64d80e6 and edf2d74.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (69)
  • Cargo.toml
  • docs/CEDAR_SUPPORT.md
  • rules/csharp/aspnet-authorize-attribute.toml
  • rules/csharp/aspnet-authorize-policy-shorthand.toml
  • rules/csharp/aspnet-authorize-policy.toml
  • rules/csharp/aspnet-authorize-roles.toml
  • rules/csharp/aspnet-require-authorization.toml
  • rules/csharp/authorization-service-authorize-async.toml
  • rules/csharp/has-claim-call.toml
  • rules/csharp/is-in-role-call.toml
  • rules/go/access-descriptor-builder.toml
  • rules/go/casbin-enforce.toml
  • rules/go/feature-gate-check.toml
  • rules/go/gin-auth-middleware.toml
  • rules/go/has-role-call.toml
  • rules/go/opa-rego-eval.toml
  • rules/go/ownership-check.toml
  • rules/go/permission-check-call.toml
  • rules/go/role-check-conditional.toml
  • rules/java/authorized-annotation.toml
  • rules/java/has-role-call.toml
  • rules/java/is-user-in-role.toml
  • rules/java/role-equals-check.toml
  • rules/java/shiro-is-permitted.toml
  • rules/java/shiro-requires-permissions.toml
  • rules/java/shiro-requires-roles-array.toml
  • rules/java/shiro-requires-roles.toml
  • rules/java/spring-preauthorize.toml
  • rules/java/spring-roles-allowed-array.toml
  • rules/java/spring-roles-allowed.toml
  • rules/java/spring-secured.toml
  • rules/python/django-permission-required.toml
  • rules/python/has-perm-call.toml
  • rules/python/has-role-call.toml
  • rules/python/ownership-check.toml
  • rules/python/permission-check-call.toml
  • rules/python/role-check-conditional.toml
  • rules/typescript/authorize-function-call.toml
  • rules/typescript/chained-permission-call.toml
  • rules/typescript/feature-gate-check.toml
  • rules/typescript/has-role-call.toml
  • rules/typescript/membership-check-call.toml
  • rules/typescript/nestjs-roles-decorator.toml
  • rules/typescript/ownership-check.toml
  • rules/typescript/permission-check-call.toml
  • rules/typescript/role-check-conditional.toml
  • rules/typescript/role-includes-check.toml
  • src/cedar/grouping.rs
  • src/cedar/mod.rs
  • src/cedar/templates.rs
  • src/cedar/validator.rs
  • src/cli.rs
  • src/commands/extract.rs
  • src/commands/rules.rs
  • src/deep/candidate.rs
  • src/deep/context.rs
  • src/deep/finding.rs
  • src/deep/merge.rs
  • src/deep/prompt.rs
  • src/lib.rs
  • src/mcp/tools.rs
  • src/output/json.rs
  • src/output/text.rs
  • src/rego/grouping.rs
  • src/rules/mod.rs
  • src/scanner/matcher.rs
  • src/types.rs
  • tests/deep_http_integration.rs
  • tests/deep_subprocess_integration.rs

Comment thread rules/java/authorized-annotation.toml
Comment thread rules/java/has-role-call.toml
Comment thread rules/java/shiro-requires-roles-array.toml
Comment thread rules/java/spring-roles-allowed-array.toml
Comment thread rules/python/has-perm-call.toml
- Replace hardcoded "TODO" with captured values in Cedar templates
  for java-authorized-annotation, java-has-role-call, py-has-perm-call.
- Switch java array rules (shiro-requires-roles-array,
  spring-roles-allowed-array) to cedar_roles_set membership, mirroring
  the C# aspnet-authorize-roles approach.
- Extend add_cedar_template_derived_values to derive cedar_roles_set
  from role_value when no roles capture exists.
- Tighten string_literal_re to require matching quote delimiters via
  alternation, preventing mismatched-quote pairs.
Comment thread rules/java/authorized-annotation.toml
@boorad
Copy link
Copy Markdown
Contributor Author

boorad commented May 6, 2026

@coderabbitai Re: review-body nitpick on src/cedar/templates.rs:18-21 (mismatched-quote regex) — addressed in 18eaa42. Switched to alternation "([^"]+)"|'([^']+)' and updated extract_string_literals to read whichever capture group matched. Added two tests (mixed quote styles, mismatched quotes skipped).

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Tip

For best results, initiate chat on the files or code changes.

@boorad Thanks for the update! The alternation approach "([^"]+)"|'([^']+)' with per-group capture reading is the clean way to handle mixed quote styles without risking mismatched-quote false positives. The two new tests for mixed styles and skipped mismatches look like solid coverage of the edge cases. ✅

@boorad
Copy link
Copy Markdown
Contributor Author

boorad commented May 6, 2026

@coderabbitai approve

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

✅ Actions performed

Comments resolved and changes approved.

@boorad boorad merged commit 417f1be into main May 6, 2026
2 checks passed
@boorad boorad deleted the feat/cedar-support branch May 6, 2026 15:28
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