SalesBleed, and the agent that already had the permissions

Someone typed instructions into a Salesforce web form. When an employee asked the company’s AI agent about new leads, it followed them and sent account data to a stranger. It never needed more access than the employee already had.

Note · Sep 2026

On 24 September, Zenity Labs published three flaws in Salesforce Agentforce, which it calls SalesBleed. Two of them let an outsider take CRM data without anyone clicking anything. The third let the agent post messages into Slack with no sign of who had asked it to.

Salesforce has fixed all three. Zenity reported them on 1 June, and by its own account Salesforce confirmed the next day and worked with the researchers until the last fix was confirmed on 21 September. The disclosure went the way a disclosure should.

It’s still worth taking apart. Salesforce fixed the parts that got the data out. The parts that let the instructions in, and set how much there was to take, are just how the product works, and they’re still there.

A lead that gives instructions

Salesforce’s Web-to-Lead form lets a company put a lead-capture form on its public website. Anyone on the internet can fill it in, and the submission lands in the CRM as a lead record. That is the point of the feature.

Zenity’s researchers, playing the attacker, filled it in with instructions instead of a sales enquiry. They are hidden in the lead, addressed to whatever AI eventually reads it.

Later, an employee asks Agentforce about recent leads, which is also the point of the feature. According to Zenity’s write-up, the agent’s default General CRM subagent read the poisoned lead and followed it. It used its Query Records tool to read the Accounts object, pulled company names and deal sizes, and wrote them into a web address pointing at the attacker’s server.

The agent was using the employee’s own permissions, and they covered both leads and accounts. Zenity’s summary of that step is the most important sentence in the research: “the injection didn’t need to escalate privileges, the permissions were already there.”

Getting the data out

Getting the data into a web address is only half of it. Something has to fetch that address.

Salesforce already had a control for this. Trusted URLs restricts which outside destinations Agentforce may reach, and strips links and images that point anywhere else from the agent’s answer.

Zenity found two edge cases in it. The filter recognised only a fixed list of top-level domains, and .fun was not on it. It also disagreed with the browser about where a web address ends: curly braces and square brackets went through the filter untouched, and the browser still treated them as part of the address.

Together, those let the agent return an image whose address carried the stolen data. The employee’s screen tried to load the image, and the request went to the attacker.

The second method needed no image at all. When the agent is deployed in Slack, Slack builds a preview for any link that appears in a message, and to build it Slack fetches the link. As Zenity puts it, “Slack automatically turns raw URLs into previews, and to build a preview it just crawls a link.” The agent only had to print the address. Slack made the request on its behalf.

Messages with no name on them

The third flaw, in a second write-up, is quieter and more human.

Agentforce has a Reply to a Slack Thread action. Similar actions, such as Send a Slack Direct Message, ask the user to confirm before sending, and name the person who invoked them. This one did neither. Messages sent through it showed only the company’s agent as the sender.

That serves two kinds of attacker.

An employee can ask the agent to post a phishing link into a thread and stay anonymous. An outsider can plant the same request in a web-form lead and wait for a colleague to ask about leads. Either way the link arrives from a trusted internal agent, and a markdown link hides where it really points.

Zenity confirmed on 20 August that the action now names the person who invoked it, and confirmed the remaining fixes on 21 September. It also notes that the confirmation step can be switched off again with a single click.

ForcedLeak did this a year earlier

This has happened before. In September 2025, Noma Security disclosed ForcedLeak, rated CVSS 9.4. It went in through the same Web-to-Lead form, using a description field that Noma measured at 42,000 characters, and exfiltrated through an image address as well.

ForcedLeak sent the data to a domain that had once been on Salesforce’s allowlist and had since expired. Noma bought it for about five dollars. Noma’s timeline records Salesforce’s fix as Trusted URLs Enforcement for Agentforce and Einstein AI, on 8 September 2025. SalesBleed’s first flaw was a way round that same control.

Two research teams, a year apart, started with the same web form and got the data out in different ways. Each patch fixed how the data got out. The form is still there, because it’s the product: an agent built to read what the outside world sends a company, with the same access as the employee who asked.

Why it keeps happening

Look at what Salesforce was actually asked. Could this employee read these accounts? Yes. Was the agent signed in as that employee? Yes. Could the employee have run the same search themselves? Also yes.

The real question was narrower. Should this request, made on this person’s behalf, triggered by text a stranger typed into a web form, read the company’s deal sizes? Salesforce knew who was asking and what they were allowed to see. It had no way to know why.

Zenity’s CTO, Michael Bargury, told The Register that “the idea of secure-by-design remains essential but for agents it may no longer be enough.” Salesforce fixed the filters. How much could leak still comes down to what each employee can see.

What’s new here

Ignore the individual bugs for a moment and look at where each step moved from one system to another.

Anything a stranger can type, an agent can read. For years a lead form was a place to collect email addresses. Now whatever someone types into it can end up as instructions to an agent that can read the CRM.

Link previews make requests. Image rendering and link previews exist to be helpful, and both fetch an address the agent chose. In Slack the agent didn’t even need an image. Printing the link was enough, because Slack fetched it itself.

The agent is a trusted sender. A message from an internal agent carries the company’s authority and, until August, carried no name. Staff are trained to be wary of unknown senders. A message from the company’s own agent doesn’t look like one.

Connected apps are targets too. In August 2025, Google’s Threat Intelligence Group reported that an actor it tracks as UNC6395 used stolen OAuth tokens from Salesloft Drift, a chat app connected to Salesforce, to export accounts, opportunities, users and cases from what GTIG called “numerous corporate Salesforce instances”.

GTIG saw the actor going after AWS access keys, passwords and Snowflake tokens in what it took. Same CRM, a different attack, and the credentials it was after would have given access to other systems.

Each of these is a gap between two systems: the CRM and the internet, the CRM and Slack, a vendor’s app and the CRM. Salesforce looks after what’s in Salesforce, and Slack looks after Slack. Once an agent carries data from one to the other, it’s outside both.

What you can know before the next one

Prompt injection will not be solved by the next patch. OpenAI, writing about its own browser agent in December 2025, called it “unlikely to ever be fully ‘solved’”.

What you can know, ahead of time, is how much an agent could take when it is hijacked. The size of that answer is decided by permissions granted years ago, spread across every system the agent is connected to.

For SalesBleed, the answer was every account the employee could see. Ask the same question of Salesforce, SharePoint, the warehouse and Slack together, for any one person, and most companies would have to go and find out.

That is what our Agent Readiness Scan is for. Read-only connectors pull the permissions your systems already hold, and we reconcile them into one map of what an agent acting for each person could reach, ranked worst first. It would not have stopped a poisoned lead. It tells you what that lead could have taken.

What could your agents reach for the person asking?

Contact sales

Fill out the form and we will be in touch.

Start with a scan

Ready to find out what your agents can reach before somebody else does?

Tell us a bit about your setup and we will be in touch.