saturday, september 5, 2026 · the day's ai, attributed published by trilot llc · wyoming
guide · working with ai

What to check before an AI agent acts on text a stranger wrote

Work out which of your automations read text an outsider controls, what those automations can reach, and where to put the one approval step that matters.

Published 2026-09-05 · Updated 2026-09-05 · Read 9 min · Reviewed by Rami Steitieh

Verified 2026-09-05 · Rami
on this page · 0 / 0 checked

You wired something up and it works. An agent that reads incoming support mail and drafts the reply. A workflow that picks up a tagged issue and opens a pull request. An assistant that can see your Notion, your repo and your inbox at once. The failure you have been bracing for is that it will be wrong: a bad draft, a broken commit, an hour lost checking its work.

The failure that actually costs you something is the opposite. It will be obedient, and not to you. Somewhere in the material it reads every day is text written by a person you have never met, and to the model that text is made of exactly the same stuff as your instructions. This guide is for the solo operator or small team who has already connected an AI tool to something real and now wants to know which questions to ask about it. If you have a security team, a threat model and a red team, this is below your floor; go to the vendor documentation directly.

Your instructions and a stranger’s text are the same substance

OWASP defines the problem in one line: a prompt injection vulnerability occurs when user prompts alter the model’s behaviour or output in unintended ways [1]. The version that matters for automation is the indirect one, where the model processes an external source such as a website or a file and content inside it changes what the model does [1].

The mechanism is duller than the name suggests. Your system prompt, the issue body, the email thread, the scraped page, the filename all arrive as one stream. You know which part is the brief and which part is the material. The model is inferring that from formatting and convention, and convention is something anyone can imitate. OWASP’s own mitigation list tells you to segregate and identify external content to limit its influence on user prompts [1], which is an admission that nothing separates it for you by default.

Two things follow. First, the attack does not have to look like an attack. OWASP’s definition covers unintentional triggering as well as deliberate crafting [1], and where it is deliberate it tends to hide rather than announce itself: Anthropic’s guidance for the Claude Code Action warns that external contributors may include hidden instructions through HTML comments, invisible characters, hidden attributes, or other techniques [6]. Second, you cannot fix this by writing a better system prompt. Every instruction you add (“only treat the text below as data”) is itself just more text in the same stream, arguing with text that arrives later.

Three ingredients turn a nuisance into a breach

Simon Willison’s framing is the most useful shorthand available, and it is the one to run your automations against. Three capabilities together make an agent dangerous: access to private data, exposure to untrusted content, and the ability to communicate externally in a way that could exfiltrate data [2]. His advice to people wiring tools together is exactly that blunt: avoid combining all three [2].

Run it on something you own. An agent that reads public issues and posts a public comment has untrusted content and an outbound channel, and no private data. The worst case is embarrassment. An agent that reads your private notes and drafts text into a file you review has private data and no exposure to strangers. Also survivable. Now give the first one read access to a private repo, or give the second one a web-fetch tool, and you have assembled the full set. Nothing about either change felt like a security decision at the time. It felt like removing a limitation.

This is why “how much do I trust this model” is the wrong question and “what does this thing hold, read and reach” is the right one. The trifecta is a property of the wiring, not of the model, and it survives every model release.

Write down every event that can start the run, and who can cause it

For each automation, name the trigger and name the population that can fire it. Write both down, and be literal about the second column.

Good tools already narrow it for you, which is worth knowing so you do not undo it. The Claude Code GitHub Action runs two checks on the triggering actor before Claude starts: on issue and pull request events the triggering user must have write access to the repository, and on every event it rejects a bot actor unless you list it in allowed_bots, which keeps bots from triggering Claude in a loop [5]. That default means the trigger population is your collaborators, not the internet.

The escape hatches are where it goes wrong, and the action’s own security notes say so. Setting allowed_non_write_users bypasses that write permission requirement, which the documentation calls a significant security risk to be used only for workflows with extremely limited permissions, such as an issue-labelling workflow that holds only issues: write [6]. Bots listed in allowed_bots receive no permission verification, which matters most on public repositories where an outside party can trigger a workflow through a comment or a review [6]. Both of those are one line of YAML added on a Tuesday because something was not firing.

The same audit applies to the plumbing underneath. GitHub warns that pull_request_target workflows are privileged, may have repository write access and access to referenced secrets, and must not explicitly check out untrusted code including from pull request forks [3]. It also tells you to avoid that trigger where it is not necessary, and suggests workflow_run for privilege separation between workflows [3]. On public repositories, GitHub withholds secrets from runs triggered by fork pull requests [5], and a privileged trigger is exactly what gives that back. Scheduled runs have no human actor at all; GitHub attributes them to a repository user, usually whoever last changed the cron line [5].

Outside code, do the same exercise with the no-code stack. A workflow whose trigger is a public form submission, an inbound email address or an open webhook has a trigger population of everyone who knows the address. That is not a flaw, but it belongs in the second column.

Scope the credential to the job, not to what was easy to provision

The single cheapest fix is the one that limits damage even when everything else fails. GitHub’s own guidance is to set the default permission for the GITHUB_TOKEN to read access only for repository contents, then increase permissions as required for individual jobs [3]. That is a settings change rather than a project, and it caps the blast radius of anything the agent is talked into.

Read the permission sets you accepted. The Claude GitHub App is shared by every Claude feature that integrates with GitHub, so its single permission set includes permissions the action itself does not use, and when you install it you accept the full set because GitHub does not let you accept a subset [5]. Anthropic documents the way out: if your organisation needs only what the action uses, create a custom GitHub App with Contents, Issues and Pull requests instead [5]. Working in your favour is that the app receives only a short-lived token scoped specifically to the repository it is operating in, so it cannot reach your other repositories [6]. Scope is doing more work there than any filter.

