S4 Security Scan Results
The use of static code analysis has significant benefit for a development team, most importantly it establishes best practices patterns of reuse. All dev teams run based on what is common within the existing code base and the use of copy and paste to support development outcomes is universal!
Static code analysis and their recommendations are, however, not enough to ensure that anti patterns are not introduced and propagated. Additional quality processes must be used to support this, the simplest being pull requests.
What do I mean by an anti-pattern? Here’s a common scenario that is flagged by all static code analysis engines and the recommendation to correct.
S4 Warning
Background
CRUD violations occur when the Apex code doesn't enforce CRUD checks and runs in system context. This leads to authorization by-pass. Authorization, or access control, is a way of mediating access to resources and application functionality based on the identity of a user. Anytime a user can gain access to a resource that is denied to their role, they have performed authorization bypass. There are countless ways authorization bypass can occur. In this case, the user is able to execute a Read, Update, or Delete (CRUD) function in Apex code without their permission being validated correctly. Authorization Bypass occurs when user permissions are not validated prior to executing a Create, Read, Update, or Delete (CRUD) function in Apex code.
Remediation Guideline
User permissions should always be validated before a CRUD function is executed in Apex code. This way, if the user does not have the correct permissions for what they are trying to do, they will now be allowed to proceed.
Implement access control checks such as isAccessible() prior to Select. isAccessible() checks to see if the user who is trying to select something has permissions to access that information.
Implement access control checks such as isCreateable() prior to insert. isCreateable() checks to see if the user who is trying to insert something is allowed to insert that type of information/object.
Implement access control checks such as isUpdateable() prior to update. isUpdateable() checks to see if the user who is trying to update something is allowed to do so.
Implement access control checks such as isDeleteable() prior to delete. isDeleteable() checks to see if the user who is trying to delete something is allowed to do so.
Code Snippet:
WITH SECURITY_ENFORCED in SOQL Select statements can also be used instead of isAccessible().
Code Scan reports similar
Field Level Security Vulnerabilities
- Vulnerability
- Major
- owasp-a3, owasp-a5, salesforce
- Available Since Feb 22, 2019
- Apex (APEX)
- Constant/issue: 30min
This rule makes sure that the code checks for access permissions before running a SOQL, SOSL, or DML operation. This prevents access to protected resources by users without the correct privileges.
The recommendations made are syntactically correct and rerunning a scan will remove the flagged failure if you implement one of the suggestions, however the introduction of this test has now potentially introduced a significant defect.
if(Case.SObjectType.getDescribe().isAccessible() &&
Schema.SObjectType.Case.fields.Id.isAccessible() && Schema.SObjectType.Case.fields.ContactId.isAccessible() && Schema.SObjectType.Case.fields.ContactEmail.isAccessible()) {
caseRec = [Select ContactId, ContactEmail From Case where Id =: caseRecord FOR UPDATE];
}
String targetObjectId = caseRec.ContactId;
System.assert(Case.SObjectType.getDescribe().isAccessible(), ‘The Case object is not accessible’);
System.assert(Schema.SObjectType.Case.fields.ContactId.isAccessible() ‘The Contact field on the Case object is not accessible’);
System.assert(Schema.SObjectType.Case.fields.ContactEmail.isAccessible() ‘The Contact Email field on the Case object is not accessible’);
caseRec = [Select ContactId, ContactEmail From Case where Id =: caseRecord FOR UPDATE];
String targetObjectId = caseRec.ContactId;
List<ContentDocumentLink> returnList = new List<ContentDocumentLink>();
SObjectAccessDecision securityDecision = Security.stripInaccessible(AccessType.READABLE, [SELECT Id, LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId IN :linkedEntityIdSet]);
returnList = (List<ContentDocumentLink>) securityDecision.getRecords();
return returnList;
}
Again assertion should be made in this case using the object isSet() function
Contact c = securityDecision.getRecords()[0];
System.assert(c.isSet('social_security_number__c'));