Skip to content

Salesforce Attack Chain Detection: When Five Medium Findings Are One Critical Problem

Isolated Salesforce security findings look survivable. Attack chain detection correlates them into named multi-step scenarios, each mapped to real controls.

We shipped sf-audit v1.0 in April with 23 checks. It's now at 61, with attack chain detection, posture history tracking, and compliance tags on every finding.

We shipped sf-audit v1.0 in April with 23 checks. It's now at 61. Along the way it gained attack chain detection, posture history tracking, and compliance tags on every finding.

Here's what landed.


38 new checks, grouped by what they protect

Closing the SBS baseline gaps

Eight checks target the Salesforce Baseline Security (SBS) standard controls that were missing from the original set. These are the controls enterprise auditors reach for first:

Check SBS control What it flags
Standard Profiles SBS-ACS-005 Users still on Standard User or System Administrator profiles instead of cloned, least-privilege profiles
SSO Enforcement SBS-AUTH-001/002 Auth providers configured but not enforced as the only login path
MFA Enforcement SBS-AUTH-004 Portal and Experience Cloud users without MFA-enforced permission sets
API Client Permission SBS-ACS-006 Permission sets granting API Enabled without a corresponding integration justification
Integration Users SBS-ACS-007/008/009 Named integration users sharing a real employee's account, or without IP restrictions
Content Links SBS-FILE-001/002/003 Salesforce Files shared via public links or accessible by guest users
Field History Tracking SBS-DATA-004 Sensitive objects with no field history tracking enabled
Data Classification SBS-DATA-001/002/003 Fields handling PII or PHI with no data sensitivity or compliance categorisation

MFA beyond the checkbox

Three checks go past "is MFA on?" to verify who has actually registered a factor, and whether that factor is worth anything:

  • Internal User MFA: scans every active internal standard user and flags those with no MFA method registered in the org's user record.
  • MFA Registration: queries the TwoFactorInfo and UserTwoFactorCredential objects to identify users whose only factor is SMS OTP (phishable).
  • MFA Method Strength: distinguishes authenticator apps, hardware security keys, and Salesforce Authenticator from SMS-based and email-based factors, and flags the weak methods as MEDIUM.

Can your SOC see what's happening?

Four checks verify that your org produces the log data needed to detect and respond to incidents:

  • Deployment Identity: flags Connected Apps used in CI/CD pipelines with no unique owner, or that share credentials across environments.
  • Apex Logging: checks whether scheduled and batch Apex jobs write structured log events rather than ad-hoc System.debug calls.
  • Event Monitoring: verifies that the Event Monitoring add-on is active and that key event types (Login, API, Apex, Report, ContentDistribution) are being collected.
  • SIEM Integration: correlates Named Credentials, Remote Sites, Connected Apps, and scheduled Apex against known SIEM egress patterns to identify whether logs are being forwarded out of Salesforce.

Code and application security

Four checks extend the static analysis already in the plugin:

  • Apex CRUD/FLS: flags Apex classes that perform DML or SOQL without an isCreateable(), isAccessible(), or stripInaccessible() guard. These are the most common source of OWASP A01 violations in Salesforce code.
  • Apex REST Endpoints: identifies @RestResource classes exposed without with sharing or with global scope, reachable by any authenticated user.
  • Visualforce XSS: scans Visualforce pages for unescaped {!expr} or apex:outputText without escape="true", the classic Salesforce XSS vector.
  • Anonymous Apex Audit: flags orgs where anonymous Apex execution has been used in production within the past 30 days, a persistent indicator of ad-hoc privileged code execution.

Platform hygiene and supply chain

