Skip to content

Salesforce Guest User Exposure: How sf-audit Grades It by Real Reachability

sf-audit now grades Salesforce guest user exposure by real UI API reachability, not just the sharing model, and adds six new external-facing security checks.

TL;DR

  • The guest user is the most exploited surface in Salesforce, and the sharing model alone over-reports and under-reports what a guest can actually pull.
  • @cclabsnz/sf-audit now grades guest object exposure by real reachability: it probes the org's UI API for each exposed object and confirms read access against UserRecordAccess, rather than trusting the OWD and sharing setup on its own.
  • Findings split into two tiers. Tier 1 is CRITICAL: an object that is both readable in the sharing model and reachable through the UI API, so a guest can bulk-read it. Tier 2 is MEDIUM: readable in the sharing model but not reachable through the UI API, which is a defence-in-depth grant to remove, not an open door.
  • Six new external-facing checks join the guest domain: guest site options, Secure Guest User Record Access, guest API and bulk access, classic Force.com Sites, Experience Cloud CSP and Lightning Web Security, and guest traffic anomaly.
  • The plugin is now at 82 read-only checks across nine domains, up from the 61 documented earlier. This release also adds shadow-admin detection and separation-of-duties, and a Metadata client for settings the read APIs never exposed.

What You'll Learn

  • Why grading guest exposure by the sharing model alone gives you both false criticals and blind spots
  • How sf-audit probes real UI API reachability, and what the two-tier grade means in practice
  • What each of the six new external-facing checks looks for
  • How this feeds attack-chain correlation, so a guest foothold is scored as part of a path in, not a lone finding
  • How to run just the guest and external-facing checks against an org

The Problem

Every large Salesforce guest-access incident of the last few years has the same shape. An Experience Cloud or Force.com Sites page runs as the guest user, the org-wide defaults or a sharing rule leave an object readable, and an attacker walks records out through an unauthenticated API call. The org owner is usually surprised, because the object was never meant to be public and nobody clicked a button that said "expose this."

The hard part is measuring the risk accurately before someone else does. Grade a guest audit purely on the sharing model and you get two errors at once. You over-report, flagging objects that are readable in theory but that the platform will not actually return to a guest through its APIs, which inflates the risk score and trains people to ignore it. And you under-report, because reachability depends on more than OWD: guest record-access policy, the site's own options, whether the object is modelled in the UI API, and whether Apex or a Site exposes it another way. A checklist that only reads sharing settings cannot see any of that.

Common questions this article answers:

  • Why did sf-audit change a guest finding from CRITICAL to MEDIUM after I upgraded?
  • How do I know whether a guest can actually pull an object, not just whether it is readable on paper?
  • What does sf-audit check now for Experience Cloud and Force.com Sites guests?
  • Is it safe to run reachability probes against a live public site?

Full disclosure: I build and maintain @cclabsnz/sf-audit. This post is about what changed in the guest and external-facing domain and why the change makes the grade more honest, not less alarming.

Quick Answer

sf-audit's guest domain no longer grades on the sharing model alone. For every object a guest can read on paper, it asks a second question: can a guest actually reach it? It probes the org's UI API object-info endpoint for that object and confirms read access against UserRecordAccess, using your authenticated session, read-only. Objects that are both sharing-readable and UI-API-reachable are graded Tier 1, CRITICAL, because a guest can bulk-read them. Objects readable in the sharing model but not modelled in the UI API drop to Tier 2, MEDIUM, a grant worth removing but not an exfiltration path. Run the guest surface on its own with:

sf plugins install @cclabsnz/sf-audit

sf audit security --target-org myOrg \
  --checks guest-user-access,guest-object-exposure,guest-record-access-policy,guest-site-options,guest-api-access,guest-executable-apex,experience-cloud-site,classic-sites,experience-csp,guest-traffic-anomaly

Everything runs locally against an org you are already authenticated to, and nothing is written back.

Why the sharing model is necessary but not sufficient

The sharing model tells you what a guest is permitted to see. It does not tell you what a guest can retrieve. Those are different questions, and the gap between them is exactly where both false alarms and real breaches live.

Consider two objects that the sharing model reports as guest-readable. The first is a custom object backing a public form, exposed through a public external OWD. A guest can query it through the standard APIs and page through every row. That is a genuine bulk-read exposure. The second is an object like Calendar or AuthSession that ends up readable through some inherited grant, but that the UI API does not model, so a guest running the site's own GraphQL or Aura calls cannot actually pull it. Both look identical to a sharing-only scan. Only one is a way out.

