AI Agent Security: What SalesBleed and the AgentCore Leak Teach About Agents in Production
Two disclosures in September 2026 hijacked agents from Salesforce and AWS with nothing more than text in a web form and a support ticket. Neither needed a software bug in the usual sense. Here is what they had in common, and the controls that would have stopped them.
Oct 16, 2026
AI agent security is about one thing above all: an agent that reads untrusted text, such as an email, a web form or a support ticket, can be steered by instructions hidden in it, and then acts with the agent's own permissions. Two disclosures in September 2026 showed how far that goes in mainstream platforms. In SalesBleed, a lead submitted through a public Salesforce web form made Agentforce leak account data. Against AWS AgentCore, a support ticket made an agent dump its own credentials. Neither required a software bug in the usual sense. Both needed the same three ingredients: untrusted input, broad default access, and a way out. Below: what happened, the three ingredients, and the controls that remove them. We build and run agents and MCP servers in production, so the last section is what we do and what we don't do yet.
SalesBleed: a web form that read your CRM
From Zenity Labs, published 24 September 2026:
- An attacker submits a lead through a company's public Web-to-Lead form, with a prompt injection hidden in one field. No account needed.
- An employee asks their Agentforce agent a routine question: review my newest leads.
- The agent reads the malicious lead and follows it. Using the Query Records tool that the default CRM subagent can access, it reads the Accounts table too.
- It encodes the values into the subdomain of an attacker's hostname and outputs it as an image tag. Rendering the image triggers a DNS lookup, so the data leaves even if web traffic is blocked. Slack link unfurling worked the same way.
- Salesforce's Trusted URLs redaction missed it, because the redactor and the renderer disagreed on where the URL ended.
The proof of concept pulled company names and deal sizes. Zenity reported it on 1 June, and Salesforce confirmed fixes on 18 August. The pattern did not depend on Salesforce: any agent that reads public input and can query private data has the same shape.
AgentCore: a support ticket that read the agent's memory
From Palo Alto Networks Unit 42, published 18 September 2026:
- A support ticket contained a hidden instruction to download a script and run it.
- The AgentCore Harness shell tool, enabled by default and running as root, shared memory with the runtime that resolves credentials. The script read the runtime's memory through
/procand found a bearer token and the MCP server's URL. - From a laptop with no AWS credentials, the researchers replayed the token against the MCP server, listed its tools, looked up customer PII, and created a ticket.
- The token belonged to the operator's service account, not the end user.
AWS closed the report as informative under its shared responsibility model, pointing to tool scoping and egress filtering as the customer's controls. That is a reasonable position, and it is the point: the defaults are yours to change.
The three ingredients
Every agent attack disclosed this year that we have read combines three things:
- Untrusted input. The agent reads text written by someone outside: a lead, a ticket, an email, a web page, a document.
- Broad access. The agent can reach far more than the task needs, often through a default role or a service account that sees everything.
- A way out. Somewhere the data can go: an outbound request, a rendered image, an unfurled link, a webhook.
Prompt injection itself is not reliably preventable at the model level today. Models resist many attempts and miss others. Remove any one of the three ingredients and the attack has nothing to steal or nowhere to send it.
Controls that work
Against broad access:
- The fewest tools that do the job. Turn off the defaults you don't use: the shell tool, the general-purpose query tool.
- Read and write separated, with unknown tools treated as writes.
- The asking user's permissions, not a service account's. Read the user's role from your own database on every call, so the agent can never see more than the person who asked could.
Against untrusted input:
- Treat what the agent reads as data, not instructions. Keep agents that read public input away from tools that query private data, or put a person between them.
- Know where your untrusted text enters: web forms, inbound email, tickets, scraped pages.
Against a way out:
- Block outbound traffic the agent does not need, including DNS where you can.
- Don't render images or unfurl links from agent output in places that fetch automatically.
- Don't rely on output redaction alone. SalesBleed beat it with a parsing disagreement.
After the fact:
- Make every change visible and reversible. A one-step undo catches what slips through. Why we prefer it to an approve button.
- Log tool calls: who, which tool, when, with which arguments.
What we do, and what we don't yet
In the MCP servers we build and run:
- Each tool declares whether it reads or writes, a tool that declares nothing is treated as a write, and a read-only token calling a write tool is refused before the tool runs.
- Roles come from the database on every call, not from the token. A user removed from an organization loses access on their next request.
- People sign in with their own account through OAuth, so the agent acts as them, not as a service account.
- Writes we put in front of users come with undo.
What we don't do yet: we do not keep a tamper-evident log of every tool call with its arguments, and the older API keys some of our integrations use are broader than our OAuth scopes. Both are on the list, and we say so on our MCP security write-up.
Related: MCP security in 2026, Agentforce pricing and the Claude route, the LiteLLM vulnerability, we deleted the approve button, and when an AI agent deleted the production database.