Profiles and Permission Sets
Vulkro walks every *.profile-meta.xml, *.permissionset-meta.xml,
*.permissionsetgroup-meta.xml, *.samlssoconfig-meta.xml, and
*.loginFlow-meta.xml file under your project, plus (when an org
connector is configured) the User, PermissionSetAssignment, and
PermissionSetGroupAssignment rows returned from the Tooling API. The
detectors below target the identity-over-privilege failure mode behind
two of the largest 2025-26 Salesforce breach classes: Experience Cloud
guest-user exposure (Loblaw, ADT) and the ShinyHunters / UNC6040
vishing campaign (Google, Qantas, Allianz).
What Vulkro detects
- Standard profile over-privilege: a clone of
System Administrator,Standard User, orMarketing UserwithModifyAllData=trueandManageUsers=true. The platform-shipped profiles are immutable; a clone with the same admin flags is the canonical "an admin needed one custom field, so they cloned and elevated" anti-pattern. Use a custom profile that grants only the necessary permissions. - Custom profile over-privilege: any custom profile (built from scratch, not cloned) with
ModifyAllData=true,ManageUsers=true, orAuthorApex=true. The last flag in particular lets a user write Apex (and therefore bypass every CRUD / FLS rule the codebase enforces) and is rarely required for business users. - Permission Set over-privilege: the same three flags at the permission-set level. Vulkro treats a permission set as the elevation surface most likely to be assigned ad-hoc (admins use permission sets to "temporarily" grant a power and forget to revoke it).
- Permission Set Group aggregation: a
PermissionSetGroupthat aggregates two or more medium-privilege permission sets whose combined effect is admin-equivalent. Vulkro computes the union ofuserPermissionsacross the group's members and flags the group when the union contains any pair from a documented dangerous-combination list (e.g.ViewAllData+ManageUsers, orViewAllUsers+ManageDataCategories). - Dormant admin assignments: with the org connector active, any
UserwhoseLastLoginDateis older than 90 days but who still holdsModifyAllData(directly via profile, via a permission set, or via a permission set group). These are the highest-value vishing targets per the ShinyHunters / UNC6040 incident profile: an unused admin account that no one will notice being abused. - Guest-user license profile elevation: a profile whose
userLicenseisGuest License(Experience Cloud), and whose object-permission block grantsreadorviewAllRecordson any sObject that the PII map marks as tier-1 or tier-2. This is the Experience Cloud guest-user exposure class behind the Loblaw 75M-record and ADT 10M-record disclosures. - SAML SSO signature algorithm weakness: a
SamlSsoConfigwith<requestSignatureMethod>RSA-SHA1</requestSignatureMethod>. SHA-1 is broken; the request signature is forgeable. UseRSA-SHA256. - LoginFlow inventory: a flat list of active
LoginFlowrecords. Not a finding by itself: governance baseline for the security team's "what runs on every login?" review.
Risk anchors
- 2025-26 breach class map: Experience Cloud guest-user exposure. Loblaw (75M+ records), ADT (10M+). The vulnerable shape is exactly the guest-user license profile elevation detector: a Guest License profile that grants
readon a sObject containing PII makes every record in that object visible to the unauthenticated Internet. - 2025-26 breach class map: ShinyHunters / UNC6040 vishing. Google (2.55M), Qantas (5.7M), Allianz (1.4M). The attack ingress is social engineering of an unused admin: the dormant-admin detector elevates those accounts so the team can decommission, demote, or step up the MFA posture before the next campaign.
- AppExchange Top-20 mapping: rule 7 (CSRF) and rule 18 (username or email enumeration) both indirectly depend on the profile model; the rules in this page are the platform-level controls that the per-page Apex / VF rules sit on top of.
Example positive (code that triggers a finding)
<!-- Reporting Admin.profile-meta.xml -->
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<custom>true</custom>
<userLicense>Salesforce</userLicense>
<userPermissions>
<name>ModifyAllData</name>
<enabled>true</enabled>
</userPermissions>
<userPermissions>
<name>ManageUsers</name>
<enabled>true</enabled>
</userPermissions>
</Profile>
With the org connector active, the corresponding User query returns:
Id: 005xx000001AbCdE
Username: [email protected]
ProfileId: <Reporting Admin>
LastLoginDate: 2025-12-12T10:14:22Z
Two findings emit. Custom profile over-privilege (High): the
profile grants both ModifyAllData and ManageUsers. Dormant admin
assignment (Critical): the user holding the profile has not logged in
for over 90 days as of the scan date.
Example negative (code that does not trigger)
<!-- SOC Analyst.permissionset-meta.xml -->
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<userPermissions>
<name>ViewAllData</name>
<enabled>true</enabled>
</userPermissions>
</PermissionSet>
PermissionSetAssignment: [email protected] -> SOC Analyst
User.LastLoginDate: 2026-05-29T08:02:11Z
ViewAllData (read-only) is a medium-privilege grant, not on the
dangerous-pair list with anything else this permission set holds. The
assignee is an active user with a recent login. No finding emits;
the permission set still appears in the governance inventory.
Tuning
-
Dormant-admin confidence is High when the org connector returned a
LastLoginDateand the gap exceeds 90 days. The threshold is configurable via--dormant-admin-days <N>onvulkro-sf scan(the default is 90; raise to 180 for orgs with quarterly admin reviews, drop to 30 for high-sensitivity environments). -
Guest-user license elevation does not fire on the platform's built-in
Help CenterandCustomer Portalprofiles when those grant read only on objects that are not in the PII map. The rule consults the same PII map the PII page documents. -
Common false positive: a custom profile cloned from
System Administratorfor a short-lived migration. Suppress with a project fingerprint:# .vulkroignorefp=<hex> # ProfileOver-Privilege: MigrationAdmin until=2026-06-30The general suppression rules (
// vulkro:disable[<rule>]) do not apply to XML metadata; the metadata rule emits at file level and is suppressed via the.vulkroignorefingerprint.
Where to go next
- Security settings for the org-wide login posture (session timeout, IP binding, password policy) that gates how dangerous a misconfigured profile becomes.
- Connected Apps for the federated-identity surface that integrates with the SAML SSO config above.
- Methodology, section 5 for the full metadata-type taxonomy.