Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
853 changes: 841 additions & 12 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ sha2 = "0.11"
regex = "1"
streaming-iterator = "0.1"
regorus = { version = "0.9", default-features = false, features = ["arc"] }
cedar-policy = "4"
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
url = "2"

Expand Down
12 changes: 7 additions & 5 deletions docs/CEDAR_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ So: log the design now while context is fresh; revisit when v0.2 is shipping.
- Add `suggest_policy(finding_id, engine)` and `validate_policy(content, engine)`.
- Keep `suggest_rego` and `validate_rego` as Rego-pinned aliases that internally call the new tools with `engine="rego"`. Document them as deprecated but supported.

### Phase B — clean up the abstraction (later, ~150 lines + refactor)
### Phase B — clean up the abstraction (~150 lines + refactor)

Once Phase A has shipped and users have actually generated Cedar in anger:
Tracked separately as its own work item so Phase A can ship and Phase B can
land on its own merits, not on a wait-for-feedback gate.

6. **Extract a `PolicyGenerator` trait**
```rust
Expand All @@ -103,14 +104,14 @@ Once Phase A has shipped and users have actually generated Cedar in anger:
```
`RegoGenerator` and `CedarGenerator` implement it. The `extract` pipeline becomes generic over `dyn PolicyGenerator`, dispatched off the `--engine` flag.

7. **Consider a `PolicyOutput` collection on `Finding`**
Replace the parallel `*_stub` fields with `policy_outputs: Vec<PolicyOutput>` once enough engines exist that the parallel-field pattern becomes embarrassing. This is a JSON-schema break — gate it behind a major-version bump.
7. **Replace parallel `*_stub` fields with a `PolicyOutput` collection on `Finding`**
Swap `rego_stub` / `cedar_stub` for `policy_outputs: Vec<PolicyOutput>`. Provide a deserialization shim that folds legacy `*_stub` fields into the new collection so existing findings files keep loading; the shim is part of Phase B, not a separate migration.

## Risks and migrations

| Risk | Detail | Mitigation |
|---|---|---|
| Persisted JSON findings drift | Users who store findings files will have a mix of `rego_stub`-only and `rego_stub` + `cedar_stub` records | Keep both fields; never remove `rego_stub` without a major-version bump |
| Persisted JSON findings drift | Users who store findings files will have a mix of `rego_stub`-only and `rego_stub` + `cedar_stub` records | Phase A keeps both fields. Phase B replaces them with `policy_outputs` and ships a deserialization shim that reads the legacy fields, so old findings files keep loading without a coordinated bump |
| MCP tool-name contract | Agent hosts have `suggest_rego` / `validate_rego` wired up | Keep them as aliases indefinitely; new tools are opt-in |
| Half-baked Cedar templates | Shipping Cedar coverage for some rules but not others reads as broken | Document Cedar coverage per-rule; CLI warns when extracting Cedar from rules that have no Cedar template |
| `regorus` and `cedar-policy` binary size | Two policy engines linked into one binary | Both are Rust-native; combined overhead should be ~3–5MB. Acceptable. Revisit with `--features rego,cedar` if it bloats |
Expand All @@ -133,3 +134,4 @@ Once Phase A has shipped and users have actually generated Cedar in anger:
## Decision log

- **2026-05-03** — memo drafted alongside the user-facing rename from "Rego for OPA" to "Policy as Code (PaC)." No code changes proposed yet; doc captures the investigation while context is fresh.
- **2026-05-06** — Phase A landed (`feat: add Cedar as a second policy engine`). Phase B's "wait for users" gate and "JSON-schema break needs a major-version bump" gate dropped: the `*_stub` → `policy_outputs` migration is in Phase B's scope and ships with a deserialization shim, so it doesn't need a coordinated version cut.
12 changes: 12 additions & 0 deletions rules/csharp/aspnet-authorize-attribute.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
context.authenticated == true
};
"""
[[rule.tests]]
input = """
using Microsoft.AspNetCore.Authorization;
Expand Down
12 changes: 12 additions & 0 deletions rules/csharp/aspnet-authorize-policy-shorthand.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
context.policy == "{{policy}}"
};
"""
[[rule.tests]]
input = """
using Microsoft.AspNetCore.Authorization;
Expand Down
12 changes: 12 additions & 0 deletions rules/csharp/aspnet-authorize-policy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
context.policy == "{{policy}}"
};
"""
[[rule.tests]]
input = """
using Microsoft.AspNetCore.Authorization;
Expand Down
12 changes: 12 additions & 0 deletions rules/csharp/aspnet-authorize-roles.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.role in [{{cedar_roles_set}}]
};
"""
[[rule.tests]]
input = """
using Microsoft.AspNetCore.Authorization;
Expand Down
12 changes: 12 additions & 0 deletions rules/csharp/aspnet-require-authorization.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
context.authenticated == true
};
"""
[[rule.tests]]
input = """
var app = builder.Build();
Expand Down
13 changes: 13 additions & 0 deletions rules/csharp/authorization-service-authorize-async.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
// TODO: translate ASP.NET authorization service call: {{arguments}}
principal.authenticated == true
};
"""
[[rule.tests]]
input = """
var result = await _authorizationService.AuthorizeAsync(User, resource, "CanDeleteUsers");
Expand Down
12 changes: 12 additions & 0 deletions rules/csharp/has-claim-call.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.claims["{{claim_type}}"] == "{{claim_value}}"
};
"""
[[rule.tests]]
input = """
if (User.HasClaim("scope", "users.delete")) {
Expand Down
12 changes: 12 additions & 0 deletions rules/csharp/is-in-role-call.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.role == "{{role_value}}"
};
"""
[[rule.tests]]
input = """
if (User.IsInRole("Admin")) {
Expand Down
13 changes: 13 additions & 0 deletions rules/go/access-descriptor-builder.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
// TODO: verify attribute check
principal.attr == "TODO"
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/casbin-enforce.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.role == "TODO"
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/feature-gate-check.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.plan == "TODO"
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/gin-auth-middleware.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
context.authenticated == true
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/has-role-call.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.role == "{{role_value}}"
};
"""
[[rule.tests]]
input = """
package main
Expand Down
13 changes: 13 additions & 0 deletions rules/go/opa-rego-eval.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ allow if {
}
"""


[rule.cedar_template]
template = """
// TODO: custom authorization pattern — review and implement manually
// permit (
// principal,
// action,
// resource
// )
// when {
// ...
// };
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/ownership-check.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
resource.owner == principal
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/permission-check-call.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.permissions.contains("{{perm_value}}")
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/go/role-check-conditional.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.role == "{{role_value}}"
};
"""
[[rule.tests]]
input = """
package main
Expand Down
12 changes: 12 additions & 0 deletions rules/java/authorized-annotation.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ allow if {
}
"""


[rule.cedar_template]
template = """
permit (
principal,
action,
resource
)
when {
principal.role == "{{role_value}}"
};
"""
Comment thread
coderabbitai[bot] marked this conversation as resolved.
[[rule.tests]]
input = """
public class UserService {
Expand Down
Loading