---
title: "SalesBleed: Salesforce Patched the Same Agentforce Hole Twice"
pubDatetime: 2026-09-25T06:21:00.000Z
description: "Zenity Labs bypassed the Trusted URLs control Salesforce built a year ago to stop prompt injection in Agentforce, then used a Slack agent action to send phishing replies with no attribution. All three bugs are fixed."
tags: [ai-security, prompt-injection, ai-agents, vulnerability, agentforce, salesforce, vulnerability-disclosure, 2026, 2026-q3, 2026-09]
---
> [!tldr] TL;DR
> Zenity Labs published SalesBleed yesterday: three flaws in Salesforce Agentforce that let an attacker poison a public Web-to-Lead form and hijack an internal user's agent with no click required. One of them bypasses Trusted URLs, the exact allowlist control Salesforce shipped a year ago to fix a near-identical bug called ForcedLeak. A second flaw let the agent post phishing replies into Slack threads with no confirmation prompt and no trace of who asked. Zenity reported everything on 1 June 2026 and Salesforce had all three patched and independently verified by 21 September. If you run Agentforce with Web-to-Lead, the patches are in, but the pattern deserves more attention than the bugs.

Noma Security disclosed ForcedLeak exactly one year ago today: a prompt injection planted in a Salesforce Web-to-Lead form, picked up later by an Agentforce agent, used to pull CRM data out to an attacker-controlled domain. Salesforce fixed it with Trusted URLs Enforcement, an allowlist that redacts links the agent tries to reach. Nine months later Zenity Labs walked through it.

[SalesBleed](https://labs.zenity.io/post/salesbleed-0-click-data-exfiltration-on-agentforce) is the result, two Zenity Labs write-ups and a proof-of-concept video published on 24 September. Same product, same entry point, same control defeated.

## The Trusted URLs bypass came down to string parsing

The exfiltration chain starts where ForcedLeak's did. An attacker submits a lead through a public Web-to-Lead form with injected instructions in a free-text field. Nobody has to click anything. When an internal sales user later asks their Agentforce agent about leads, the agent reads the poisoned record and follows the planted instructions, reaching for Account data it has legitimate read access to.

Getting that data out means reaching an attacker's server, which Trusted URLs is supposed to block. Zenity found two ways past it. The allowlist checker did not recognise the `.fun` TLD as a domain at all, so a `.fun` address sailed through unredacted. Separately, the checker and the renderer disagreed about how to parse braces and brackets, and that mismatch was enough to smuggle a URL past the redaction. From there the callback is an image tag or a DNS lookup, or Slack's own link unfurling fetching the URL on the attacker's behalf.

That last detail is worth sitting with. The exfiltration does not need the user to click a link. The chat surface fetches it to render a preview.

## The Slack phishing bug was a missing dialog box

The [second Zenity report](https://labs.zenity.io/post/salesbleed-hijacking-agentforce-in-slack-for-anonymous-phishing) covers something less exotic and arguably worse. Agentforce's "Reply to a Slack Thread" action fired without asking the user to confirm, and the resulting message carried no attribution back to whoever, or whatever, triggered it.

Chain that to the injection above and an external attacker gets an internal Slack account posting on their behalf. The message arrives in a real thread, inside the trust boundary, wearing the agent's identity. Anyone reading it sees a colleague's tooling, not a lead form submission from a stranger. There is no novel jailbreak here and no clever token smuggling, just an agent action with write access to a human communication channel and no consent step in front of it.

Zenity confirmed the attribution fix on 20 August 2026 and the user-confirmation fix on 21 September.

## Allowlisting URLs is a losing control for agent output

Here is the structural problem. Trusted URLs is a filter applied to strings produced by a probabilistic model that an attacker partly controls. Every such filter is a parser, every parser has edge cases, and the attacker gets unlimited attempts to find one. Zenity needed two: an unknown TLD and a bracket-handling disagreement. Neither is deep magic.

Point-fixing each bypass leaves the design intact. Salesforce patched ForcedLeak's specific escape in September 2025, and the next researcher to look at the same surface found new ones in under a year. The control that would actually hold is narrowing what the agent can reach and what it can do without a human in the loop, which is precisely what the Slack confirmation dialog does and what the URL allowlist does not.

## Disclosure, credit where it is due

The counter-case is real, and I mostly buy it as far as it goes. Zenity reported privately on 1 June. Salesforce confirmed it was working on fixes the next day, held a technical call on 16 June, and started fix development on 17 June. The data-exfiltration fix landed on 18 August, Zenity independently verified the Trusted URLs hardening on 19 August, and the last of the three was confirmed on 21 September. Nothing went public until all of it was verified.

That is a clean process by any standard, and about 3.5 months from report to full public write-up on three bugs is respectable. Whether either flaw was exploited before the fix is unknown; no source I have says either way, so treat "no evidence of exploitation" as unestablished rather than reassuring.

Scope matters too. Both attack chains need the organisation to have Web-to-Lead enabled and to run the default General CRM subagent configuration with read access to Leads and Accounts. That is a common setup, not a universal one, so "every Salesforce customer was exposed" overstates it. Zenity's disclosure mentions no CVE IDs and no CVSS score, which makes SalesBleed harder to track than ForcedLeak, which carried a researcher-assigned CVSS 4.0 rating of 9.4.

[Salesforce told The Register](https://www.theregister.com/2025/09/26/salesforce_agentforce_forceleak_attack/) last year that prompt injection "remains a complex and evolving area." A year and one bypass later, that still reads as accurate.

---

*Sources: [SalesBleed: 0-Click Data Exfiltration on Agentforce](https://labs.zenity.io/post/salesbleed-0-click-data-exfiltration-on-agentforce)  •  [SalesBleed: Hijacking Agentforce in Slack](https://labs.zenity.io/post/salesbleed-hijacking-agentforce-in-slack-for-anonymous-phishing)  •  [ForcedLeak flaw exposes CRM data](https://securityaffairs.com/182676/hacking/forcedleak-flaw-in-salesforce-agentforce-exposes-crm-data-via-prompt-injection.html)*

*Coverage: [The Register on SalesBleed](https://www.theregister.com/security/2026/09/24/salesforce-agentforce-vulns-allowed-0-click-crm-data-theft-anonymous-phishing/5298958)  •  [Dark Reading on SalesBleed](https://www.darkreading.com/application-security/salesbleed-exploits-salesforce-agents-slack-phishing)  •  [The Register on ForcedLeak](https://www.theregister.com/2025/09/26/salesforce_agentforce_forceleak_attack/)*

*Related on this blog: [The diffusers trust_remote_code Bypass Was Patched in May](/posts/diffusers-trust-remote-code-bypass)  •  [Google Patched Its Agent-to-Agent Privilege Bug, Then Declined to Pay for It](/posts/google-adk-agent-privilege-escalation)  •  [[AUTO] Self-Propagating Prompts Infect AI Agents](/posts/auto-agent-prompt-infection)*