Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[apex] False positives with ApexCRUDViolation #1418

Open
trentchilders opened this issue Oct 29, 2018 · 5 comments
Open

[apex] False positives with ApexCRUDViolation #1418

trentchilders opened this issue Oct 29, 2018 · 5 comments
Labels
a:false-positive PMD flags a piece of code that is not problematic

Comments

@trentchilders
Copy link

trentchilders commented Oct 29, 2018

Affects PMD Version:
All
Rule:
ApexCrudViolation
Description:
Hello, I have encountered what I believe to be a false positive in the ApexCRUDViolation rule. Specifically the READ portion of that rule (or an isAccessible() check in Apex). I created a helper method so that I could dynamically do this check rather than hardcode every single field that I'm querying for. However, this still causes ApexCRUDViolation to fail its read check.
Code Sample demonstrating the issue:
Helper method:

public with sharing class Utilities {
  public static void checkReadAccess(Map<String,Set<String>> fieldMap){
        Set<String> keySet = fieldMap.keySet();
        
        for(String key : keySet){
            
            Schema.SObjectType objectType = Schema.getGlobalDescribe().get(key);
            
            Schema.DescribeSObjectResult describeSObject = objectType.getDescribe(); 
            if(!describeSObject.isAccessible()){
                throw new System.NoAccessException();
            }
            Map<String,Schema.SObjectField> schemaFieldMap = describeSObject.fields.getMap();
            for(String fieldName : fieldMap.get(key)){
                Schema.DescribeFieldResult describeField = schemaFieldMap.get(fieldName).getDescribe();
                if(!describeField.isAccessible()){
                    throw new System.NoAccessException();
                }
            }
        }
}

And then to call it:

Set<String> fieldSet;
fieldSet = new Set<String>{'Id','Name'};
Map<String,Set<String>> fieldMap;
fieldMap = new Map<String,Set<String>>{'Account'=>fieldSet};
Utilities.checkReadAccess(fieldMap);
List<Account> templateList = [SELECT Id, Name FROM Account];

Running PMD through: Codacy

@jsotuyod
Copy link
Member

@trentchilders thanks for the report.

This is indeed a false positive, but it's one that PMD can't currently sort out. PMD is currently limited to same-file analysis. So, when it checks the code:

Set<String> fieldSet;
fieldSet = new Set<String>{'Id','Name'};
Map<String,Set<String>> fieldMap;
fieldMap = new Map<String,Set<String>>{'Account'=>fieldSet};
Utilities.checkReadAccess(fieldMap);
List<Account> templateList = [SELECT Id, Name FROM Account];

PMD has no idea what Utilities.checkReadAccess is, or what it does, and is therefore unable to waive this usage.

@jsotuyod jsotuyod added the a:false-positive PMD flags a piece of code that is not problematic label Oct 30, 2018
@trentchilders
Copy link
Author

Thanks for the reply!

@trentchilders
Copy link
Author

So, even by adding the method to the file, I still get the false positive. Is this expected behavior?

public static void Foo(){
   Set<String> fieldSet;
   fieldSet = new Set<String>{'Id','Name'};
   Map<String,Set<String>> fieldMap;
   fieldMap = new Map<String,Set<String>>{'Account'=>fieldSet};
   checkReadAccess(fieldMap);

   List<Account> templateList = [SELECT Id, Name FROM Account];
}

public static void checkReadAccess(Map<String,Set<String>> fieldMap){
        Set<String> keySet = fieldMap.keySet();
        
        for(String key : keySet){
            
            Schema.SObjectType objectType = Schema.getGlobalDescribe().get(key);
            
            Schema.DescribeSObjectResult describeSObject = objectType.getDescribe(); 
            if(!describeSObject.isAccessible()){
                throw new System.NoAccessException();
            }
            Map<String,Schema.SObjectField> schemaFieldMap = describeSObject.fields.getMap();
            for(String fieldName : fieldMap.get(key)){
                Schema.DescribeFieldResult describeField = schemaFieldMap.get(fieldName).getDescribe();
                if(!describeField.isAccessible()){
                    throw new System.NoAccessException();
                }
            }
        }

@jsotuyod
Copy link
Member

@trentchilders yes, this is expected. As you use a Map and Set, working this out would require not only having access to checkReadAccess but understanding how the map / set works (what get(key) does, etc.) and following through the code which values are in the map and set.

The one thing we support is a List of fields when using the ESAPI:

List<String> fieldsToView = new List<String> { 'Id', 'Name' };
if (ESAPI.accessController().isAuthorizedToView(Account.sObject, fieldsToView)) {
    …
}

but support for this is very tailor made.

Moreover, the check is currently very naive, as Apex has no real control flow support (unlike Java). This means, we can understand:

Account.sObjectType.getDescribe().isAccessible()

But we don't follow through a split statement

Schema.DescribeFieldResult describeField = Account.sObjectType.getDescribe()
describeField.isAccessible()

There is plenty of room for improvement, shall anyone be willing to take on this. Adding full support for control flow on Apex is a major task, but one that would greatly help many rules besides this one.

@boBNunny
Copy link

I know you are looking "in stream" for the Schema check, but maybe you could look for the same final method names like "isAccessible", "isCreatable", etc. In this way, we could create Utility methods to do the checks and make it standardized instead of repeating the multi-line IF statements for (in our case) 3000 SOQL's/DML's. We created 4 Utility methods to meet the "spirit" of the security check, but PMD still identifies them.

@jsotuyod jsotuyod added the needs:pmd7-revalidation The issue hasn't yet been retested vs PMD 7 and may be stale label Mar 17, 2024
@jsotuyod jsotuyod removed the needs:pmd7-revalidation The issue hasn't yet been retested vs PMD 7 and may be stale label Apr 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:false-positive PMD flags a piece of code that is not problematic
Projects
None yet
Development

No branches or pull requests

3 participants