Merge pull request #3211 from jonathanwiesel:no-assume-apex-getter-vf
[apex] ApexCRUDViolationRule: Do not assume method is VF getter to avoid CRUD checks #3211
This commit is contained in:
commit
1985f72d2a
@ -27,6 +27,13 @@ This is a {{ site.pmd.release_type }} release.
|
||||
|
||||
This rule is also part of the Quickstart Ruleset (`rulesets/java/quickstart.xml`) for Java.
|
||||
|
||||
#### Modified rules
|
||||
|
||||
* The Apex rule {% rule "apex/security/ApexCRUDViolation" %} does not ignore getters anymore and also flags
|
||||
SOQL/SOSL/DML operations without access permission checks in getters. This will produce false positives now for
|
||||
VF getter methods, but we can't reliably detect, whether a getter is a VF getter or not. In such cases,
|
||||
the violation should be [suppressed](pmd_userdocs_suppressing_warnings.html).
|
||||
|
||||
#### Deprecated rules
|
||||
|
||||
* java-bestpractices
|
||||
@ -39,11 +46,12 @@ This is a {{ site.pmd.release_type }} release.
|
||||
* java-errorprone
|
||||
* {% rule java/errorprone/ImportFromSamePackage %}: use the rule {% rule "java/codestyle/UnnecessaryImport" %} instead
|
||||
|
||||
|
||||
### Fixed Issues
|
||||
|
||||
* apex-performance
|
||||
* [#3198](https://github.com/pmd/pmd/pull/3198): \[apex] OperationWithLimitsInLoopRule: Support more limit consuming static method invocations
|
||||
* apex-security
|
||||
* [#3210](https://github.com/pmd/pmd/issues/3210): \[apex] ApexCRUDViolationRule false-negative on non-VF getter
|
||||
* java-bestpractices
|
||||
* [#3190](https://github.com/pmd/pmd/issues/3190): \[java] Use StandardCharsets instead of Charset.forName
|
||||
* [#3224](https://github.com/pmd/pmd/issues/3224): \[java] UnusedAssignment crashes with nested records
|
||||
@ -59,6 +67,7 @@ This is a {{ site.pmd.release_type }} release.
|
||||
|
||||
* [#3193](https://github.com/pmd/pmd/pull/3193): \[java] New rule: UseStandardCharsets - [Andrea Aime](https://github.com/aaime)
|
||||
* [#3198](https://github.com/pmd/pmd/pull/3198): \[apex] OperationWithLimitsInLoopRule: Support more limit consuming static method invocations - [Jonathan Wiesel](https://github.com/jonathanwiesel)
|
||||
* [#3211](https://github.com/pmd/pmd/pull/3211): \[apex] ApexCRUDViolationRule: Do not assume method is VF getter to avoid CRUD checks - [Jonathan Wiesel](https://github.com/jonathanwiesel)
|
||||
|
||||
{% endtocmaker %}
|
||||
|
||||
|
@ -32,7 +32,6 @@ import net.sourceforge.pmd.lang.apex.ast.ASTIfElseBlockStatement;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTNewKeyValueObjectExpression;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTParameter;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTProperty;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTReferenceExpression;
|
||||
import net.sourceforge.pmd.lang.apex.ast.ASTReturnStatement;
|
||||
@ -56,7 +55,6 @@ import com.google.common.collect.ListMultimap;
|
||||
*
|
||||
*/
|
||||
public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
private static final Pattern VOID_OR_STRING_PATTERN = Pattern.compile("^(string|void)$", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern SELECT_FROM_PATTERN = Pattern.compile("[\\S|\\s]+?FROM[\\s]+?(\\w+)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@ -514,7 +512,6 @@ public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
collectCRUDMethodLevelChecks(prevCall);
|
||||
}
|
||||
|
||||
boolean isGetter = false;
|
||||
String returnType = null;
|
||||
|
||||
final ASTMethod wrappingMethod = node.getFirstParentOfType(ASTMethod.class);
|
||||
@ -527,7 +524,6 @@ public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
}
|
||||
|
||||
if (wrappingMethod != null) {
|
||||
isGetter = isMethodAGetter(wrappingMethod);
|
||||
returnType = getReturnType(wrappingMethod);
|
||||
}
|
||||
|
||||
@ -538,13 +534,11 @@ public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
StringBuilder typeCheck = new StringBuilder().append(variableDecl.getDefiningType())
|
||||
.append(":").append(type);
|
||||
|
||||
if (!isGetter) {
|
||||
if (typesFromSOQL.isEmpty()) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeCheck.toString());
|
||||
} else {
|
||||
for (String typeFromSOQL : typesFromSOQL) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeFromSOQL);
|
||||
}
|
||||
if (typesFromSOQL.isEmpty()) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeCheck.toString());
|
||||
} else {
|
||||
for (String typeFromSOQL : typesFromSOQL) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeFromSOQL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -557,15 +551,12 @@ public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
String variableWithClass = Helper.getFQVariableName(variable);
|
||||
if (varToTypeMapping.containsKey(variableWithClass)) {
|
||||
String type = varToTypeMapping.get(variableWithClass);
|
||||
if (!isGetter) {
|
||||
if (typesFromSOQL.isEmpty()) {
|
||||
validateCRUDCheckPresent(node, data, ANY, type);
|
||||
} else {
|
||||
for (String typeFromSOQL : typesFromSOQL) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeFromSOQL);
|
||||
}
|
||||
if (typesFromSOQL.isEmpty()) {
|
||||
validateCRUDCheckPresent(node, data, ANY, type);
|
||||
} else {
|
||||
for (String typeFromSOQL : typesFromSOQL) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeFromSOQL);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -574,13 +565,11 @@ public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
|
||||
final ASTReturnStatement returnStatement = node.getFirstParentOfType(ASTReturnStatement.class);
|
||||
if (returnStatement != null) {
|
||||
if (!isGetter) {
|
||||
if (typesFromSOQL.isEmpty()) {
|
||||
validateCRUDCheckPresent(node, data, ANY, returnType);
|
||||
} else {
|
||||
for (String typeFromSOQL : typesFromSOQL) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeFromSOQL);
|
||||
}
|
||||
if (typesFromSOQL.isEmpty()) {
|
||||
validateCRUDCheckPresent(node, data, ANY, returnType);
|
||||
} else {
|
||||
for (String typeFromSOQL : typesFromSOQL) {
|
||||
validateCRUDCheckPresent(node, data, ANY, typeFromSOQL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -602,13 +591,4 @@ public class ApexCRUDViolationRule extends AbstractApexRule {
|
||||
return new StringBuilder().append(method.getDefiningType()).append(":")
|
||||
.append(method.getReturnType()).toString();
|
||||
}
|
||||
|
||||
private boolean isMethodAGetter(final ASTMethod method) {
|
||||
final boolean startsWithGet = method.getCanonicalName().startsWith("get");
|
||||
final boolean voidOrString = VOID_OR_STRING_PATTERN
|
||||
.matcher(method.getReturnType()).matches();
|
||||
final boolean noParams = method.findChildrenOfType(ASTParameter.class).isEmpty();
|
||||
|
||||
return startsWithGet && noParams && !voidOrString;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,11 @@ public without sharing class Foo {
|
||||
The rule validates you are checking for access permissions before a SOQL/SOSL/DML operation.
|
||||
Since Apex runs in system mode not having proper permissions checks results in escalation of
|
||||
privilege and may produce runtime errors. This check forces you to handle such scenarios.
|
||||
|
||||
Note: This rule will produce false positives for VF getter methods. In VF getters the access permission
|
||||
check happens automatically and is not needed explicitly. However, the rule can't reliably determine
|
||||
whether a getter is a VF getter or not and reports a violation in any case. In such cases, the violation
|
||||
should be [suppressed](pmd_userdocs_suppressing_warnings.html).
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
|
@ -135,8 +135,8 @@ public class Foo {
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>VF built-in CRUD checks via getter</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<description>VF built-in CRUD checks via getter, but cannot determine if is really VF</description>
|
||||
<expected-problems>2</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public Contact getFoo() {
|
||||
@ -153,35 +153,6 @@ public class Foo {
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Bypassing VF built-in CRUD checks via getter method
|
||||
</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public String getUserName() {
|
||||
String tempID = 'someID';
|
||||
Contact c = [SELECT Name FROM Contact WHERE Id=:tempID];
|
||||
return c.Name;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>No VF provided CRUD checks via getter method
|
||||
</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public Contact justGiveMeFoo() {
|
||||
String tempID = 'someID';
|
||||
return [SELECT Name FROM Contact WHERE Id=:tempID];
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Proper CRUD checks</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
|
Loading…
x
Reference in New Issue
Block a user