Reading an approval prompt so it actually protects you
Agent approval prompts are software with their own failure modes; learn what a permission prompt can guarantee, what it cannot, and what to set instead.
on this page · 0 / 0 checked
You gave an AI agent the run of a project on the understanding that it stops and asks before anything consequential. That is the deal, and on paper it is a good one. The agent proposes, a box appears with the command in it, you read the command, you press enter. The standard advice arrives at the same place; OWASP’s guidance on prompt injection asks builders to “require human approval for high-risk actions” and to use “human-in-the-loop controls for privileged operations to prevent unauthorized actions” [6].
The trouble is that the loop is made of 2 things that both wear out. One is the prompt, which is a piece of software that renders text, and software that renders text has bugs. The other is you, several hours into a session, recognising the shape of a command rather than reading it. This guide is about what a permission prompt can honestly promise, where it stops promising, and which settings keep working on the days you are not really looking. It is not a security-engineering guide; if you build agent platforms, the interesting parts of this problem are below the level covered here.
The prompt is a rendering, not the command
What you approve is not the command. It is text that a tool generated from a proposed tool input, then handed to something else to draw on a screen. In a terminal that chain is short. When the approval is relayed somewhere else, to a chat channel or a phone notification, it gets longer, and every hop is a place where the drawn text can drift from the bytes that will execute.
That is not a theoretical concern. Claude Code’s release notes for v2.1.211 record the fix plainly: “Fixed permission previews relayed to chat channels not neutralizing bidirectional-override, zero-width, and look-alike quote characters, so tool inputs cannot visually alter the approval message” [3]. Read the clause at the end as the design goal it states. Until that release, a tool input could visually alter the approval message. The model did not need to be tricked. The person did.
The character tricks involved are old and well documented outside AI entirely. Unicode Technical Standard #55, the standard for handling Unicode in source code, names 3 spoofing mechanisms directly: line break spoofing, where “multiple logical lines can be displayed as a single line, or a single logical line can be displayed as multiple lines”; lookalike glyphs, where characters from different scripts are visually indistinguishable; and bidirectional reordering, where invisible directional characters change the apparent order of what is written [5]. The standard’s advice to editors is worth borrowing as a personal habit: it recommends editors “provide an option to make visible any default ignorable code points”, and recommends a warning “when an identifier is confusable with some other relevant identifier or with a reserved word of the language” [5].
The durable lesson has nothing to do with one vendor or one release. Any place where untrusted text reaches a human who is about to make a trust decision is a display-layer security problem, and the fix lives in the renderer, not in the reader’s eyesight.
The prompt only promises what it can show
Good approval UI knows the limits of its own display, and says so. Claude Code’s permission prompts have a rule most people never notice: sometimes the prompt offers a one-time approval only, with no “don’t ask again” option and no option to allow the action for the rest of the session. The documented reason is that Claude Code “offers those options only when the prompt can show you everything they would allow, so a rule you save from a prompt covers only what its option named” [1].
There are 3 cases where it drops the persistent options. The command or edit is too large to show in full. The label cannot fit all the commands or paths the rule would cover. Or the starting directory is too long and cannot be shortened, because “it contains characters Claude Code can’t display safely, or even its start doesn’t fit” [1]. That third case is the same family of characters as the relay bug, handled the other way round: rather than render something it cannot render honestly, the tool withholds the durable grant and makes you approve once.
So when a prompt only offers you a single yes, treat that as information rather than friction. It is the tool telling you it could not fully describe what a broader yes would mean.
The same principle explains a detail worth internalising for compound commands. Approving git status && npm test with “Yes, and don’t ask again” does not save a rule for that string. Claude Code saves a separate rule for each subcommand that needs approval, so it saves a rule for npm test, and future npm test calls are allowed regardless of what precedes the &&; up to 5 rules can be saved from one compound command [1]. What you granted is what the option named, not the line you happened to be looking at. And a command longer than 10,000 characters always prompts, because it exceeds what the command analysis parses [1], which is a reasonable proxy for “nobody is reading this anyway”.
The gate fails at volume, not at cleverness
Almost no approval you will ever see is an attack. That is precisely the problem. A control you exercise dozens of times a day, correctly, on benign input, stops being a control and becomes a keystroke. The character-spoofing case is memorable because it is vivid, but the ordinary failure is that the prompt was accurate and nobody read it.
The vendors have responded by removing prompts rather than by making people read more of them. Claude Code’s auto mode reached general availability on 10 July 2026, after a research preview that opened on 24 March 2026 [4]. Anthropic describes it as “a safer long-running alternative to --dangerously-skip-permissions”: a classifier reviews each tool call before execution, and “actions that the classifier deems as safe proceed automatically, and risky ones get blocked” [4]. On Pro, Max and Team plans, auto mode is now the built-in starting permission mode [2], which means anyone on those plans starts each session seeing far fewer prompts than their permission rules alone would suggest.
That is a reasonable trade, and it is worth knowing what it buys. The classifier blocks “anything that escalates beyond your request, targets unrecognized infrastructure, or appears driven by hostile content Claude read” [2]. It sees your messages, tool calls other than read-only lookups, and your CLAUDE.md; tool results are stripped, “so hostile content in a file or web page can’t manipulate it directly” [2]. If it blocks an action 3 times in a row or 20 times in a session, auto mode pauses and Claude Code goes back to prompting you [2]. Anthropic’s own framing is not triumphant: auto mode “reduces risk compared to —dangerously-skip-permissions” but “doesn’t eliminate it entirely”, and the recommendation is to run it in isolated environments [4]. Cursor puts the same point about its own run modes more bluntly, calling them “best-effort guardrails rather than a hard security boundary” [8].
prompts × seconds × 22 working days. If the answer looks like more time than you will spend, you are not reading them; set rules instead. Computed in the page; nothing is sent anywhere.
Run that number honestly. If reading every prompt properly would cost more hours per month than you are willing to give it, then reading is not your control, and pretending otherwise is worse than admitting it. The answer is to move the decision from the moment to the configuration.
Rules survive a bad reading; instructions do not
The single most useful line in Claude Code’s permission documentation is a note that sounds bureaucratic and is not: “Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or CLAUDE.md shape what Claude tries to do, but they don’t change what Claude Code allows” [1]. Anything you type into a conversation is a request. Anything in a rule is a constraint.
The mechanics are worth learning once. Rules are evaluated deny, then ask, then allow, and the first match in that order decides; specificity does not change the order, so a broad deny like Bash(aws *) blocks a call that also matches a narrower allow like Bash(aws s3 ls) [1]. Ask rules work the same way against allow rules, which makes them the right tool for the actions you want to keep a human on: an explicit ask rule is one of the things Claude Code does not auto-approve in any mode, including bypassPermissions [2]. Deny rules also apply in every mode including bypassPermissions, while allow rules have no effect there at all [2].
Compare that with the softer version. If you tell Claude “don’t push until I review”, the auto mode classifier does treat that as a block signal, and, usefully, “Claude’s own judgment that a condition was met does not lift it” [2]. But those boundaries are not stored as rules. The classifier re-reads them from the transcript on every check, so a boundary can vanish when context compaction removes the message that stated it. The documentation’s own advice is the right one: “For a hard guarantee, add a deny rule instead” [2].
This is the practical answer to everything above. You cannot make yourself read carefully on the fortieth prompt of the day. You can decide once, on a calm afternoon, which 3 or 4 operations in your setup are genuinely hard to undo, and write ask or deny rules for them. Deploying to production, pushing to a shared branch, sending mail, touching a payments API, deleting outside the working directory. Those get a rule. Everything else can flow.
Approve as close to the source as you can
The relay bug landed in previews relayed to chat channels for a reason [3]. A terminal you are watching shows you a tool call one hop from where it was produced. A Slack message, an email digest, a push notification or a custom approval bot is a re-rendering of a re-rendering, and the sanitising has to be right at every step. If you built the relay yourself with Zapier, n8n or Make, nothing in a vendor’s patch protects it; the question of whether your bot neutralises invisible and bidirectional characters before display is yours to answer.
The other vendors converge on the same instinct even in very different products. OpenAI says its agents are “often” designed “to get a final confirmation from you before taking certain consequential actions like completing a purchase or sending an email”, and for sensitive sites it runs a Watch Mode that “alerts you to the sensitive nature of the site and requires you have the tab active to watch the agent do its work” [7]. That last requirement is the interesting one: the control is not just a confirmation, it is a confirmation you have to be present for. Cursor’s default is the same shape, with terminal commands needing your approval unless you have explicitly configured a run mode that relaxes it [8].
There is a matching rule on the other side, which is that the agent must never be able to answer its own prompt. Claude Code’s classifier explicitly treats an attempt to send keystrokes to Claude Code’s own tmux pane as “Claude changing its own permissions or oversight” and blocks it [2]. If you are assembling your own automation around an agent, that is the boundary to copy: whatever surface renders the approval must be one the agent cannot reach.
What still goes wrong
None of this closes the underlying hole. OWASP’s own text on prompt injection concedes that “given the stochastic influence at the heart of the way models work, it is unclear if there are fool-proof methods of prevention” [6], and OpenAI describes prompt injection as “a frontier, challenging research problem” it expects to keep working on [7]. Human approval is a mitigation layered on an unsolved problem, and every guide that presents it as the answer is overselling it.
The specific weakness of an approval gate is that it consumes the one resource you cannot scale. Rules help, but rules are written by the same tired person, and a deny rule you wrote in March does not know about the API you started using in August. Auto mode’s classifier helps too, and it is honest about its blind spots: it never sees tool results, which is exactly what makes it hard to manipulate and also exactly what stops it from noticing something alarming inside a file the agent just read [2]. The 2 defences have complementary vision, which is better than one, and neither is complete.
Finally, the fix described in v2.1.211 [3] applies to one product. Every other place an agent’s proposed action gets re-rendered for a human, including anything you wired together yourself, has had whatever scrutiny it has had. The reasonable posture is not suspicion of every prompt; it is a short list of actions you have decided a human must genuinely stop for, enforced by a rule rather than by an intention, and reviewed somewhere close enough to the agent that there is little between the bytes and your eyes.
- 01Anthropic — Configure permissions (Claude Code)code.claude.com
- 02Anthropic — Choose a permission mode (Claude Code)code.claude.com
- 03Anthropic — Claude Code release v2.1.211github.com
- 04Anthropic — Auto mode for Claude Codeclaude.com
- 05Unicode Technical Standard #55 — Unicode Source Code Handlingunicode.org
- 06OWASP — LLM01:2025 Prompt Injectiongenai.owasp.org
- 07OpenAI — Understanding prompt injectionsopenai.com
- 08Cursor — Agent securitycursor.com