Skip to content

fix: sync drifted policies.cedar copies, restore unknown-MCP-tool deny - #1596

Merged
lavkushry merged 1 commit into
mainfrom
fix/ci-unknown-mcp-tool-default-deny
Jun 27, 2026
Merged

fix: sync drifted policies.cedar copies, restore unknown-MCP-tool deny#1596
lavkushry merged 1 commit into
mainfrom
fix/ci-unknown-mcp-tool-default-deny

Conversation

@lavkushry

@lavkushry lavkushry commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

Root-caused the Gateway (beta)/Gateway (1.88) CI failures that have been showing up red on every PR in this repo regardless of what that PR touched.

The repo carries 4 copies of the base Cedar policy set:

  • policies.cedar (root) — canonical
  • lib/policy/policies.cedar — already in sync with root
  • src/policies.cedarthe one PolicyEngine::init("policies.cedar") actually loads at gateway runtime/test time
  • helm/aegis-gateway/files/policies.cedar — Helm deployment copy

src/policies.cedar and the Helm copy had silently drifted from the canonical root file, missing the mcp-unknown-tool-forbid rule entirely. This downgraded the fail-closed default for any unregistered MCP tool from deny to require_approval (via the critical-risk-requires-approval override) — exactly what authorize_denies_unknown_mcp_tools_by_default and authorize_denies_unknown_mcp_tool_with_encoded_or_cased_identifier were catching on every CI run.

(Production Docker deployments were unaffected — docker-compose.yml build context is the repo root and also bind-mounts the root policies.cedar at runtime — but every cargo test --workspace run, and any bare cargo run/dev invocation pointed at the wrong copy, were exposed to this.)

Changes

Test plan

  • Confirmed src/policies.cedar was missing the forbid rule present in root policies.cedar and lib/policy/policies.cedar (byte-diff).
  • Confirmed Docker/Compose deployment paths use the root copy, not the stale one.
  • CI (cannot run cargo test locally — no Rust toolchain in this environment) — expect Gateway (beta)/Gateway (1.88) to go green; Gateway (stable)'s cargo fmt --check will still show pre-existing, unrelated drift in files this PR doesn't touch (cedar.rs, decisions.rs, playbooks.rs, tenant.rs, tenant_bloom.rs, lib.rs mod ordering) — flagging separately, not fixed here.

Summary by CodeRabbit

  • New Features

    • Added a stricter default rule that blocks tool calls to unknown MCP tools.
    • Unknown tools are now denied before other trust-based checks apply.
  • Tests

    • Added coverage to ensure all bundled policy copies stay byte-for-byte identical.
    • Updated existing test formatting without changing behavior.

The repo carries 4 copies of the base Cedar policy set. src/policies.cedar
(the one PolicyEngine::init("policies.cedar") actually loads at gateway
runtime/test time) and helm/aegis-gateway/files/policies.cedar had silently
drifted from the canonical root policies.cedar, missing the
"mcp-unknown-tool-forbid" rule entirely. This silently downgraded the
fail-closed default for any unregistered MCP tool from deny to
require_approval (via the critical-risk-requires-approval override), which
is exactly what
routes::authorize::tests::authorize_denies_unknown_mcp_tools_by_default and
authorize_denies_unknown_mcp_tool_with_encoded_or_cased_identifier were
catching — these were failing on Gateway (beta)/(1.88) CI on every PR,
unrelated to whatever that PR's own diff touched.

Syncs both stale copies to the canonical content and adds
policies_cedar_copies_stay_byte_identical (src/src/routes/policy.rs) so
future drift fails CI immediately with a clear cause, instead of as two
opaque test failures days or weeks later.

Also includes 36 round-trip tests (lib/policy/src/compiler.rs) and rustfmt
fixes for the new test code carried over from #1328's branch, since this
fix was found while investigating that PR's CI run.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@ecc-tools