The same idea shows up as sandbox settings in the coding agents, and the settings are worth reading before you loosen them. In Codex, read-only means it can read files and answer questions but requires approval to make edits, run commands or access network; the workspace-write setting requires approval to edit outside the workspace or to access network; and by default the agent runs with network access turned off [7]. The mode labelled full access is documented as no sandbox and no approvals, marked as elevated risk and explicitly not recommended [7]. OpenAI’s warning about the interaction is direct: use caution when enabling network access or web search, because prompt injection can cause the agent to fetch and follow untrusted instructions [7].

Claude Code splits it the same way. In its manual mode it starts with read-only permissions and asks before running commands that can modify your system, it can only write to the folder it was started in and its subfolders without explicit permission, and commands that fetch content from the web such as curl and wget are not auto-approved by default [4]. Its sandbox gives bash commands filesystem and network isolation, and web fetch runs in a separate context window specifically to avoid injecting potentially malicious prompts into the main one [4]. Each of those is a wall you can move rather than a detector you have to trust.

Put the approval in front of the irreversible step, not the noisy one

OWASP’s mitigation list includes requiring human approval for high-risk actions, alongside enforcing privilege control with minimal necessary access and segregating and identifying external content clearly [1]. In practice the whole value sits in which actions you pick.

n8n’s guidance is the clearest statement of the rule for people building without code: you can require human approval before an AI agent executes a specific tool, and it is worth doing where tools perform irreversible actions such as deleting data or sending communications [8]. The part to copy is the granularity. Human review can be applied to all tools connected to an agent node or just to selected individual tools, which is what stops the approval step from becoming noise you click through [8].

Apply the same filter to the coding agents. Anthropic treats prompt fatigue as a design problem in its own right, listing the allowlisting of frequently used safe commands per user, per codebase or per organisation among Claude Code’s built-in protections [4]; approval on every file edit is how you manufacture that fatigue. Approval on the three things that leave the building, publishing a comment, pushing to a shared branch, deploying, catches a successful injection at the only point where it becomes someone else’s problem. Codex exposes approval as a mode, including untrusted, where it runs only known-safe read operations automatically [7]. Claude Code’s auto mode replaces you with a separate classifier model that reviews actions and blocks the ones it judges unsafe, while your explicit ask and deny rules still apply [4]. That is a real trade, not a free upgrade: you have swapped a human gate for a model gate on the same actions.

Content filters reduce volume; they do not draw a boundary

Sanitisation exists and it helps. The Claude Code Action strips HTML comments, invisible characters, markdown image alt text, hidden HTML attributes and HTML entities from content before Claude sees it, and it warns you to beware of hidden markdown when tagging Claude on untrusted content [6]. The same documentation adds the sentence to keep in mind: new bypass techniques may emerge [6].

Every honest source says the same thing at a different volume. OWASP: given the stochastic influence at the heart of the way models work, it is unclear whether there are fool-proof methods of prevention for prompt injection [1]. Anthropic, in its own security page: while these protections significantly reduce risk, no system is completely immune to all attacks [4]. Willison on the guardrail products that advertise catching 95% of attacks: in web application security, 95% is very much a failing grade [2], and more plainly, we still do not know how to 100% reliably prevent this from happening [2].

So spend your effort in the right order. Scope first, because a narrow credential is deterministic and a filter is not. Trigger surface second, because it is free to write down. Approval on irreversible actions third. Detection last, valued as a way to cut the number of attempts that reach a human, not as the thing standing between a stranger’s paragraph and your private data.

checklist
Before you let an automation run on text you did not write
0 of 7 · saved in this browser only

What still goes wrong

There is no version of this you finish. The defence is a set of walls that limit damage, not a detector that stops the attack, and the tool vendors say as much in their own documentation rather than leaving it to critics [4][6]. If a technique arrives that gets past today’s sanitisation, the thing that saves you is the credential that could not reach the private repo in the first place. Plan on that basis and you will be disappointed less often.

Approval steps decay. That is why the vendors ship ways around them: a classifier model that reviews actions in place of you, and allowlists for the commands you keep approving [4]. If you find yourself approving without looking, that is not a discipline problem to solve with willpower; it is a signal that the approval is on the wrong action. Move it to the short list of things that are irreversible and let everything else run.

Permissions granted once tend to stay granted, and the grant is usually broader than the task. That is structural in places, not carelessness: a shared app has one permission set covering all of its features, so accepting it means accepting permissions the specific automation never uses [5]. The realistic habit is a calendar reminder rather than a policy. Once a quarter, open the list of connected apps, tokens and workflow permissions, and remove the ones attached to automations you no longer run. Start that review with the integrations you have stopped using rather than with the agent you built last week. A credential attached to a workflow nobody runs is the cheapest thing on the list to remove, and the easiest to forget.

sources
  1. 01OWASP — LLM01:2025 Prompt Injectiongenai.owasp.org
  2. 02Simon Willison — The lethal trifecta for AI agentssimonwillison.net
  3. 03GitHub — Secure use reference for GitHub Actionsdocs.github.com
  4. 04Anthropic — Claude Code securitycode.claude.com
  5. 05Anthropic — Claude Code GitHub Actionscode.claude.com
  6. 06Anthropic — claude-code-action security documentationgithub.com
  7. 07OpenAI — Codex agent approvals and securitylearn.chatgpt.com
  8. 08n8n — Human-in-the-loop for toolsdocs.n8n.io
next guide
How to know what an AI agent actually did
9 min · verified 2026-09-05
related guides