Skip to content

PATTERN Cited by 1 source

Deny-wins policy composition

Problem: A policy engine evaluates multiple independent policies (intent, risk scoring, PII blocking, custom rules) against the same action. How should their verdicts combine? If an "allow" from one policy can override a "deny" from another, an attacker who can inject a permissive policy (or trick the system into adding one) can escalate privileges.

Pattern: A single denial from any policy wins, regardless of what other policies allow. The composed verdict is the most restrictive across all active policies. This makes the policy set monotonically restrictive — adding a new policy can only narrow the allowed action set, never widen it.

Shape

Verdict = AND(policy_1.verdict, policy_2.verdict, ..., policy_n.verdict)

Where:
  DENY from any → final = DENY
  ASK from any (and no DENY) → final = ASK
  ALLOW from all → final = ALLOW

Why it matters for agent security

In Omnigent, the agent has tools to browse and add policies, but none to remove, edit, or disable them. Even if the agent (via prompt injection) adds a permissive policy, deny-wins ensures it cannot override existing restrictions. Combined with human-gated policy addition, this makes intent tamper-resistant from the model side.

Precedent in other systems

Deny-wins (or deny-override) is a well-established pattern in:

  • AWS IAM — explicit Deny always overrides Allow.
  • Cedar (Amazon Verified Permissions) — forbid policies override permit.
  • SELinux — deny rules take precedence.
  • Kubernetes RBAC — absence of allow = deny (default-deny); no explicit deny rules, but the mental model is similar.

Trade-offs

✓ Advantages ✗ Costs
Prevents privilege escalation via policy injection Can make debugging "why was this denied?" harder
Composable — policies are independent modules Over-restrictive policies may require operator intervention
Monotonically restrictive — safe to add policies incrementally Cannot express "override" semantics when legitimately needed

Seen in

Last updated · 593 distilled / 1,805 read