[SalesForce] Security Review Preparation: Checking isAccessible on each object and field in complex SOQL queries for whole codebase

We are preparing our managed package for security review.
And as a part of this we need to enforce not just create/update/delete but also read (i.e. select) permissions. That means basically checking isAccessible on each field from every SOQL query in our codebase.

What is best way to do it? Is there any way of framework/approach to avoid doing it manually in each class/method separately?

Moreover, some of our queries are really complex, like the one from example below. It looks like a nightmare to enlist each field separately when calling some PermissionUtils.assertReadAllowed method and then maintain such code in sync with actual field lists in query with time passes.

Does SF security review team seriously inspect all fields in all SOQL queries? Or is it enough just to enforce create/update/delete checking?

Any thoughts would be appreciated. Thanks in advance.

    [
        SELECT
            Id,
            Name,
            OwnerId,
            Owner.Id,
            Owner.Name,
            EnforceOwnerPermissions__c,
            OfflineUniqueId__c,
            DependentOfflineIds__c,
            ItemOfflineIds__c,
            LastSyncTime__c,
            Status__c,
            IsNew__c,
            IsSuccess__c,
            IsRetry__c,
            IsFailed__c,
            IsCancelled__c,
            IsManual__c,
            CreatedMoreThan24HoursAgo__c,
            HasSyncHappenedAfterwards__c,
            IsPending__c,
            IsRetriable__c,
            IsCancellable__c,
            IsSimulatable__c,
            IsRepairable__c,
            ReadyToBeProcessed__c,
            NumberOfFailedAttempts__c,
            ExpectedItemsCount__c,
            ActualItemsCount__c,
            AllItemsCount__c,
            HasLessItemsThanExpected__c,
            HasMoreItemsThanExpected__c,
            DebugMode__c,
            OfflineTimestamp__c,
            LastRunLog__c,
            LastRunLog__r.Id,
            LastRunLog__r.Log__c,
            LastRunDebug__c,
            Comments__c,
            CreatedById,
            CreatedBy.Id,
            CreatedBy.Name,
            CreatedDate,
            LastModifiedById,
            LastModifiedBy.Id,
            LastModifiedBy.Name,
            LastModifiedDate,
            (
                SELECT
                    Id,
                    Name,
                    OfflineUniqueId__c,
                    SyncTransaction__c,
                    Status__c,
                    ReadyToBeProcessed__c,
                    EntityId__c,
                    EntityType__c,
                    Operation__c,
                    Data__c,
                    OfflineTimestamp__c,
                    Owner__c,
                    EnforceOwnerPermissions__c,
                    Order__c,
                    IsEntityIdOffline__c,
                    ProcessedRecordId__c,
                    RecordId__c,
                    IsInsert__c,
                    IsUpdate__c,
                    IsDelete__c,
                    IsRecordIdNotApplicable__c,
                    CreatedById,
                    CreatedBy.Id,
                    CreatedBy.Name,
                    CreatedDate,
                    LastModifiedById,
                    LastModifiedBy.Id,
                    LastModifiedBy.Name,
                    LastModifiedDate
                FROM
                    SyncTransactionItems__r
                WHERE
                    ReadyToBeProcessed__c = true
                ORDER BY
                    Order__c ASC
            )
        FROM
            SyncTransaction__c

Best Answer

We're in doomed 2020 but still we have few good things in Salesforce. Now, Salesforce has provided out of the box features to enforce the security:

Use the stripInaccessible method to enforce field- and object-level data protection. This method can be used to strip the fields from query and subquery results that the user can’t access. The method can also be used to remove inaccessible sObject fields before DML operations to avoid exceptions and to sanitize sObjects that have been deserialized from an untrusted source. https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm

Security.SObjectAccessDecision securityDecision = Security.stripInaccessible(AccessType.READABLE, [SELECT Name, BudgetedCost, ActualCost FROM Campaign]; );

// Construct the output table
if (securityDecision.getRemovedFields().get('Campaign').contains('ActualCost')) {
    for (Campaign c : securityDecision.getRecords()) {
    //System.debug Output: Name, BudgetedCost
    }
} else {
    for (Campaign c : securityDecision.getRecords()) {
    //System.debug Output: Name, BudgetedCost, ActualCost
    }

}


Apart from that if you just want to query fields having current user access "WITH SECURITY_ENFORCED" keyword as below: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_enforced.htm

List<Account> accounts = [SELECT Type, Name, Website FROM Account WITH SECURITY_ENFORCED];
Related Topic