Skip to content

feat(security): declarative policy compiler for tool call authorization#1870

Merged
bug-ops merged 2 commits intomainfrom
policy-compiler-tool-auth
Mar 15, 2026
Merged

feat(security): declarative policy compiler for tool call authorization#1870
bug-ops merged 2 commits intomainfrom
policy-compiler-tool-auth

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented Mar 15, 2026

Closes #1695

Summary

  • Implements PolicyEnforcer — a deterministic pre-execution layer that evaluates TOML-based access-control rules before any tool executes, independently of prompt content
  • PolicyGateExecutor wraps TrustGateExecutor as the outermost executor; generic error to LLM, trace details to audit log only
  • Deny-wins semantics: all deny rules evaluated first, then allow rules, then default_effect
  • Lexical path normalization via Path::components() prevents ../ traversal bypass (CRIT-01)
  • Tool name normalization (lowercase + trim) on both rule compile and evaluation (CRIT-02)
  • Audit log entry for every policy decision (allow and deny)
  • policy-enforcer feature flag (optional, included in full)

Integration points

  • [tools.policy] config section with enabled, default_effect, rules, policy_file
  • --policy-file <path> CLI flag
  • /policy check <tool> <args> and /policy status slash commands
  • --init wizard step for policy configuration
  • --migrate-config step inserts empty [tools.policy] in existing configs
  • docs/src/advanced/policy-enforcer.md — full configuration reference

Test plan

  • cargo nextest run -p zeph-tools --features policy-enforcer --lib — 670 tests pass
  • Deep .. chain traversal test: /a/b/c/d/../../../../../../etc/passwd/etc/passwd → Deny
  • Deny-wins insertion-order independence: deny-first and deny-last both produce Deny
  • cargo nextest run --workspace --features full --lib --bins — 5868 tests pass

Follow-up issues filed

  • IMP-ALLOWIF-NOOP: PolicyEffect::AllowIf declared but behaves as Allow — implement or remove
  • SEC-POLICY-FILE: load_policy_file() lacks symlink boundary check
  • MED-ENV-LEAK-IN-TRACE: /policy check injects real env vars into trace context
  • MED-POLICY-CHECK-TRUST-HARDCODED: hardcoded TrustLevel::Trusted in check command
  • GAP-03/04/05: additional test coverage gaps for policy_file and boundary conditions

bug-ops added 2 commits March 15, 2026 20:54
…on (#1695)

Implements PolicyEnforcer — a deterministic pre-execution layer that evaluates
TOML-based access-control rules before any tool executes, independently of
prompt content. Addresses the shell blocklist bypass class (SEC-1525) with a
structured, auditable enforcement layer.

Key changes:
- PolicyEnforcer with deny-wins semantics, lexical path normalization (prevents
  path traversal via ../ chains), and tool name normalization (case/whitespace)
- PolicyGateExecutor wraps TrustGateExecutor as the outermost executor in the
  chain; generic error to LLM, trace details to audit log only
- [tools.policy] config section with enabled/default_effect/rules/policy_file
- policy-enforcer feature flag (optional, included in full)
- --policy-file CLI flag, /policy check|status slash commands
- --init wizard step, --migrate-config step for existing configs
- docs/src/advanced/policy-enforcer.md with full configuration reference
@github-actions github-actions bot added documentation Improvements or additions to documentation rust Rust code changes core zeph-core crate dependencies Dependency updates config Configuration file changes enhancement New feature or request size/XL Extra large PR (500+ lines) labels Mar 15, 2026
@bug-ops bug-ops enabled auto-merge (squash) March 15, 2026 20:01
@bug-ops bug-ops merged commit 4c0bc90 into main Mar 15, 2026
24 checks passed
@bug-ops bug-ops deleted the policy-compiler-tool-auth branch March 15, 2026 20:09
bug-ops added a commit that referenced this pull request Mar 15, 2026
The variant was declared in PR #1870 but evaluated identically to Allow
in the policy engine (deny-wins check: `rule.effect != Deny`). This made
it misleading for users writing TOML policy rules.

Remove AllowIf and update the inline comment in the evaluator. Conditions
on allow rules are already expressed via rule fields (tool_matcher, params,
trust, etc.) — no separate variant is needed.

Closes #1871
bug-ops added a commit that referenced this pull request Mar 15, 2026
The variant was declared in PR #1870 but evaluated identically to Allow
in the policy engine (deny-wins check: `rule.effect != Deny`). This made
it misleading for users writing TOML policy rules.

Remove AllowIf and update the inline comment in the evaluator. Conditions
on allow rules are already expressed via rule fields (tool_matcher, params,
trust, etc.) — no separate variant is needed.

Closes #1871
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Configuration file changes core zeph-core crate dependencies Dependency updates documentation Improvements or additions to documentation enhancement New feature or request rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

research(security): declarative policy compiler for tool call authorization (Policy Compiler pattern)

1 participant