Skip to main content

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, or Marketing User with ModifyAllData=true and ManageUsers=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, or AuthorApex=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 PermissionSetGroup that aggregates two or more medium-privilege permission sets whose combined effect is admin-equivalent. Vulkro computes the union of userPermissions across the group's members and flags the group when the union contains any pair from a documented dangerous-combination list (e.g. ViewAllData + ManageUsers, or ViewAllUsers + ManageDataCategories).
  • Dormant admin assignments: with the org connector active, any User whose LastLoginDate is older than 90 days but who still holds ModifyAllData (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 userLicense is Guest License (Experience Cloud), and whose object-permission block grants read or viewAllRecords on 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 SamlSsoConfig with <requestSignatureMethod>RSA-SHA1</requestSignatureMethod>. SHA-1 is broken; the request signature is forgeable. Use RSA-SHA256.
  • LoginFlow inventory: a flat list of active LoginFlow records. 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 read on 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
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 LastLoginDate and the gap exceeds 90 days. The threshold is configurable via --dormant-admin-days <N> on vulkro-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 Center and Customer Portal profiles 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 Administrator for a short-lived migration. Suppress with a project fingerprint:

    # .vulkroignore
    fp=<hex> # ProfileOver-Privilege: MigrationAdmin until=2026-06-30

    The general suppression rules (// vulkro:disable[<rule>]) do not apply to XML metadata; the metadata rule emits at file level and is suppressed via the .vulkroignore fingerprint.

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.