Skip to main content

Least privilege and privilege escalation

These checks cover profiles, permission sets and session policy that grant more than needed, and Apex that lets a caller act above their own access.

vulkro-sf scan ./force-app
vulkro-sf org least-privilege -o <alias> # live org recommender

Profile and permission set checks​

Rule IDSeverityWhat it finds
sf-lp-fls-overgrantMediumEdit on a sensitive (PII-shaped) field granted as broadly as read, or edit without read.
sf-lp-toxic-comboCriticalOne profile or permission set granting Author Apex, Modify All Data and API Enabled together.
sf-lp-custom-perm-broadMediumA custom permission granted by a standard, admin-shaped or powerful profile or permission set.
sf-lp-session-not-high-assuranceMediumSensitive permissions with no high-assurance session required.
sf-lp-integration-user-no-ipMediumAn integration-user profile with no login IP ranges.

Per-user effective field access​

These compose each user's profile and assigned permission sets (from the metadata plus a User and PermissionSetAssignment export) and flag a sensitive field reachable by too many users. Each set can look narrow while their union is wide.

Rule IDWhat it finds
SF-FLS-USER-001A sensitive field effectively readable by more users than the bar.
SF-FLS-USER-002A sensitive field effectively editable by more users than the bar.

VULKRO_SF_FLS_USER_BROAD_BAR sets the bar (default 5 distinct users).

Apex and guest checks​

Rule IDWhat it findsFix
SF-GUEST-AURA-001An @AuraEnabled method in a class granted to the site guest user that performs DML, starts an async job (System.schedule, enqueueJob, Database.executeBatch) or runs dynamic SOQL with no authorization check (CWE-862).Add WITH USER_MODE, Security.stripInaccessible, a CRUD check or a custom-permission check, or remove the guest grant.
SF-PRIVESC-SYS-001A without sharing class doing DML with request data and no CRUD/FLS check.Check isUpdateable() / isCreateable() and strip inaccessible fields before DML, or use with sharing.
SF-PRIVESC-SYS-002Apex inserting a PermissionSetAssignment or PermissionSetGroupAssignment from request-controlled input.Remove runtime assignment of privileged sets; allow only a vetted list; never take the target user from the request.

Example​

Non-compliant (SF-PRIVESC-SYS-002):

public without sharing class AccessGrantController {
@AuraEnabled
public static void grant(Id userId, Id permSetId) {
insert new PermissionSetAssignment(AssigneeId = userId, PermissionSetId = permSetId);
}
}

Compliant: the caller edits only their own record, under sharing, with an FLS check.

public with sharing class SelfServiceController {
@AuraEnabled
public static void updateMyPhone(String phone) {
User u = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];
if (Schema.sObjectType.User.fields.Phone.isUpdateable()) {
u.Phone = phone;
update u;
}
}
}

See also: Profiles and permission sets, Sharing leakage