0

I've added the WITH SECURITY_ENFORCED clause to my SOQL query. The query is present at a Rest service for a callback so I am testing it by creating a public site will create a profile (External Users with Guest User license) automatically to do this, I have given the read and edit permissions to the objects. But I'm getting the System.QueryException: Insufficient permissions: secure query included inaccessible field error. My Rest Service is something like below, I've used the SObjectAccessDecision as follows but the System.debug line didn't reach to troubleshoot,

Is there anyway we can short it? I cannot able to assign a PS to the Guest user by giving access to my custom object.

@RestResource(urlMapping='/smsResponse')
global without sharing class Twilio_SMSManager {
    
    @HttpPost
    global static void postCallback() {

        TwilioConfig__c twilioCustomSetting = TwilioConfig__c.getOrgDefaults();
        String expectedSignature = RestContext.request.headers.get('X-Twilio-Signature');
        String url = twilioCustomSetting.Status_Callback_URL__c;
        Map <String, String> params = RestContext.request.params;
        System.debug(LoggingLevel.INFO, '<---Return Response--->' + params);
        System.debug(LoggingLevel.INFO, '<---The Host URL--->' + RestContext.request.headers.get('Host'));
        System.debug(LoggingLevel.INFO, '<---The Callback URL at the Custom setting--->' + twilioCustomSetting.Status_Callback_URL__c);

        //Validate signature.
        if (!TwilioAPI.getDefaultClient().validateRequest(expectedSignature, url, params)) {
            RestContext.response.statusCode = 403;
            RestContext.response.responseBody = Blob.valueOf('Failure! Rcvd '+expectedSignature+'\nURL '+url+'\nHeaders'+RestContext.request.headers);
            return;
        }

        RestContext.response.responseBody = Blob.valueOf('ok');

        if(params.size() > 0) {
            List<SMS__c> lstSMSToUpsert = new List<SMS__c>();
            SMS__c smsDetails = new SMS__c();

            smsDetails.Message_SID__c = params.get('MessageSid');
            smsDetails.Status__c = params.get('MessageStatus');

            lstSMSToUpsert.add(smsDetails);

            List<Id> lstUpsertedSMSRecordIds = new List<Id>();
            Schema.SObjectField messageSID = SMS__c.Fields.Message_SID__c;
            Database.UpsertResult[] upsertResult = Database.upsert(lstSMSToUpsert, messageSID, false);
            for (Database.UpsertResult sr : upsertResult) {
                if (sr.isSuccess()) {
                    // Operation was successful
                    lstUpsertedSMSRecordIds.add(sr.getId());
                }
            }

            System.debug(LoggingLevel.INFO, '<---lstUpsertedSMSRecordIds--->' + lstUpsertedSMSRecordIds);

            if(lstUpsertedSMSRecordIds.size() > 0) {

                    [Options]
            }
        }
    }
}

Option 1 - To find the fields which don't have the access

SObjectAccessDecision securityDecision = Security.stripInaccessible(AccessType.READABLE,
                                                [SELECT Id, Status__c, 
Service_Appointment__r.Id, Service_Appointment__r.SA_Dispatched_SMS_Status__c FROM SMS__c WHERE Id IN :lstUpsertedSMSRecordIds limit 1]
                                                );

System.debug('1111111 ' + securityDecision.getRecords()); => 1111111 (SMS__c:{Id=a036D000005HXUsQAO})
System.debug('2222222 ' + securityDecision.getRemovedFields().get('SMS__c')); => 2222222 null 

So from the above debug there are no inaccessible fields.

Option 2 - Update the records.

SMS__c upsertSMS = [SELECT Id, Status__c, Service_Appointment__r.Id, Service_Appointment__r.SA_Dispatched_SMS_Status__c FROM SMS__c WHERE Id IN :lstUpsertedSMSRecordIds WITH SECURITY_ENFORCED limit 1];


if(upsertSMS != null && upsertSMS.Service_Appointment__r != null) {
            upsertSMS.Service_Appointment__r.SA_Dispatched_SMS_Status__c = upsertSMS.Status__c;
            update upsertSMS.Service_Appointment__r;
        }

System.QueryException: Insufficient permissions: secure query included inaccessible field

I don't understand the behavior.

Without the WITH SECURITY_ENFORCED I can able to query and update the records.

1 Answer 1

3

You can't use Security.stripInaccessible() and WITH SECURITY_ENFORCED on the same query:

SObjectAccessDecision securityDecision = Security.stripInaccessible(
    AccessType.READABLE,
    [SELECT Id, Status__c, Service_Appointment__r.Id, Service_Appointment__r.SA_Dispatched_SMS_Status__c 
     FROM SMS__c 
     WHERE Id IN :lstUpsertedSMSRecordIds 
     WITH SECURITY_ENFORCED 
     limit 1]
);

WITH SECURITY_ENFORCED throws an exception if unreadable fields are present, so you'll never get back any details on a security decision from Security.stripInaccessible().


In Spring '21, guest users cannot have Edit permissions at all. You won't be able to enforce that area of CRUD/FLS in your Apex because you cannot provide the access at all.

More generally, though, I would suggest that it's unclear whether you need to be enforcing CRUD and FLS at all here. This integration code has a specific job to do and you're validating the receipt of the message from Twilio via their API. Since properly enforcing CRUD/FLS for all of the operations you're doing is not possible anyway, I'd document an exception for this class and rigorously review it for potential exploits, but not try to enforce CRUD/FLS.

1
  • Yes exactly forgot to remove the WITH SECURITY_ENFORCED in the query. But I can see strange behaviors while working with SObjectAccessDecision and SECURITY_ENFORCED. I'll update the question. Without enforcing CRUD/FLS I cannot able to resolve the security issue which thrown by PMD for the security review. Commented Feb 3, 2021 at 6:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .