{"slug":"agent-stateful-permissions","title":"Why can a permission check that's correct for a single tool call still let an agent break the rules across many?","question":"Why can a permission check that's correct for a single tool call still let an agent break the rules across many?","summary":"A policy that checks each tool call in isolation can be right every time and still let an agent break a limit across a sequence. AWS's Dogwood shows a $5,000 cap beaten by three concurrent $2,000 transfers when it counts settled responses, not issued requests.","status":"active","cluster":"safety","cluster_label":"Safety and control","updated":"2026-10-06","latest_evidence_added":"2026-08-22","audience":"strong-software-engineer","math_depth":"","sections":[{"heading":"Builder consequence","html":"<p>If your tool-call authorization checks each request in isolation (classic RBAC, and Cedar&#x27;s default), a policy can be correct for every single call and still let the agent do what it was meant to prevent. AWS&#x27;s example: a <strong>$5,000</strong> transfer cap, checked correctly per request, falls to three concurrent <strong>$2,000</strong> transfers. The cap logic is fine. The bug is enforcing a stateful rule with a stateless check.</p>"},{"heading":"Short answer","html":"<p>Rules about what an agent has already done (approval before acting, rate or spend limits, &quot;stop after touching confidential data&quot;) need the agent&#x27;s event history, not just the current request. AWS&#x27;s Dogwood adds a <code>when temporal</code> clause to Cedar with four history operators. What the rule counts matters as much as the operator. Counting *settled responses* lets several *in-flight requests* each pass before any of them updates the total.</p>"},{"heading":"Builder model","html":"<p>Split authorization into two questions that need different mechanisms:</p>\n<ul><li><strong>Is this request allowed on its own?</strong> A stateless check of caller, action, and resource answers it. Most tool calls only need this.</li><li><strong>Is it allowed given what the agent already did?</strong> Approval workflows, rate or spend limits, and sequencing rules need history. A stateless check cannot answer this however you tune it.</li></ul>\n<p>Even history-aware checks fail when they count the wrong event: completed actions instead of issued ones.</p>"},{"heading":"Mechanism","html":"<p><strong>Why stateless is the default.</strong> Cedar evaluates a request using only its own context: principal, action, resource, and attributes. That restriction is what lets Cedar formally verify a policy set, for example that no request is both permitted and denied, without simulating every call sequence.</p>\n<p><strong>How Dogwood adds history.</strong> A <code>when temporal</code> clause is translated into ordinary Cedar context fields, filled from the event history, before the stateless evaluator runs. Temporal reasoning happens once, up front. Four operators cover the common shapes:</p>\n<ul><li><code>formerly</code>: did X happen in a window?</li><li><code>count_within</code>: how many times?</li><li><code>count_distinct_within</code>: how many distinct values?</li><li><code>sum_within</code>: what is the running total?</li></ul>\n<p>The cost is that temporal conditions give up Cedar&#x27;s automated formal-analysis guarantees.</p>\n<p><strong>The concurrency trap.</strong> <code>sum_within(response.amount, 1h) &lt;= 5000</code> passes every sequential test. Under concurrency, three $2,000 requests are each evaluated while the settled total is still $0, so all three pass. Writing the rule against request events closes the gap, because a request counts the moment it is issued.</p>\n<p><strong>Consistency across servers.</strong> A correct policy on one MCP server does not help if every server defines &quot;risky write&quot; differently. Cloudflare&#x27;s WriteGuard centralizes that into one layer with four risk tiers, from read-only to critical.</p>"},{"heading":"How to apply","html":"<ul><li><strong>Decide up front whether a rule needs history.</strong> Approval workflows, rate or spend limits, and post-action sequencing rules do. Most others do not.</li><li><strong>Count events when they are issued, not when they settle.</strong> This applies to any budget or rate limit you write, with or without Dogwood.</li><li><strong>Use the four operator shapes as vocabulary</strong> to name which kind of history a rule needs, even without adopting Dogwood.</li><li><strong>Test stateful rules with concurrent calls,</strong> not only sequential ones.</li><li><strong>Do not make a reference interpreter or private beta your only control.</strong> AWS says Dogwood&#x27;s interpreter is not for production; WriteGuard has no production track record.</li><li><strong>Centralize risk tiering</strong> if you run many MCP servers, instead of letting each server define its own.</li></ul>"},{"heading":"Failure modes","html":"<ul><li>Testing a spend cap or rate limit only with sequential traffic, then shipping it.</li><li>Summing responses instead of requests, so in-flight calls slip past a rule that looks correct.</li><li>Expecting a stateless system (classic RBAC, plain Cedar) to express &quot;stop after the agent did X&quot;.</li><li>Deploying a reference implementation or beta as a production control despite the maker&#x27;s own caveats.</li></ul>"},{"heading":"Related","html":"<p>See <a href=\"/topic/mcp\">MCP</a> for the protocol both tools govern, <a href=\"/topic/agent-sandboxing\">agent sandboxing</a> for isolation controls at a different layer, and <a href=\"/topic/tool-use\">tool use</a> for the agent-tool integrations where this gap appears.</p>"}],"evidence":[{"id":"story-410ca031ddd240de-aws-dogwood","kind":"story","tier":"source story","title":"AWS Open-Sources Dogwood, Extending Cedar to Govern Sequences of Agent Tool Calls","note":"AWS open-sourced Dogwood (Apache 2.0), a Cedar extension adding a `when temporal` clause that reads agent event history. Four operators, macros over Metric First-Order Temporal Logic: formerly, count_within, count_distinct_within, sum_within. Worked example: a $5,000 cap summing settled responses is beaten by three concurrent $2,000 transfers; count requests as issued instead. Caveats: the reference interpreter is not for production, and temporal conditions give up Cedar's automated formal-analysis guarantees.","added":"2026-08-22","sid":"410ca031ddd240de"},{"id":"story-e3560887ce822a61-cloudflare-writeguard","kind":"story","tier":"source story","title":"Cloudflare WriteGuard Brings Fine-Grained Security Controls for MCP Servers","note":"Cloudflare WriteGuard, in private beta with no production results reported, sits behind Cloudflare's MCP server portal as a shared policy and audit layer. It intercepts MCP tool calls and assigns each operation a risk tier: read-only, minimal impact, contained write, or critical. Stated rationale: reimplementing controls in each server 'would take more work and produce inconsistent behavior.'","added":"2026-08-22","sid":"e3560887ce822a61"},{"id":"agent-stateful-permissions-editorial-synthesis","kind":"editorial-inference","tier":"editorial inference","title":"LLM Digest synthesis","note":"Dogwood and WriteGuard attack the same gap from different sides. Dogwood puts cross-call state into the policy language; WriteGuard centralizes per-operation risk tiering so many MCP servers share one enforcement point. Neither is a proven production control yet, but both are independent 2026 evidence that per-request authorization is no longer treated as enough for agents acting in sequences.","added":"2026-08-22"}],"related_topics":[{"slug":"mcp","title":"Model Context Protocol: a standard interface for agent tools"},{"slug":"agent-sandboxing","title":"Sandboxing, scoped credentials, and guardrails"},{"slug":"tool-use","title":"Agents reach the outside world through fragile, ad-hoc integrations"}],"related_playbook_cards":[],"related_storylines":[]}