Grading them the same way is the problem. Mark both CRITICAL and the real one drowns in noise; the person reading the report learns that the tool cries wolf and starts skimming. The fix is not to soften the language, it is to measure reachability directly and let the grade reflect it.

Grading guest object exposure by real reachability

The guest-object-exposure check now works in three steps.

First, it finds the candidate objects the way it always did: objects a guest can read because of a public external OWD, or because guest-owned records are visible despite a private OWD. That is the sharing-model pass, and it produces the candidate list.

Second, for each candidate it probes the org's UI API object-info endpoint. A positive response means the platform models that object for UI API access, so it is reachable through the GraphQL and record APIs an Experience Cloud site actually uses. A negative response means the UI API explicitly does not model it, so those APIs will not return it. If the endpoint cannot be reached at all, the check assumes reachable and stays conservative rather than quietly clearing a finding.

Third, it confirms read access against UserRecordAccess on a sample record, as a ground-truth check that the permission really resolves to a read, rather than inferring it from configuration alone.

The result is a two-tier grade:

Finding Meaning Severity
guest-object-exposure-public-owd Objects fully readable by unauthenticated guests through a public external OWD, and reachable CRITICAL
guest-object-exposure-guest-owned Guest-owned records exposed despite a private OWD, and reachable CRITICAL
guest-object-exposure-sharing-only Readable in the sharing model but not reachable through the UI API MEDIUM
guest-object-exposure-ok Active guest users present, no objects bulk-exposed LOW (pass)
guest-object-exposure-none No active guest users LOW (pass)

If you upgrade and a finding moves from CRITICAL to MEDIUM, that is this change working. The object is still a grant you should tidy up, but the platform will not hand it to a guest through the UI API, so it is not the four-alarm exfiltration path the old grade implied. The reverse also happens: an object the sharing model looked relaxed about can stay CRITICAL because the reachability probe confirmed a guest can pull it.

The new guest and external-facing checks

The reachability grade sits inside a wider expansion of the guest and external-facing domain. Six new checks join it.

  • guest-record-access-policy (Secure Guest User Record Access). Salesforce's Secure Guest User Record Access setting removes the old guest ownership-based sharing behaviour and is the single most important guardrail for a public site. This check reports whether it is in force, so an org running without it is flagged before that becomes the reason records leak.
  • guest-site-options. Experience Cloud sites carry guest-facing options that quietly widen exposure, such as letting the guest user see other members or reach files. This check inspects those site options rather than only the data model.
  • guest-api-access. Guest reachability is not only a page problem. This check looks at guest access through API and bulk paths, the routes an attacker prefers because they page through data faster than any UI.
  • classic-sites. Force.com (Classic) Sites predate Experience Cloud and are easy to forget. Old public sites are a common source of exposure precisely because nobody has looked at them in years, so they get their own check.
  • experience-csp (Experience Cloud CSP and Lightning Web Security). Content Security Policy level and Lightning Web Security are what stand between a public site and a cross-site scripting or data-exfiltration payload. Because the relevant settings live in the Experience bundle and are not fully exposed to the read APIs, this one is advisory where it has to be, and tells you so rather than guessing.
  • guest-traffic-anomaly. Detection matters as much as prevention. This check verifies that guest-user threat and anomaly events are being stored, so unusual guest traffic is something you can actually see after the fact.

Together with the existing guest-user-access, guest-executable-apex, experience-cloud-site, and cors-allowlist checks, that is eleven checks aimed squarely at the surface most orgs get wrong.

How this plugs into attack chains

sf-audit does not just list findings, it correlates them into named, multi-step scenarios. Guest exposure is the classic first move. The Unauthenticated Bulk Exfiltration chain fires when a guest foothold combines with either guest-executable Apex or a bulk-readable object, which is exactly the pattern behind the well-known guest-access breaches.

The reachability grade makes that correlation sharper. A Tier 2 sharing-only finding is not treated as a live exfiltration ingredient, because the platform will not return the object to a guest, so it does not inflate the chain. A Tier 1 reachable finding is, so the chain reflects a real path rather than a theoretical one. The point of correlating is to show how three medium issues become one critical route in; feeding it accurate reachability data is what keeps that from becoming noise.

