Salesforce record access is layered. Org-wide defaults set the floor for each object, the role hierarchy opens records upward, and sharing rules open them sideways to roles, groups and portal users. The design is sound. The problem is that the floor is visible on one Setup page and the openings are scattered across dozens of rules that nobody reads twice.
The 2026 Experience Cloud campaign made the cost concrete. Salesforce Ben's mitigation list for it starts with record access, not code: set org-wide defaults to Private for external users, enable Secure guest user record access, and uncheck Portal User Visibility and Site User Visibility. Earlier research on the same exposure, first published in 2021, adds turning off "Let guest users see members of this site" and setting a default owner for guest-created records. The Verizon 2026 DBIR puts the wider stakes plainly: 31% of breaches start with a vulnerability, and 48% involve ransomware. A CRM full of readable records is what the second number runs on.
This post is about the single rule that undoes a careful set of defaults, why it is invisible until listed, and how to list it.
The floor and the openings
Each object has an internal org-wide default and, for orgs with external users, an external one. Private means a record is visible to its owner and to people above the owner in the role hierarchy. Public Read Only and Public Read/Write widen that for everyone. Setting both defaults to Private for the objects that hold customer data is the right starting point, and it is where most reviews stop.
A sharing rule then grants access on top of the floor. Owner-based rules share records owned by one group with another. Criteria-based rules share every record matching a field condition with a recipient. The recipient can be a Role, a Role and its Subordinates, a public Group, or all internal users. The access level can be Read, Edit, or on some objects All.
Each rule is reasonable in isolation. "Share Accounts in region EMEA with the EMEA sales role" is a normal business request. The exposure comes from the combination: a criteria rule on an object whose fields hold something sensitive, granting Edit, to a recipient that turns out to be most of the company.
The rule you cannot see
Consider a criteria-based rule on Account granting Edit to a Role and Subordinates. If that role is near the top of the hierarchy, "and Subordinates" means nearly everyone. The Account object carries billing fields, and many orgs have added tax identifiers, bank details or contract values to it over the years. The org-wide default on Account is Private, the review ticked the box, and one rule made the default irrelevant for most users.
Nothing in Setup presents this as a risk. The rule appears in a list of rules on the object, by name, with a recipient. To understand its reach you have to know the role tree, the fields on the object and what those fields contain. That is three screens and a conversation, per rule, per object.
Vulkro's security-settings detectors read the two files where this lives, *.object-meta.xml and *.sharingRules-meta.xml, and flag two shapes directly. The first is externalSharingModel set to ReadWrite on any object: an external default that lets portal and guest users edit records. The second is a criteria-based sharing rule on Account, Contact, Lead, Opportunity, Case or User that grants Edit or All to a Role, RoleAndSubordinates or Group.
Fields make it worse
An object is only as sensitive as its fields, so the useful question is not "is Account shared broadly" but "which fields on Account does that rule expose, and what is in them".
The sharing-leakage detectors answer that. sf-sharing-leak-field fires when a rule shares an object holding tier-1 or tier-2 PII, a credential, a financial field or a government identifier with a broad recipient, and it names the rule, the object, the field, the access level and the recipient in one line. sf-sharing-leak-recordtype does the same for restricted record types. Broad recipient means a Role, RoleAndSubordinates, a public Group or AllInternalUsers.
The field classification comes from the Salesforce PII map, which assigns tiers to standard fields and to custom fields by name and type. A rule sharing an object that only holds a region code is not reported. A rule sharing the same object after someone added Tax_ID__c is.
The attack-surface inventory adds SF-ATTACK-SURFACE-003, which lists every sharing rule and public group that shares to a broad recipient, so you can see the full set before deciding which ones are wrong. The attack-surface detectors page describes the inventory.
Guests and portal users
External access deserves its own pass because the recipients are strangers. The settings to verify, drawn from the 2021 research and the 2026 mitigation list: Secure guest user record access is checked, "Let guest users see members of this site" is off, guest-created records get a default owner so they do not stay visible to the guest, external defaults are Private, and Portal User Visibility and Site User Visibility are unchecked.
Each of these is a different Setup page, which is why they drift. A sharing-rule audit that ignores them is only half done.
Reading it from source and from the org
vulkro-sf scan ./force-app runs the sharing and security-settings checks on the metadata in the repository, so a rule added in a feature branch is reported in the pull request that adds it. For a repository that mirrors the org, this is the cheapest place to catch the rule before it deploys.
Rules changed in Setup never reach the repository until someone retrieves them. vulkro-sf org report --target-org <alias> -O posture.html reads the live org through the Salesforce CLI and writes one posture report covering identity, settings, connected apps and packages, including the sharing settings as they are today. It is read-only, runs against an alias you already authorised, and the credential never leaves the sf CLI store.
One limit: the scanner reports what the rules grant. Whether a given user actually opened a given record is a question for event monitoring, not for metadata.
What to do this week
- Export the sharing rules for Account, Contact, Lead, Opportunity, Case and User and read every criteria-based rule granting Edit or All. Write down the recipient's real size.
- Run
vulkro-sf scan ./force-appand triage eachsf-sharing-leak-fieldresult by the field it names, not by the object. - Check
externalSharingModelon every object that has external users. ReadWrite needs a written reason. - Walk the external settings list: Secure guest user record access, site member visibility, default owner for guest records, Portal and Site User Visibility.
- Run
vulkro-sf org report --target-org <alias> -O posture.htmland compare the live settings with the repository.
The sample workspace shows these findings on fictional Salesforce data, including the rule, field and recipient for each. To run it on your own org, start the 14-day trial.