ecc-tools Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df0e5f31-9d21-41b2-8587-ffd7c644555e

📥 Commits

Reviewing files that changed from the base of the PR and between b1289b3 and 6926c5c.

📒 Files selected for processing (4)
  • helm/aegis-gateway/files/policies.cedar
  • lib/policy/src/compiler.rs
  • src/policies.cedar
  • src/src/routes/policy.rs

📝 Walkthrough

Walkthrough

Adds an early deny rule for unknown MCP tool calls in the Cedar policy copies, adds a byte-equality regression test for deployed policy copies, and reformats policy compiler test inputs.

Changes

MCP tool policy gating

Layer / File(s) Summary
Unknown-tool deny rule
src/policies.cedar, helm/aegis-gateway/files/policies.cedar
tool_call requests are denied when context.is_mcp_tool_known is false.
Policy copy parity test
src/src/routes/policy.rs
The bundle round-trip test reformats the standalone Cedar path construction and adds byte-equality checks for the canonical and deployed policies.cedar copies.

Policy compiler test reflow

Layer / File(s) Summary
Wrapped test inputs
lib/policy/src/compiler.rs
Multiple request(...) constructions and one engine_from_cedar(...) call in lib/policy/src/compiler.rs are split across lines without changing inputs or assertions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through Cedar, neat and bright,
Unknown tools met a deny tonight.
Byte-for-byte, the copies stay true,
With tidy lines and carrots too.
Thump-thump — the policy moon feels right!

✨ 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 fix/ci-unknown-mcp-tool-default-deny

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lavkushry
lavkushry merged commit 5bef7af into main Jun 27, 2026
17 of 23 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request synchronizes the mcp-unknown-tool-forbid rule across the various copies of the Cedar policy files in the repository and adds a unit test to ensure they remain identical. Feedback suggests normalizing line endings in the new test to prevent failures on Windows environments due to CRLF/LF differences.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/src/routes/policy.rs
Comment on lines +1725 to +1750
fn policies_cedar_copies_stay_byte_identical() {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let canonical = std::fs::read_to_string(format!("{manifest_dir}/../policies.cedar"))
.expect("root policies.cedar must exist");

for (label, path) in [
("src/policies.cedar", format!("{manifest_dir}/policies.cedar")),
(
"lib/policy/policies.cedar",
format!("{manifest_dir}/../lib/policy/policies.cedar"),
),
(
"helm/aegis-gateway/files/policies.cedar",
format!("{manifest_dir}/../helm/aegis-gateway/files/policies.cedar"),
),
] {
let copy = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("{label} must exist at {path}: {e}"));
assert_eq!(
copy, canonical,
"{label} has drifted from the canonical root policies.cedar — \
every copy must be byte-identical, since the gateway loads \
{label} at runtime/test time, not the root copy"
);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Comparing files directly using std::fs::read_to_string without normalizing line endings can cause the test to fail on Windows or other environments where Git's core.autocrlf or different editors might checkout/save files with CRLF (\r\n) instead of LF (\n). Normalizing the line endings to \n before asserting equality makes the test robust across different platforms.

    fn policies_cedar_copies_stay_byte_identical() {
        let manifest_dir = env!("CARGO_MANIFEST_DIR");
        let canonical = std::fs::read_to_string(format!("{manifest_dir}/../policies.cedar"))
            .expect("root policies.cedar must exist")
            .replace("\r\n", "\n");

        for (label, path) in [
            ("src/policies.cedar", format!("{manifest_dir}/policies.cedar")),
            (
                "lib/policy/policies.cedar",
                format!("{manifest_dir}/../lib/policy/policies.cedar"),
            ),
            (
                "helm/aegis-gateway/files/policies.cedar",
                format!("{manifest_dir}/../helm/aegis-gateway/files/policies.cedar"),
            ),
        ] {
            let copy = std::fs::read_to_string(&path)
                .unwrap_or_else(|e| panic!("{label} must exist at {path}: {e}"))
                .replace("\r\n", "\n");
            assert_eq!(
                copy,
                canonical,
                "{label} has drifted from the canonical root policies.cedar — \
                 every copy must be identical, since the gateway loads \
                 {label} at runtime/test time, not the root copy"
            );
        }
    }

lavkushry added a commit that referenced this pull request Jun 27, 2026
…eny rule

PR #1596 correctly restored the mcp-unknown-tool-forbid Cedar rule (synced
src/ and helm/ policies.cedar copies to the canonical root), but this broke
53 tests that relied on never-registered fixture tool/action pairs
(filesystem/read_file, github/read_issue, github/push_commit,
github/read_file, github/delete_branch, quarantine_canary/trigger) silently
falling through to a permissive default instead of hitting a named permit
rule. Forbid always wins over permit, so once unknown tools are correctly
denied, those fixtures need to be registered like every other test action.

