SecuritySettings and sharing
Vulkro inspects every Security.settings-meta.xml (one per org under
force-app/main/default/settings/), the corresponding *.sharingRules-meta.xml,
*.object-meta.xml org-wide-default blocks, and the per-profile
<loginIpRanges> block on *.profile-meta.xml. These detectors cover
the org-level posture that no Apex or Visualforce rule reaches: a
hardened codebase still leaks records if the org-wide defaults are
permissive, the session timeout is twelve hours, and password
expiration is set to Never.
What Vulkro detects
- Excessive session timeout:
<sessionTimeout>TwelveHours</sessionTimeout>or<sessionTimeout>TwentyFourHours</sessionTimeout>. A stolen session cookie is valid for the duration. The platform-recommended ceiling isTwoHours; high-sensitivity orgs useOneHour. - Session not pinned to source IP:
<lockSessionsToIp>false</lockSessionsToIp>. A token replayed from a different IP is honoured, which materially helps an attacker who exfiltrated a cookie via the Salesloft Drift class of OAuth-token theft. - HTTPS not enforced:
<requireHttps>false</requireHttps>. Login or session traffic overhttp://is sniffable. - No forced logout on timeout:
<forceLogoutOnSessionTimeout>false</forceLogoutOnSessionTimeout>. The expired session is silently extended on the next activity instead of forcing reauthentication. - Clickjack protection disabled for non-setup pages:
<enableClickjackNonsetupUser>false</enableClickjackNonsetupUser>. The user-facing UI is framable by an attacker page. - CSRF protection disabled on POST:
<enableCSRFOnPost>false</enableCSRFOnPost>. State-changing Visualforce and Aura endpoints lose their automatic CSRF token. - Unlimited login attempts:
<maxLoginAttempts>NoLimit</maxLoginAttempts>. Credential-stuffing is unrate-limited. - Password expiration set to Never:
<passwordPolicies><expiration>Never</expiration></passwordPolicies>. Compromised credentials remain valid indefinitely. - Org-wide defaults too permissive: any sObject with
<externalSharingModel>ReadWrite</externalSharingModel>(records are writable by every external user the org grants any access). Vulkro flags for review; the legitimate use cases are narrow. - Sharing rules opening sensitive standard objects: a
*.sharingRules-meta.xmlcriteria-based rule whose target isAccount,Contact,Lead,Opportunity,Case, orUser, whose<accessLevel>isEditorAll, and whose<sharedTo>is a broad group (Role,RoleAndSubordinates,Group). - High-privilege profile missing login IP range: a
*.profile-meta.xmlwithModifyAllData=trueorManageUsers=trueand no<loginIpRanges>element. The profile can log in from anywhere on the Internet, removing one of the cheapest mitigations against the vishing class of attack.
Risk anchors
- AppExchange Top-20 rule 13 (information disclosure on errors): the session-timeout, CSRF, and password-policy detectors are the org-level controls that pair with the per-page disclosure rules.
- AppExchange Top-20 rule 14 (Aura components: CSS outside component): not a direct match; the closest org-level analogue is the clickjack-protection rule above (both protect the embedding boundary).
- AppExchange Top-20 rule 15 (message channel exposed): not a direct match at the org level; the LWC + Aura page handles the message-channel detector.
- 2025-26 breach class map: Salesloft Drift + Gainsight (Connected App OAuth token theft, 700+ orgs and 200+ orgs respectively). The
lockSessionsToIp=false,requireHttps=false, and high-privilege-profile-without-IP-range findings together describe the org posture that made those incidents tractable for the attacker: a stolen token replayable from any IP, over any transport, against an admin profile with no source restriction.
Example positive (code that triggers a finding)
<!-- Security.settings-meta.xml -->
<SecuritySettings xmlns="http://soap.sforce.com/2006/04/metadata">
<sessionSettings>
<sessionTimeout>TwelveHours</sessionTimeout>
<lockSessionsToIp>false</lockSessionsToIp>
<requireHttps>false</requireHttps>
<forceLogoutOnSessionTimeout>false</forceLogoutOnSessionTimeout>
<enableClickjackNonsetupUser>false</enableClickjackNonsetupUser>
<enableCSRFOnPost>false</enableCSRFOnPost>
</sessionSettings>
<passwordPolicies>
<maxLoginAttempts>NoLimit</maxLoginAttempts>
<expiration>Never</expiration>
</passwordPolicies>
</SecuritySettings>
Eight findings emit on this single file: every detector above except the org-wide-default and sharing-rule rules (which depend on separate object and sharing-rules files). The file is a worst-case posture; a real org rarely sets all eight at once, but each element is its own high-confidence finding wherever it appears.
Example negative (code that does not trigger)
<!-- Security.settings-meta.xml -->
<SecuritySettings xmlns="http://soap.sforce.com/2006/04/metadata">
<sessionSettings>
<sessionTimeout>TwoHours</sessionTimeout>
<lockSessionsToIp>true</lockSessionsToIp>
<requireHttps>true</requireHttps>
<forceLogoutOnSessionTimeout>true</forceLogoutOnSessionTimeout>
<enableClickjackNonsetupUser>true</enableClickjackNonsetupUser>
<enableCSRFOnPost>true</enableCSRFOnPost>
</sessionSettings>
<passwordPolicies>
<minimumPasswordLength>12</minimumPasswordLength>
<maxLoginAttempts>FiveAttempts</maxLoginAttempts>
<expiration>NinetyDays</expiration>
<complexity>SpecialCharacters</complexity>
<historyRestriction>10</historyRestriction>
</passwordPolicies>
</SecuritySettings>
Every element sits at a hardened value. No SecuritySettings
finding emits; the file passes the org-posture portion of the scan.
Tuning
-
Severity is High for the IP-binding, HTTPS, CSRF, and unlimited-login-attempts findings (each is a single misconfigured element with a direct attack path). Medium for the session timeout, password-policy, and clickjack findings (mitigated by other controls but still part of a hardened posture). Low for the org-wide-default review prompt (the rule defers the call to the reviewer because legitimate
ReadWriteexternal sharing exists). -
These rules read metadata only and do not require an org connector. When an org connector IS configured, Vulkro additionally validates that the metadata file's values match the live org (a stale
Security.settings-meta.xmlcommitted against a hardened production org becomes a Low-confidence drift finding instead of a misleading High). -
Common false positive: an integration sandbox where
requireHttps=falseis set deliberately for local testing. Suppress via fingerprint in.vulkroignore:fp=<hex> # SF_SECURITY_SETTINGS_REQUIRE_HTTPS: dev sandbox only until=2026-08-01The same fingerprint shape works for every metadata-level finding in this surface.
Live-org retrieval
The detectors above run on a committed Security.settings-meta.xml.
With a live org connected, vulkro-sf also reads
the running org's SecuritySettings directly: it performs a
metadata-format Metadata API retrieve through your own sf CLI (no
SFDX project required), so the file does not need to be checked into
source. The live pass emits the session-timeout, IP-binding, clickjack,
CSRF, and password-policy findings from the org's actual values, and
surfaces them in the org Posture tab of the desktop console. A
value the retrieve cannot read produces no finding rather than a guess,
so a hardened setting is never reported as a gap.
Where to go next
- Profiles and permission sets for the per-identity layer (the missing login IP range on a high-privilege profile is the natural follow-up).
- Connected Apps for the OAuth surface that the session-binding rules harden.
- Methodology, section 5.5 for the full session and org-config taxonomy.