Beyond guest: what else is new in this release

Guest is the headline, but this is also where the plugin crossed from the 61 checks documented earlier to 82 across nine domains, and two of the new checks are worth calling out on their own.

  • privileged-access (Privileged Access and Shadow Admins). Instead of listing who holds a permission, sf-audit now resolves each active user's effective high-risk permissions across their profile, permission sets, and permission set groups, then flags shadow admins: users with admin-equivalent power who are not on the System Administrator profile. Holding Modify All Data, or the combination of Manage Users and Assign Permission Sets, makes someone an effective admin no matter what their profile says.
  • separation-of-duties. Working from that same effective-permission model, this check looks for toxic permission combinations in a single user, such as Author Apex together with Modify All Data (deploy unrestricted code with full data access), or Manage Users together with Assign Permission Sets (create an account and grant it anything). Six combinations are graded from MEDIUM to CRITICAL.

There is also a new Metadata client behind the scenes. A few settings, such as the session and clickjack hardening flags and the "administrators can log in as any user" toggle, are not clean to read through SOQL or the Tooling API. Reading them through the Metadata API upgrades those checks from advisory guesses to real detections, with a graceful fallback to advisory if the Metadata read is unavailable. For how the plugin resolves and caches this kind of data internally, see How We Built a Native sf Plugin for Salesforce Security. For the full check catalogue and the compliance mapping, see sf-audit: 61 Checks, Attack Chains, and Compliance Mapping, which this release extends.

Frequently Asked Questions

Is it safe to run the reachability probes against a live public site?

Yes. The checks run under your own authenticated sf session using read-only SOQL, Tooling, REST GET, and Metadata read calls. The UI API object-info probe reads how an object is modelled; it does not act as the guest user, post data, or change anything. As always, run against a sandbox first if you want to watch it before pointing it at production.

Why did a guest finding drop from CRITICAL to MEDIUM after I upgraded?

Because the object is readable in the sharing model but not reachable through the UI API, so a guest cannot bulk-read it through the APIs an Experience Cloud site uses. It is now graded Tier 2, a grant worth removing for defence in depth, rather than Tier 1, a live exfiltration path. The change makes the grade match what an attacker can actually do.

Does this cover classic Force.com Sites, or only Experience Cloud?

Both. experience-cloud-site, guest-site-options, and experience-csp cover Experience Cloud, and classic-sites covers legacy Force.com Sites specifically, because old public sites are a frequent and easily forgotten source of guest exposure.

What about guest write access, not just read?

Guest write access is covered by guest-user-access, which flags guest create, edit, or delete on standard objects such as Account, Contact, Case, and Lead as HIGH, separately from the read-exposure grading in guest-object-exposure. Write access to a public object is treated as more serious than read.

How many checks are there now, and where do I see them all?

82, across nine domains. Run sf audit security --target-org myOrg --checks <id> to scope to specific checks, or run the full audit for the whole picture and an A to F grade.

Key Takeaways

  • The sharing model is necessary but not sufficient. What a guest is permitted to see is not the same as what a guest can retrieve, and grading on permission alone produces both false criticals and blind spots.
  • Reachability is measured, not assumed. sf-audit probes the UI API and confirms against UserRecordAccess, so a CRITICAL guest finding means a guest can actually pull the object.
  • Two tiers keep the grade honest. Tier 1 reachable exposure is CRITICAL; Tier 2 sharing-only exposure is MEDIUM, a cleanup rather than an emergency.
  • The external-facing surface is now covered in depth. Secure Guest User Record Access, site options, API and bulk access, classic Sites, CSP and Lightning Web Security, and guest traffic anomaly all have dedicated checks.
  • The plugin grew to 82 checks, adding shadow-admin detection, separation-of-duties, and a Metadata client alongside the guest work.

What's Next?

Recommended Reading:

For the wider enforcement backdrop these checks operate in, see the 2026 security enforcement guide and the latest on the MFA enforcement pause.

Action Items:

  1. Run the guest and external-facing checks against your most exposed org and read the Tier 1 findings first.
  2. Confirm Secure Guest User Record Access is in force on every public site, and remove any Tier 2 sharing-only grants while you are there.
  3. Check experience-csp and classic-sites for the sites nobody has looked at in a year.

Resources & References