Skip to main content

Security Model

SimplePerm's security implementation, for security reviewers and developers.

Sharing Model​

Every Apex class uses with sharing. Zero without sharing classes exist in the package. Record-level access always respects the running user's sharing rules, role hierarchy, and ownership-based sharing.

CRUD/FLS Enforcement​

CRUD/FLS enforcement depends on the object type.

Standard Objects​

All SOQL queries against standard objects (Profile, PermissionSet, ObjectPermissions, FieldPermissions, etc.) use WITH USER_MODE or AccessLevel.USER_MODE. Both CRUD and FLS are enforced at the query level -- the running user only sees objects and fields their profile or permission sets allow.

Package-Internal Custom Objects​

DML on SimplePerm's own custom objects (Migration_Project__c, Migration_Log__c, SimplePermConfig__c) uses plain insert/update/delete without AccessLevel.USER_MODE. This is deliberate.

AccessLevel.USER_MODE enforces FLS based on the running user's profile. In managed packages, the subscriber org's admin may not have explicitly set FLS on namespace-prefixed fields (e.g., simpleperm__Status__c). When FLS is undefined, USER_MODE treats those fields as inaccessible and the DML fails. Known platform limitation.

Access to these custom objects is gated by the SimplePerm_Admin permission set -- the only path to SimplePerm's objects and tabs. All classes still use with sharing, so record-level access is enforced regardless.

Documented in the false-positive justifications (Category 2: ApexCRUDViolation, 23 suppressions).

Session ID Usage​

Three classes call UserInfo.getSessionId():

ClassMethodPurpose
MetadataRetrieverreadProfileAppVisibilities(), readProfileMetadata()SOAP Metadata API readMetadata() SessionHeader
MetadataDeployerdeploy()SOAP Metadata API createMetadata() SessionHeader
MigrationOrchestratorreassignOverwriteUsersViaRest()REST API Bearer token for PermissionSetAssignment re-creation

Every call targets Salesforce's own APIs:

  • SOAP Metadata API at /services/Soap/m/62.0
  • REST API at /services/data/v62.0/composite/sobjects

The session ID is:

  • Never stored in any database record, custom setting, or log
  • Never logged -- error handling strips SOAP envelopes before writing to Migration_Log__c
  • Never transmitted externally -- callouts go to the customer's own Salesforce instance only
  • Never exposed to the client -- LWC components never receive or handle it

Named Credentials are not an option here. Salesforce does not support Named Credential authentication for same-org Metadata API calls in first-generation managed packages.

Dynamic SOQL​

Four classes use Database.query():

ClassPurposeField SourceBind Variables
MetadataRetrieverRetrieves profile permissions dynamically based on available fieldsSchema.SObjectType.*.fields.getMap()Yes -- profile IDs
VerificationServiceVerifies deployed permission sets match expected stateSchema.describe()Yes -- permission set IDs
ProfileViewerControllerLoads profile and permission set details for the viewerSchema.describe()Yes -- profile/PS IDs
PermissionCatalogDiscovers available permission types in the orgSchema.describe()Yes -- filter values

In every case:

  • Field names come from Schema.describe() calls (compile-time metadata), never from user input
  • Filter values use bind variables (:paramName), preventing SOQL injection
  • Access level uses AccessLevel.USER_MODE where applicable

No user-supplied strings are concatenated into query text.

HTTP Callouts​

All HTTP callouts target Salesforce's own SOAP Metadata API and REST API. Zero third-party endpoints.

ClassEndpointProtocol
MetadataRetriever/services/Soap/m/62.0SOAP (readMetadata)
MetadataDeployer/services/Soap/m/62.0SOAP (createMetadata)
MigrationOrchestrator/services/data/v62.0/composite/sobjectsREST (sObject operations)

Remote Site Settings for the org's instance URL are configured automatically. No external domains are contacted.

Data Privacy​

SimplePerm processes Salesforce metadata only -- profile definitions and permission set definitions. It does not touch end-user personal data.

All data stays within the customer's Salesforce org:

  • Migration_Project__c -- migration run records
  • Migration_Log__c -- per-step log entries
  • SimplePermConfig__c -- package configuration
  • Migration_Event__e -- transient platform events (not persisted beyond the event bus TTL)

No external data transmission, no analytics, no telemetry, no cookies.

Code Analyzer Compliance​

SimplePerm passes Salesforce Code Analyzer v5 with the AppExchange rule set at severity threshold 2. No severity-1 or severity-2 violations.

53 suppressions at severity 3, each with a reviewer-ready justification, organized into 5 categories:

  1. UserSessionId (4 suppressions) -- Same-org SOAP/REST API authentication
  2. ApexCRUDViolation (23 suppressions) -- Package-internal custom object DML
  3. ApexSuggestUsingNamedCred (2 suppressions) -- Same-org API calls where Named Credentials are not viable
  4. EmptyCatchBlock (6 suppressions) -- Intentional non-fatal exception swallowing for notifications
  5. Intentional full-table SOQL (3 suppressions) -- Bounded queries for admin UI data

The complete justification document is available at docs/security/false-positive-justifications.md.