Method
It reads your code, follows the value, and reports the route.
How it works
Three questions, asked of your actual code.
Where untrusted input gets in
ExfilPath maps the surfaces an attacker can write to: inbound mail, retrieved documents, scraped pages, tool and MCP results, CRM and ticket fields, uploads, queues and end-user messages.
17 input surfaces across 10 categories
Which actions cause damage
Then it maps the actions that leak data or change systems: outbound requests, auto-fetched media in rendered output, mail and chat sends, code execution, data and file writes, cloud calls, payments and tool dispatch.
15 sensitive actions across 8 categories
Whether anything stands between them
Last, it looks for controls on the value itself: schema validation, destination allowlists, egress policy, authorisation, sandboxing and human approval. When nothing covers the value, that is a path.
9 control types, 5 model boundaries
The analysis
What happens between reading a file and reporting a path.
1. Parse and normalise
Files are split into logical statements so a call spread over eight lines is analysed as one thing. Comments are stripped without touching string contents, so a URL inside a string is never lost.
2. Mark untrusted values
When a statement reads a source, the variables it assigns become tainted, and the source is recorded with its trust level: external, partially trusted, or internal.
3. Follow the value
Taint moves through assignments, template strings, arrays and objects; into helper functions at the argument positions that actually receive it; and back out of functions that read a source internally. Cross-file routes are followed to a bounded depth.
4. Treat the model as a boundary
A model call does not clean anything. Text that goes in as data comes out as instructions, so the taint continues through the response and the path is marked as crossing the model.
5. Look for guards on the value
A guard only counts if it covers the value on the way to the action. Validation of a different variable does not close a path, and neither does a classifier that a payload can be reworded to pass.
6. Report and rank
Each path gets a full trace, a severity, a confidence score, an OWASP and CWE mapping, and the smallest change that closes it. Duplicate routes between the same two points collapse into one finding.
Guards
What counts as standing between them.
| Guard | Strength | Why |
|---|---|---|
| Schema validation | strong | Forces the value into a known shape before it is used. |
| Destination allowlist | strong | Only pre-approved destinations are reachable, so exfiltration has nowhere to go. |
| Prompt-injection screening | partial | Classifiers reduce volume but are bypassable — EchoLeak walked through one. Treated as partial. |
| Human approval gate | strong | A person authorises the action, so a payload alone cannot complete it. |
| Sandboxed execution | strong | Execution is contained, so code execution does not reach the host or its credentials. |
| Authorization check | strong | The action is bound to a caller identity and permission, not to whatever the payload asks for. |
| Egress policy / URL validation | strong | The destination is checked before the request leaves. |
| Output encoding / sanitisation | partial | Neutralises rendered content. Strong for HTML, partial for free-form model output. |
| Parameterised query | strong | Values cannot change the statement structure. |
Severity
How a path gets its rating.
| Impact of the action | Code execution, egress, data mutation and cloud calls start critical. Messaging, rendering and tool dispatch start high. |
|---|---|
| Trust of the source | Fully external input (mail, public forms, scraped pages, tool results) keeps the impact. Authenticated user input lowers it one step. |
| Model boundary | If the value passes through a model call, severity rises one step, because data becomes instructions on the way out. |
| Guard coverage | A strong guard on the value closes the path. It is reported as guarded with severity low, so you can see it without acting on it. |
source inbound email body external trust
model openai.chat.completions boundary crossed → +1
sink fetch(destinationFromText) egress → critical
guard none covering the value → path is open
severity critical
confidence 82% (direct chain, crosses two files)
mapping LLM02:2025 · CWE-918
fix resolve the host against an allowlistScope of analysis
What is inside the boundary, and what sits outside it.
Evidence, reviewable by design
Static analysis over dynamic languages is inherently approximate, so every path is delivered with its trace and a confidence score. A finding is something your engineers confirm against the code in seconds, not something you take on faith.
Controls in code, not in infrastructure
Network policy, IAM boundaries and gateway rules live outside the repository. Where a route is already contained by one of those, record it as accepted — the analysis reports what the code itself guarantees.
Controls, not prompt wording
Instructions in a system prompt are guidance, not enforcement, so they are not scored as controls. ExfilPath looks for what actually constrains the value.
Languages traced end to end
TypeScript, JavaScript and Python are traced across files today, with Java and Go on the roadmap. Anything outside that set is reported in the notes rather than silently ignored.
Why routes matter
Two breaches where every component was configured correctly.
EchoLeak
CVE-2025-32711Microsoft 365 Copilot · Disclosed June 11, 2025 · found by Aim Labs (Aim Security)
A single crafted email could make Copilot pull sensitive context and send it to an attacker-controlled server with no user interaction. Aim Labs described it as an LLM scope violation, and it is widely reported as the first zero-click prompt-injection exploit against a production AI system.
- 01 A benign-looking email lands in the mailbox and phrases its instructions for a human reader, so the cross-prompt injection classifier does not flag it.
- 02 Copilot retrieves that email as ordinary context while answering an unrelated question.
- 03 The hidden instructions make Copilot embed sensitive context into a reference-style Markdown image, a form that link redaction did not strip.
- 04 The client fetches the image automatically, and a Microsoft Teams preview endpoint allowed by the content-security policy proxies the request out.
The lesson: Every component behaved as configured. The exposure was that an untrusted mail body could reach an output channel the client fetches on its own.
In ExfilPath terms: Inbound mail is an input surface, rendered media in model output is a sensitive action, and neither redaction nor a classifier fully covers the value — so this shape reports as an open critical path.
ForcedLeak
CVSS 9.4 vulnerability chainSalesforce Agentforce · Disclosed September 2025 · found by Noma Labs (Noma Security)
Instructions placed in the Description field of a public Web-to-Lead form were executed when an employee asked Agentforce to process the lead. CRM data was then exfiltrated through an image request to a domain still on the trusted list — a domain the researchers noted could be bought for about five dollars.
- 01 An attacker submits a lead through the public form with instructions inside a free-text field.
- 02 An employee asks the agent to qualify the lead, and the agent ingests the field as context.
- 03 The agent follows the embedded instructions and queries CRM records.
- 04 The result is appended to an image URL on an expired but still-trusted domain, and the request carries the data out.
The lesson: Public form input reached an action that could both read records and make outbound requests. Salesforce began enforcing Trusted URL allow lists for Agentforce and Einstein generative AI agents on September 8, 2025.
In ExfilPath terms: A CRM free-text field is an input surface, an outbound request is a sensitive action, and a trusted-URL list only closes the route when the code resolves the destination against it.
See it on your own agent.
Add a file or a folder and read the report. Free during early access.