Five checks cover the slow-burn risks that rarely trigger alerts but accumulate exposure over time:

  • Certificate Expiry: queries the certificate store and warns when any org certificate expires within 60 days (HIGH) or 30 days (CRITICAL).
  • Installed Packages: inventories managed and unmanaged packages, flagging packages from untrusted namespaces, packages inactive for 12+ months, or packages with full-admin permission sets.
  • Release Updates: lists pending Salesforce release updates that your org has deferred past their enforcement deadline.
  • Legacy API Versions: flags Apex classes compiled against API versions below 50.0 (Winter '21) and SOAP remote site registrations still targeting legacy endpoints.
  • Enhanced Domains: checks whether Enhanced Domains URL isolation is enabled, Salesforce's recommended defence against cross-org session attacks.

Evidence of active attacks

Four checks look for conditions consistent with attacks already in progress:

  • Transaction Security Policies: verifies that at least one automated threat-detection policy is active (e.g. report exports over 2,000 rows, login from new IP, API calls from unknown app).
  • Failed Login Detection: queries LoginHistory for credential stuffing patterns: 10 or more consecutive failures for a single username, or failures from more than 20 distinct source IPs within 24 hours.
  • Debug Log Access: identifies active debug log traces for production users. Live traces dump Apex variable values, including any credential strings the code touches.
  • Trusted IP Ranges: flags IP ranges defined on profiles that bypass MFA, particularly ranges broader than a /24.

Data exposure

Three checks target the vectors most commonly behind insider-threat and accidental leak scenarios:

  • Experience Cloud Site: flags live sites with self-registration enabled, guest-accessible pages, or unauthenticated API access.
  • Custom Labels Credential: scans custom label values for patterns matching API keys, Bearer tokens, and Base64-encoded user:pass strings. Custom labels are globally readable by all Apex code regardless of field-level security.
  • Report Folder Access: identifies public report folders shared with All Internal Users on objects containing PII (Account, Contact, Lead, Case). A user with no object-level permissions can still export a report they can see.

Attack chain ingredients

Three lightweight checks exist specifically to feed the chain engine:

  • Escalation Perms: detects the permission cluster that enables privilege escalation: Assign Permission Sets, Manage Users, Author Apex, Modify Metadata, Manage Profiles.
  • CORS Allowlist: flags wildcard origins (*) or overly broad origins in the CORS allowed-origins list, which enable cross-origin credential exfiltration.
  • Guest Executable Apex: identifies Apex classes reachable by guest users via Visualforce or Experience Cloud that run without sharing enforcement.

Compound findings surface as named attack chains

After the checks run, the engine in src/chains/ evaluates your org's active findings against five named attack scenarios. If enough ingredients are present for a realistic multi-step attack, a separate CRITICAL or HIGH chain finding appears in the report alongside the individual findings.

The five chains:

Unauthenticated bulk exfiltration

Trigger: unauth-foothold + (code-exec OR data-read-bulk OR data-write)

An unauthenticated guest foothold (from guest object permissions or guest-accessible Experience Cloud pages) combines with guest-executable Apex or public external sharing to read business records in bulk without any login credential.

Standard user → org takeover

Trigger: low-trust-authenticated OR unauth-foothold + (priv-esc OR org-takeover)

A low-trust authenticated user, or even a guest, combines with an escalation permission (Assign Permission Sets, Author Apex, Manage Users) to reach full org administrator control.

Credential theft → external pivot

Trigger: credential-theft + external-egress

Exposed secrets (hardcoded credentials in Apex, credentials in custom labels, active debug traces, or a broad CORS origin) combine with an external callout path (Named Credential or Remote Site) to move stolen credentials to attacker infrastructure.

SOQL injection → mass read

Trigger: code-exec + injectable dynamic SOQL + bulk-readable data

Injectable dynamic SOQL in a class without bind variables combines with a permissive sharing model or broad field-level security to let an attacker extract large datasets through a single endpoint.

MFA bypass → privileged compromise

Trigger: MFA weakness + highly-privileged account present

Weak MFA enforcement, trusted-IP MFA bypass ranges, or unregistered MFA for internal users, combined with the presence of ModifyAllData or ViewAllData accounts. A credential-stuffing or phishing attack can compromise an org admin with no second-factor challenge.

Each chain finding includes the specific contributing findings and a tailored remediation narrative.


Every run archives to a local history store

sf audit security now silently archives a JSON copy of its result after each run:

~/.sf/audit-history/{orgId}/sf-audit-{orgId}-{timestamp}.json

No configuration needed. The archive accumulates across runs.

View the trend:

sf audit history --target-org myOrg
Audit History: My Org (00D000000000001)
────────────────────────────────────────────────────────────────────────────────
  #   Date                  Score   Grade   CRIT   HIGH    MED    LOW   Δ Score
────────────────────────────────────────────────────────────────────────────────
   1  2026-03-23 15:10       64      D          1      5      8      3        —
   2  2026-04-09 11:22       81      B          0      2      5      3      +17
   3  2026-06-11 09:45       88      A          0      0      4      5       +7
────────────────────────────────────────────────────────────────────────────────
  Trend: ▲ +24 over 3 audits   Best: 88 (2026-06-11 09:45)   Worst: 64 (2026-03-23 15:10)

The command also writes an HTML timeline to your current directory. Flags: --limit (most-recent N runs), --output (write directory), --reports-dir (custom archive location).


Before/after diffs for remediation sprints

Compare any two audit JSON files:

sf audit diff baseline.json current.json
─────────────────────────────
  Diff Summary
─────────────────────────────
  Score delta     +17
  Grade        D → B
  New               0
  Resolved          1
─────────────────────────────

Writes HTML and JSON diff reports to the current directory. Useful for pre/post comparisons after a remediation sprint, or before/after a Salesforce release cuts.


Every check carries a compliance reference

Every check carries compliance references, so a finding arrives already tied to the control it breaches.

The framework set has changed since this post was written. It originally spanned five, including HIPAA and GDPR. Those two were dropped: mapping a Salesforce configuration finding to a GDPR article or a HIPAA safeguard implies a legal conclusion the tool is not in a position to reach, and a tag that overstates its own authority is worse than no tag. The current set is eight, and the plugin's documentation is explicit that it does not assess HIPAA or GDPR compliance.

Framework Version Coverage
OWASP Top 10 2021 Web application risk categories
OWASP LLM Top 10 2025 LLM01 Prompt Injection, LLM02 Sensitive Information Disclosure, LLM05 Improper Output Handling, LLM06 Excessive Agency
SOC 2 AICPA TSC 2017 Common Criteria CC6 through CC9
ISO/IEC 27001 2022 Annex A controls
Security Benchmark for Salesforce current Salesforce-native benchmark
NZ Privacy Act 2020 Information Privacy Principles 5, 9 and 12
HISO 10029 2022 NZ Health Information Security Framework
NZISM v3.8 NZ Information Security Manual

The mapping is also stricter than it was. It now runs off a sourced control catalogue where each control carries its framework, a pinned version, the official title, and a citation, so a compliance reference points at an exact requirement you can defend in a review rather than a bare label.

Tags are embedded in the report output, so findings can be filtered by control without post-processing. Useful when an auditor asks for the subset of findings mapping to a specific SOC 2 criterion.


Upgrade

sf plugins install @cclabsnz/sf-audit@latest

To see every check ID and category:

sf audit list

The --scoring-config flag and existing custom scoring files from v1.0 continue to work without changes.

Since this post, the plugin has kept moving: 88 checks across ten domains as at August 2026, with a major expansion of guest and external-facing coverage, new access-control depth, and offline forensic timeline reconstruction. For how it now grades guest exposure by real reachability, see Salesforce Guest User Exposure: How sf-audit Grades It by Real Reachability.

Responses

Checking your session.

Loading responses.