JTSTech Services
All articles

Security · September 24, 2026 · 8 min read

The approve button is the whole safety net for most AI agents. New research shows what you click and what runs can be two different things.

Almost every business that lets an agent touch money, customers or published content puts a human approval step in front of it. That step is only worth something if the operation you were shown is the operation that actually executes. A paper released last week reproduces the gap between those two things in several shipped agent products by name and version, and one product that gets it right proves this is an engineering problem with a known fix rather than a law of nature.

Written by Jeremy Souffir Founder, JTS Tech Services

The short version. When a business puts an AI agent anywhere near something that matters, the control it reaches for is almost always the same one: a human has to approve the action before it happens. It is a good instinct. But a paper published on 17 September and picked up across the security community on 22 September points out something uncomfortable about it. The approval is only meaningful if the operation presented for review is the operation later carried out. The author calls failures of that binding Loopjacking: you approve what you understand to be operation A, and the system uses your decision to authorise a materially different operation B. He then reproduces it in named, shipped agent products at named versions. If your agent safety story is "a person signs off first", this is the week to find out what your sign-off is actually bound to.

What did the research actually find?

  • Two distinct failures, not one. In a representation-based attack, the harmful version of the operation is already encoded at the moment you approve, but it is omitted or misrepresented on the screen you are shown. In a post-approval state-substitution attack, the screen is honest and you approve the right thing, and then mutable workflow state is changed afterwards so that a different operation is what executes.
  • The worked example is deliberately mundane. A low-privilege user requests a transfer of 20 units to an approved vendor. An administrator reviews that and approves it. The user then replaces the pending arguments with a transfer of 2,000 units to a destination they control, and the system executes the larger one under the administrator's approval. Nobody broke in and no credential was stolen.
  • Post-approval substitution was reproduced in seven tested releases of Agno AgentOS ending at 3.0.9, and in 12 tested versions of a conditional in-memory LangGraph Agent Server composition ending at 0.14.0, under particular authorization configurations rather than in every possible setup.
  • Representation mismatch was reproduced in OpenClaw 2026.2.23, and the paper records that the following release, 2026.2.24, rejects it. This is a class of bug that gets fixed when someone looks for it.
  • One product was tested and did not fail. OpenAI Agents SDK 0.22.0 and 0.22.2 act as a negative control: the approval keeps an exact fingerprint of the specific call across serialization, so when the researcher tried to mutate the approved action it raised an error and the ledger stayed empty. That is the whole point of including it.

Whose findings these are, and what they explicitly do not say

This is one researcher, Adithyan Arun Kumar, working alone in isolated local environments with mock ledgers and synthetic data, not a survey of real deployments and not an incident report. He says so himself, and unusually clearly: the four product paths are a purposive set, not a random or representative sample, and the results are exact product and configuration findings rather than a prevalence estimate. He assigns no CVSS score and no universal severity. The reproduction archive's evidence cutoff is 10 September 2026, so newer releases may already behave differently. Read "seven releases of a named product, in specific configurations" as exactly that. The reason to care is not that the sky is falling across the industry. It is that the assumption underneath your own approval step has now been shown to be an assumption rather than a guarantee, and you can check yours in an afternoon.

Why does this break the one control everyone trusts?

Because human approval is doing a different job than most people think. When you approve something, you believe you are authorising an action. What you are actually doing, in most implementations, is setting a flag on a record that some other piece of code will later read before it executes. Everything depends on whether the thing executed is still the thing described on the screen when you looked at it. If there is any window between those two moments in which the record can change, and if anything other than the approver can reach into that window, then your approval is not a decision about an operation. It is a decision about a slot, and whatever is sitting in the slot at execution time inherits your authority.

This is why the failure is so easy to miss in a review. Nothing about it looks like a security bug. The login worked correctly, the permissions were checked, the approval was genuinely given by a genuinely authorised person, and the audit log will show an approved action that completed successfully. If you go looking afterwards, the trail says the process worked. The only thing that went wrong is a binding that was never made explicit, between a rendering on a screen and an invocation on a server, and there is usually no single place in the codebase where you can point at that binding and confirm it exists.

The gap the research is about. A request is rendered for a human, the human approves it, and the approval is recorded against a pending record. If that record can still be changed between the approval and the execution, and by anyone other than the approver, then what runs is whatever is in the record at the end, carrying the authority of a decision that was made about something else.
The gap the research is about. A request is rendered for a human, the human approves it, and the approval is recorded against a pending record. If that record can still be changed between the approval and the execution, and by anyone other than the approver, then what runs is whatever is in the record at the end, carrying the authority of a decision that was made about something else.

Has this already happened outside a lab?

Yes, in a closely related form, and it already has a CVE. In 2025 Check Point Research disclosed MCPoison, tracked as CVE-2025-54136 with a CVSS score of 7.2, in the Cursor code editor. Cursor asked a developer to approve a Model Context Protocol server configuration once, then bound that trust to the configuration's name rather than to its contents. An attacker with write access to the repository could commit something harmless, wait for a teammate to approve it, and later swap the command behind the same name. Everyone who opened the project afterwards ran the attacker's code, with no new prompt, because the approval had already been given to a label. Cursor patched it. The paper treats it as a source-grounded analogue rather than one of its own findings, and that is the right way to read it: the shape is not hypothetical, it has shipped in a widely used developer tool, and it was fixed once someone named it.

The two conclusions that both get this wrong