- Add register_default_test_fixtures(), called once from
  setup_state_with_events_capacity, registering the generic fixture
  tool/action pairs as known skill actions with default_decision: "policy"
  so Cedar's own rules still govern the actual decision.
- Tag the mcp-unknown-tool-forbid rule with @id("mcp_unknown_tool") and
  surface it via PolicyEngine::authorize's matched_policies, since Cedar's
  auto-numbered policy ids ("policy0", ...) gave the two originally-targeted
  regression tests no way to assert which rule fired.
lavkushry added a commit that referenced this pull request Jun 27, 2026
…eny rule (#1597)

* fix: sync drifted policies.cedar copies, restore unknown-MCP-tool deny

The repo carries 4 copies of the base Cedar policy set. src/policies.cedar
(the one PolicyEngine::init("policies.cedar") actually loads at gateway
runtime/test time) and helm/aegis-gateway/files/policies.cedar had silently
drifted from the canonical root policies.cedar, missing the
"mcp-unknown-tool-forbid" rule entirely. This silently downgraded the
fail-closed default for any unregistered MCP tool from deny to
require_approval (via the critical-risk-requires-approval override), which
is exactly what
routes::authorize::tests::authorize_denies_unknown_mcp_tools_by_default and
authorize_denies_unknown_mcp_tool_with_encoded_or_cased_identifier were
catching — these were failing on Gateway (beta)/(1.88) CI on every PR,
unrelated to whatever that PR's own diff touched.

Syncs both stale copies to the canonical content and adds
policies_cedar_copies_stay_byte_identical (src/src/routes/policy.rs) so
future drift fails CI immediately with a clear cause, instead of as two
opaque test failures days or weeks later.

Also includes 36 round-trip tests (lib/policy/src/compiler.rs) and rustfmt
fixes for the new test code carried over from #1328's branch, since this
fix was found while investigating that PR's CI run.

* fix(gateway): repair fixture fallout from restored unknown-MCP-tool deny rule

PR #1596 correctly restored the mcp-unknown-tool-forbid Cedar rule (synced
src/ and helm/ policies.cedar copies to the canonical root), but this broke
53 tests that relied on never-registered fixture tool/action pairs
(filesystem/read_file, github/read_issue, github/push_commit,
github/read_file, github/delete_branch, quarantine_canary/trigger) silently
falling through to a permissive default instead of hitting a named permit
rule. Forbid always wins over permit, so once unknown tools are correctly
denied, those fixtures need to be registered like every other test action.

- Add register_default_test_fixtures(), called once from
  setup_state_with_events_capacity, registering the generic fixture
  tool/action pairs as known skill actions with default_decision: "policy"
  so Cedar's own rules still govern the actual decision.
- Tag the mcp-unknown-tool-forbid rule with @id("mcp_unknown_tool") and
  surface it via PolicyEngine::authorize's matched_policies, since Cedar's
  auto-numbered policy ids ("policy0", ...) gave the two originally-targeted
  regression tests no way to assert which rule fired.
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