Build1 publisher3 min readPublished
An outside test found AgentGuard's executor would run whatever callable the caller supplied
OpenWorkProof's first pass on AgentGuard found authorization could attach to something other than the exact execution inputs. A rebuild around an immutable snapshot then passed 39 of 39 adversarial cases.
The Engineer · Build desk

What happened
- A developer building AgentGuard set out to test what keeps an approved call attached to execution, using a refund authorized at an amount of 100 and executed at an amount of 1000 as the example.
- Independent testing by OpenWorkProof found two high-severity issues, one where authorization could attach to something other than the exact execution inputs.
- The other high-severity finding was an execution path that allowed the caller to supply an arbitrary callable, so the approved tool name did not determine the code that ran.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Binding a decision to exact arguments pulls retry logic and argument normalization inside the security boundary, because any layer that rewrites inputs after the decision now needs a decision of its own.
- exposure Any executor that resolves its callable from caller-supplied input gives you an approval over a tool name while the caller picks the function, and that is testable in an afternoon without a threat model.
- contradiction The post's question and its evidence point in different directions: no framework was tested, so anyone citing 39/39 as proof that agent stacks lack this binding is citing one implementation's bug report.
- decision Anyone evaluating a runtime authorization layer now has a concrete acceptance question: which of the eight properties are bound into the snapshot, and what the layer does on replay and on expiry.
The two initial findings are more useful than the framing around them. The first was authorization being associated with something other than the exact execution inputs [4]. The second was an execution path that allowed a caller to supply an arbitrary callable [5]. The second is the one I would check first in someone else's stack. A decision that approves the name `refund_customer` and a pair of arguments still leaves the caller choosing which code runs. That path tends to survive code review, because it looks like dependency injection.
The test was meant to be framework-neutral: the author looked at what happens where a consequential action reaches the executor [12]. He listed eight things he treats as materially defining an action: tool or capability, arguments, target or resource, agent identity, runtime identity or context, authorization state, authorization expiry, and execution capability [3]. The rule he wants from that list is one sentence. "If something materially changes between authorization and execution, the execution should be rejected," he wrote [2].
The worked example is a refund. Authorized: `refund_customer(customer_id="123", amount=100)`. Executed: `refund_customer(customer_id="123", amount=1000)` [1]. Nothing carried the policy decision on the first call forward to the second.
On the fix, the author wrote that he "redesigned the execution model around that idea, using an immutable execution snapshot and capability-bound execution rather than reconstructing execution from caller-controlled inputs" [6]. Read literally, that means the callable is fixed when the decision is made, and the executor does not resolve it again from whatever the caller hands over at call time.
Then the numbers. The retest was independent and adversarial, covering argument changes, target changes, agent identity, runtime identity, capability substitution, replay, expiry, stale policy state and legitimate execution, and it passed 39 of 39 tests [7]. Nine named categories over 39 tests is about four tests per category, and fewer if the list is partial [15]. The author's own caveat is the right one to quote: "It isn't a security certification and it doesn't prove that this is a universal problem," he wrote [9]. The post does not identify any framework he tested for the property [16].
That last point is what keeps this from being a general indictment of agent security work. The open question in the post is whether existing agent infrastructure already keeps authorization attached to the exact action that reaches execution, and whether the gap hurts enough for developers to fix it [13]. He names the answer that would sink his own project: "In fact, if they already guarantee this properly, that's useful information. It would mean there may be little reason for another runtime authorization layer to exist," he wrote [10].
Adopting the rule has a price. Authorization expiry is one of the eight bound properties [3], so a human approval that sits in a queue past its expiry has to go back for a new decision. Any retry that re-serializes arguments, normalizes a currency amount or attaches an idempotency key changes the inputs the snapshot was taken over, which under the stated rule is a rejection [2]. For the class of actions the author is interested in, including refunds and payments, database writes, infrastructure changes and privileged API calls [14], failing closed on a mutated retry is the behaviour you want. It is also the behaviour that will page you at 3am the first week.
The finding came from outside. According to the post, this was not originally a security research project, and OpenWorkProof's testing requirement forced the closer look at whether authorization was bound to execution or merely associated with an action [11].
What to watch
- Whether anyone runs the same argument-mutation and capability-substitution suite against named agent frameworks and publishes per-test results.
- Whether OpenWorkProof releases the test plan and the per-category breakdown of the 39 cases, so coverage can be read property by property.
- Whether the rebuilt execution model rejects ordinary traffic in production, in particular retries that re-serialize arguments after the snapshot is taken.