The first wrong conclusion is to decide that human approval is theatre and rip it out, or to stop agent projects until the frameworks are proven. Approval is not the problem. An unbound approval is the problem, and the fix is narrow and known. The second wrong conclusion is the more common one in a business: to read the product names, check that you do not use Agno or LangGraph or OpenClaw, and move on relieved. That is the wrong test. The paper is not a list of unsafe vendors, and the author says outright that it does not estimate how widespread this is. Most businesses reading this did not buy an agent framework at all. They have an internal tool, or an automation someone wired together, where a request lands in a queue, a manager clicks approve, and a job picks it up a few seconds or a few hours later. That home-made gap between the click and the job is exactly the window the paper is describing, and nobody has ever tested it, because it was never thought of as a security boundary.

What does this look like in a business like yours?

It looks like the places where you have already, sensibly, decided a human should be in the loop. A refund queue where support proposes and a supervisor approves. A purchase order over a threshold. Bulk price or inventory changes on a store, staged for review before they go live. Outbound email or a social post drafted by a model and released by a marketer. A customer record merge. A payout run. In every one of those, someone looks at a summary on a screen and clicks a button, and something else executes afterwards. The question the research forces is narrow and answerable: between the screen and the execution, what can change, and who can change it?

Two details make this more pressing than it would have been two years ago. The first is that agents make the gap longer and busier. A traditional approval queue was a row in a database that nothing else touched. An agentic workflow keeps mutable shared state, re-plans, re-reads its inputs, and may have several actors writing to the same pending item, which is precisely the condition the substitution attack needs. The second is that the party changing the record does not have to be an attacker at all. A retry, a race between two steps, or a model that re-derives its arguments after the approval produces the same outcome by accident: a human authorised one thing and a different thing happened. You do not need a threat actor to get burned by an unbound approval. You only need one on a bad day.

What would we actually check?

  • Find every approval step you already have and write them down. Not the agent ones, all of them. The list is usually longer than anyone expects and it is almost never documented, because each step was added by a different team for a different reason.
  • For each one, ask what the approval is recorded against. If the answer is a name, a key, an identifier or a queue position rather than the full content of the operation, you have the MCPoison shape. If the answer is "the record, and the record is what runs", ask what else can write to that record.
  • Re-derive and compare at execution time. The paper's first mitigation in plain terms: render the complete operation to the human, store exactly what they were shown, and then immediately before executing, rebuild the operation that is about to run and compare it. If anything material differs, refuse and ask for a fresh approval. This is a small amount of code and it is the single highest-value change here.
  • Lock the pending state. The second mitigation is ordinary access control applied somewhere people forget to apply it: nobody who is not the approver should be able to modify work that is sitting approved and waiting. In the researcher's LangGraph case, denying non-approvers permission to update shared pending thread state blocked the attack without breaking legitimate execution.
  • Show the whole operation, not a summary. A reviewer who is shown three friendly fields out of eleven cannot catch a representation mismatch, and is being asked to take responsibility for something they were never shown. If the full operation is too ugly to display, that is a signal about the design, not a reason to hide it.
  • Log what executed, not what was approved. Most audit trails record the approval and assume the rest. Record the operation as it actually ran, alongside the approved version, so that a mismatch is visible afterwards instead of invisible forever. If you do only two things on this list, make them this one and the re-derive check.
  • Put a clock on it. An approval that is still executable a week later is a much bigger window than one that expires in fifteen minutes. Short expiry is not a fix, but it shrinks the problem cheaply while you do the rest.
The fix, in the shape the paper describes. What the human was shown is captured and kept. Immediately before execution the operation is rebuilt from current state and compared against that captured version. Identical, and it runs. Different in any material way, and it stops and goes back for a fresh approval rather than inheriting the old one.
The fix, in the shape the paper describes. What the human was shown is captured and kept. Immediately before execution the operation is rebuilt from current state and compared against that captured version. Identical, and it runs. Different in any material way, and it stops and goes back for a fresh approval rather than inheriting the old one.

The genuinely encouraging part

This is one of the rare security stories with a worked answer printed alongside the problem. The negative control is the encouraging bit: a shipped product in the same category, tested the same way, simply did not fail, because it keeps an exact fingerprint of the approved call and refuses anything that does not match at use time. OpenClaw's next release rejected the attack too. Cursor patched MCPoison. None of this required new cryptography, a new standard, a vendor you have to wait for, or a rebuild of your agent stack. It requires deciding that the approval step is a security boundary and then binding it like one, which is a contained piece of engineering with a clear test: change the pending operation after approval and confirm that execution refuses. Most teams can answer that question about their own systems this week, and for most of them the answer will be fine. The ones where it is not are much better off knowing.

Where we fit

The reason this is worth bringing someone in for is that the answer is not in any one place. The approval screen belongs to whoever built the interface, the pending record belongs to the backend, the execution belongs to a job runner or an agent framework nobody on staff wrote, and the permissions on the shared state belong to a platform configuration set up long before anyone thought about agents. Checking whether the binding holds means reading across all of those at once, which is exactly the kind of work that falls between teams and therefore never gets done. That is the work we take on: we map every approval step in the systems you actually run, test whether the executed operation is still the approved one by trying to change it, put the re-derive-and-compare check and the pending-state permissions in place where they are missing, and leave you with a log that records what ran rather than what was clicked. It is a short, contained engagement, and it ends with a boundary you can point at instead of an assumption you inherited. If you have agents near money, customer data or anything you publish, start with the contact form and tell us where your approve buttons are.

Sources

Keep reading

Agent security review

Is your approve button bound to anything?

We map every approval step in the systems you run, try to change the operation after it has been approved, and put the re-derive-and-compare check and the pending-state permissions in place where they are missing. A short engagement that ends with a boundary you can point at.