Mustafa.
Back to all projects

Agent Blast Radius — the data an Agentforce agent can really reach

In development

A static, zero-credit analyzer that computes an agent's true data-access surface at the execution-semantics layer — apiVersion defaults, USER_MODE clauses, sharing declarations, Flow run modes — diffs it against the running user's own permissions, and intersects the gap with the org's GDPR labels. The headline number: the Escalation Gap.

Year
2026
Role
Salesforce Developer — Agent Security Research
Tech stack
Agentforce (authority analysis)Python (static analyzer)Apex execution semanticsUSER_MODE / apiVersion precedenceFlow runInMode analysisPermission & FLS resolutionGDPR / ComplianceGroup intersectionDeterministic HTML + Markdown reports47 unit tests

Highlights

  • The question nobody's tooling answers: not what an agent is allowed to do (config-layer AISPM — AppOmni, Zenity, Security Center all audit that), but what its code can actually reach — because on Salesforce, execution mode, not the running user, decides
  • The headline number is the Escalation Gap: the fields the agent's code can reach beyond its own running user — intersected with the org's GDPR/PII ComplianceGroup labels
  • The precedence law (explicit clause → apiVersion default → sharing declaration) was proven by hand in a live org before it was coded: six experiments, three widely-assumed shortcuts shown wrong — a version-blind scanner would cry wolf on modern code
  • Ran against the live HealthRecord Assistant agent: caught a system-mode read on a Private object (PS501) and a GDPR-labelled Diagnosis field reaching the model past the user's FLS (PS506) — at zero Flex Credits
  • Honest unknowns are findings, not silence: dynamic SOQL and opaque managed actions are flagged (PS504/PS507), never guessed clean
  • Deterministic, fingerprint-bound reports (Markdown + a dark-themed HTML dashboard) — regenerate on any config, Apex, Flow, or permission change; runs on every commit
  • 47 unit tests across the resolver, introspectors, analyzer, and report — verified green
  • Honest framing: design-time analysis that bounds the possible blast radius; it complements runtime monitoring, and record-level leakage is reported as posture, not exact counts. Deferred: DML operations, selector-chain follow, trigger-cascade wiring

Feature walkthrough

The live report — one GDPR field beyond the user

The dashboard for the deployed HealthRecord Assistant agent: the two-circle Escalation Gap (the agent's code reaches one field its running user cannot see — and it's GDPR-labelled), the reach summary, and the findings with concrete fixes: a pre-v67 class reading a Private object in system mode (PS501), and the Diagnosis field — ComplianceGroup PII;GDPR;HIPAA — returned to the model past the user's FLS (PS506). Produced by static analysis: no agent invoked, zero Flex Credits, bound to the config's fingerprint.

Agent Blast Radius HTML report for the live HealthRecord Assistant agent: Escalation Gap of 1 GDPR-labelled field, PS501 and PS506 errors with fixes

The problem

Salesforce says give agents least privilege, and a whole product category (AISPM) audits agent permissions — at the configuration layer, what the agent is allowed. None of it reads what the agent's code can actually do. On Salesforce the two genuinely differ, because execution mode — not the running user — decides whether an action honors that user: an action's Apex may predate API v67 and still run system-mode by default; a WITH USER_MODE clause overrides a without-sharing declaration; a Flow's runInMode can silently grant system context; a clean user-mode DML can fire a legacy trigger that escalates anyway. And a naive scanner that flags 'without sharing' without understanding this precedence produces false positives that destroy its own credibility.

The approach

Prove the physics before building the instrument. Six hand-run experiments in a live org (zero credits, self-contained fixtures) established the precedence law — explicit clause beats apiVersion default beats sharing declaration — and disproved three common assumptions along the way: a missing sharing declaration is not 'without sharing'; v67's user-mode default overrides even an explicit without-sharing for plain operations; and a trigger's DML mode follows the trigger's own API version, not the action's. Only then the pipeline: an Apex introspector resolving execution mode per operation, a Flow introspector reading runInMode declaratively, a permission resolver computing the running user's effective CRUD/FLS (including the View-All-Records-but-not-FLS nuance), and an authority analyzer that joins them into PS5xx findings — with the org's own ComplianceGroup labels marking which escaped fields are GDPR/PII. Reports are deterministic and fingerprint-bound; undetermined reach is reported as an honest unknown, never a silent pass. 47 unit tests cover the chain.

The outcome

Run against the live HealthRecord Assistant agent, the report needs one line to justify the whole tool: Escalation Gap — 1 field, 1 GDPR-labelled. A pre-v67 action class reads a Private object in system mode (PS501), and the Diagnosis field — ComplianceGroup PII;GDPR;HIPAA — reaches the model although the running user has no field-level access to it (PS506). Standard managed actions are surfaced as honestly non-analysable rather than assumed safe, and the legacy-API class is flagged for migration. All of it static, on every commit, at zero Flex Credits. Honestly framed: this is design-time bounding of the possible blast radius — it complements runtime monitoring rather than replacing it, and DML operations, selector-chain following, and trigger-cascade wiring are documented as the next steps, not claimed.