TL;DR
The CodeAnt AI Security Research Team found a Claude Code Computer Use authorization bypass where an approved action could continue sending native input to an unapproved application after focus changed.
The issue affected the
hold_keypath and is a TOCTOU (time-of-check to time-of-use) vulnerability, classified as CWE-367. An earlier fix closed the same authorization gap in thetypepath, but the siblinghold_keypath still checked the recipient only once, making this an incomplete security fix / patch bypass.The finding shows a broader security challenge for AI coding agents and computer-use tools: authorization must remain tied to the application receiving each event, not just to the original tool call.
It also connects to prompt injection and OWASP LLM01, where untrusted content can influence an agent's actions.
The fix is to carry authorization through every execution layer, revalidate the recipient before each event, constrain capabilities, and review security fixes by invariant.
What is Computer Use in Claude Code? (Agentic AI Desktop Control Explained)
Computer use is the capability that lets an AI agent control a real computer the way a person does: moving the pointer, clicking, typing, and pressing keys on the actual desktop. In Claude Code, Computer Use turns the model from something that suggests code into something that operates applications, an IDE, a browser, and a terminal on the user's machine.
That is a categorical change in risk. A chat assistant produces text you choose to act on. A computer-use agent produces trusted native input, indistinguishable from a human at the keyboard, delivered directly to whatever application is in front. The security of the whole feature therefore rests on one control: the user approves specific applications for the session, and the agent is supposed to drive only those.
How Claude Code’s Application Approval Boundary Works
Claude Code is gated by per-application approval. Before the agent can click or type in an application, the user approves that application for the current session, and applications with broad power get stronger warnings. Anthropic explicitly flags terminals and IDEs as equivalent to shell access, which is the sharpest line in the model: approving a graphing app is not the same as approving a terminal.
So "is Claude Code safe" has a precise answer. The design is sound: approval is per-application, sensitive applications are called out, and the agent is meant to respect that list on every action. The risk is not the design, it is whether the implementation holds the invariant on every code path. As this whitepaper shows, the interesting failures happen when an approved action continues past its approved recipient. Anthropic runs a vulnerability disclosure program precisely to find these, and the case study below came through it.
Why Computer-Use Agents Create New Security Risks
If you have shipped a chatbot, agentic AI security will surprise you. These are the pain points that make it a different discipline.
The agent has real hands. It runs commands, drives a browser, and edits files. A single mistake is no longer a bad answer, it is an executed action with the user's privileges.
Authorization spans layers. A high-level policy authorizes an action, then delegates to a native executor that actually delivers the input. If the authorized identity does not travel with the operation, the executor cannot preserve the decision.
State changes mid-action. Focus moves, applications quit, windows close, sandboxes recycle. An action authorized at time T can be consumed at time T plus one, against a different target.
Untrusted input reaches privileged actions. Model input can be steered by prompt injection from a web page, a repository, a shared field, or on-screen content. The agent may then choose a sequence the user never intended.
Fixes are hard to review completely. The same security requirement is often implemented in several sibling code paths. Patching one and missing another leaves the boundary open.
Every case study in this whitepaper is one of these pain points made concrete.
The Application Approval Boundary in Computer-Use Tools
The boundary that computer-use tools must hold is short to state:
Approval is meaningful only if it stays bound to every event an action produces. A click can change which application is frontmost. A keystroke is then delivered to that new application. Both the click target and the new frontmost recipient must independently satisfy the session grant, every time, for the life of the action. When an action produces many events (typing a string, holding a key, pasting, dragging), the invariant has to hold across the whole sequence, not just at the start.
What is a TOCTOU (Time-of-Check to Time-of-Use) Vulnerability?
A TOCTOU vulnerability, short for time-of-check to time-of-use and catalogued as CWE-367, is a bug where a program checks a condition at one moment and then acts on it at a later moment, after the condition may have changed. The classic example is a file whose permissions are checked and then used, with an attacker swapping the file in between. The check passes, but the use lands somewhere else.
Computer-use authorization is a TOCTOU problem in disguise. The agent checks that the approved application is in front, then uses that approval to deliver input. If focus changes between the check and the delivery, the same approval now describes the wrong recipient:
Nothing about the tool call looks malicious. The gap is entirely in timing and identity. That is why a purely functional review often misses it: each function does its job, but the object the decision was about has changed underneath it.
What Is an Incomplete Security Fix or Patch Bypass?
An incomplete fix, also called a patch bypass, is when a vendor corrects a vulnerability in the specific code that was reported, while the same underlying weakness remains reachable through an adjacent path. The patch is genuinely correct where it was applied. The boundary is still open somewhere else.
Incomplete fixes are common for a structural reason: teams review and patch by function, but security boundaries live by invariant. If the rule "every native event must go to an approved recipient" is implemented separately in a type loop, a repeated-key loop, a hold_key executor, a paste path, and a drag path, then fixing type does not fix the rule. Only reviewing every implementation of the rule does. This is why incomplete-fix research is high value: following an invariant across sibling implementations reliably finds the paths a functional patch missed.
Case Study: The Claude Code hold_key Computer-Use Bypass
This is the condensed case study. The full technical writeup, with the reverse-engineered control flow, the reproduction matrix, and the disclosure detail, is the primary source: Claude Code Computer Use: A hold_key Bypass That Ran a Command in an Unapproved Terminal.
The setup
Anthropic had already fixed one path to the terminal boundary. An earlier finding showed that a multi-event type action, approved while Apple's Grapher was frontmost, could keep sending keystrokes into an unapproved iTerm after Grapher quit. Anthropic fixed it in Claude Code 2.1.243 by rechecking the recipient inside the type and repeated-key loops.
The incomplete fix
The same invariant was not applied to the sibling hold_key action. In Claude Code 2.1.252, hold_key checked the approved frontmost application once before delivery, then handed the whole +-separated component sequence to a native macOS executor that pressed each key without rechecking the recipient. The high-level policy authorized Grapher correctly; the native executor never received Grapher's identity, so it could not tell that focus had moved.
The chain
Researchers at CodeAnt AI used one composite hold_key action that began on Grapher's Save-and-Quit sheet. The first Return saved and quit Grapher; while the same call was still delivering, macOS returned focus to a blank iTerm prompt; later components typed touch z and pressed Return, and the shell executed it. Only Grapher was ever approved. A direct invocation of the same sequence while iTerm was frontmost was correctly rejected as app_not_granted, and matched controls that kept Grapher alive left the marker absent. That contrast is the proof: the application transition, not the payload, unlocked the protected sink.
The impact
A prompt-injected or otherwise model-controlled sequence could use this to run commands under the user's normal host identity, exposing readable information, modifying source or configuration, starting processes, or making network requests. Anthropic confirmed the root cause through code review, rated it High (CVSS 4.0 score 7.7), and awarded a bounty. It is an authorization-boundary bypass, not unconditional remote code execution: Computer Use had to be enabled, an ordinary app approved, and an unapproved terminal had to receive focus after that app exited.
Anatomy: Where Each Action Checked the Recipient
The whole bug fits in one row of a table. The fix landed in two of these paths and not the third.
Action | Where Recipient Identity Was Checked | Result After Focus Changed |
|---|---|---|
| Before each emitted grapheme or control key | Remaining input stopped (fixed in 2.1.243) |
repeated | Before each repeated key event | Remaining input stopped (fixed in 2.1.243) |
| Once before the complete sequence entered the executor | Later components continued into an unapproved app |
The lesson generalizes to any multi-event action: type, repeated key, hold_key, batches, paste paths, and drag sequences all need the same per-event recipient check.
The Bigger Picture: Prompt Injection and Authorization in AI Agents
The computer-use boundary is one face of a larger problem. The other face is prompt injection, ranked LLM01 in the OWASP Top 10 for Large Language Model Applications. Prompt injection is when attacker-controlled text enters the model's context and changes what it does. In an agent, that means the model can be steered into choosing an action, and if the action then crosses an authorization boundary, the injection becomes an executed command.
The two problems compose. Prompt injection decides what the agent tries to do; the authorization boundary decides whether it is allowed to do it against a given target. A computer-use bug like the hold_key bypass is the second half of that chain: even without a novel injection, a model-controlled sequence reached a protected sink. In a separate finding, the CodeAnt AI Security Research Team showed the first half on a different product, where a shared-workspace instruction in the agentic assistant Manus loaded into another user's context as trusted configuration and led to a full desktop takeover (read that case study). Different products, same underlying question: whose input reaches a privileged action, and who decided it was trusted.
How the CodeAnt Team Found and Reported This
Incomplete-fix research is method, not luck. Rather than re-testing the patched type path, we followed the invariant, "every native event must reach only an approved recipient," across every sibling action that emits multiple events. Reverse engineering the signed Claude Code 2.1.252 runtime showed that hold_key captured the authorized application, performed one focus-continuity check, and then delegated the full component sequence to a native executor that received no application identity. That single missing hand-off was the bug.
The proof was built to be unambiguous rather than dramatic. It did not race a user or an external process for focus. The authorized operation itself completed a stock application's quit sequence, which made the identity transition deterministic and repeatable, and it paired the exploit with two negative controls, a direct-terminal denial and a no-transition run, so the evidence showed exactly which condition unlocked the sink. We reported it to Anthropic through their disclosure program; they confirmed the root cause by code review, rated it High at CVSS 7.7, and awarded a bounty.
How to Secure AI Coding Agents and Computer-Use Tools: A Developer Checklist
Whether you are building an agent or auditing one, these are the controls that break this class of bug.
Carry the authorized identity into every layer. The native executor that actually delivers input must receive the approved application's bundle identifier and, ideally, its process and window identity. Security metadata has to travel with the operation through every layer that can change its meaning.
Revalidate the recipient before every event. Each keystroke, click, or component must confirm that the intended application is still the actual recipient. Abort and safely release any held keys the moment identity changes.
Bind to a strong object, not a display name. A bundle identifier alone may not distinguish process replacement or window transitions. Retain process identity and the accessibility target where possible.
Review fixes by invariant, not by function. When you patch one path, enumerate every sibling implementation of the same rule (
type,key,hold_key, batches, paste, drag) and confirm each holds it. This is how you avoid shipping an incomplete fix.Constrain capability and egress. Give the agent least privilege, gate irreversible or cross-boundary actions behind explicit consent, and allowlist outbound destinations so a steered agent cannot reach an arbitrary sink.
Treat model-influenced input as untrusted. Assume prompt injection is possible from any content the agent reads, and make sure a chosen action still cannot cross a boundary the user did not approve.
Test the boundary, not just the payload. Use deterministic lifecycle transitions (an app quitting, a window closing, a sandbox recycling) and pair every exploit with negative controls that prove the boundary, not the command, was the deciding factor.
Automate the review. This is where CodeAnt helps: automated security review that flags where untrusted or model-influenced input reaches a privileged action, and where an authorization decision is not preserved across delegation, before it ships.
Related CodeAnt Research: Claude Code and AI Agent Security
This finding is part of a series of trust-boundary audits of agentic development tools by the CodeAnt AI Security Research Team:
The earlier Computer Use recipient-validation bypass (the
typepath fixed in 2.1.243), which thishold_keyfinding is the incomplete fix of.A Finder authorization bypass in Claude Code Computer Use, where Finder exemptions let an unapproved sequence reach Terminal.
A parent-directory TOCTOU in Claude Code's file writer on macOS.
An event-identity rebinding flaw in Claude Code Action for GitHub.
A stored prompt injection to desktop takeover on Manus, reported via Meta's bug bounty.
Together they point to one thesis: in agentic tools, the boundary between untrusted input and a privileged action is where the critical bugs live.
Key Takeaways for Developers and Security Researchers
The core boundary in computer-use tools is per-event recipient identity, not per-call approval.
Most of these failures are TOCTOU (CWE-367): checked against one recipient, consumed by another after focus changes.
Many are incomplete fixes: correct in the patched function, still open in a sibling path. Review by invariant.
Prompt injection (OWASP LLM01) supplies the intent; the authorization boundary decides the outcome. Secure both.
The durable rule: authorization must be checked against the application receiving each native event, including events from composite actions and delegated executor loops.
Conclusion
Agentic AI is only as safe as the weakest boundary between a model-chosen action and a privileged sink. The Claude Code hold_key case shows how narrow that gap can be: a patch that correctly fixed one path, a sibling path that implemented the same rule differently, and one approved action that continued into an application the user never approved. The fix is not a cleverer classifier. It is discipline: carry the authorized identity everywhere, revalidate the recipient on every event, and review security fixes by the invariant they are meant to hold.
If you are building or shipping an AI coding agent, that boundary is where your next critical bug is hiding. CodeAnt AI reviews the trust boundaries in agent and application code automatically, catching the place where untrusted or model-influenced input reaches a privileged action before it reaches production. Book a demo, or reach the research team at securityresearch@codeant.ai.
References:
OWASP Top 10 for Large Language Model Applications: LLM01 Prompt Injection
CWE-367: Time-of-check Time-of-use Race Condition · CWE-863: Incorrect Authorization
Primary source (full technical writeup): Claude Code Computer Use: A hold_key Bypass
This blog accompanies original research by CodeAnt AI Security Research. We are building an agentic tool and if you want its authorization boundary reviewed, feel free to reach us at securityresearch@codeant.ai.
You can even book your demo here and get in touch with the team directly.


