/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.apex.rule.security; import java.util.List; import java.util.WeakHashMap; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; import apex.jorje.semantic.ast.modifier.OldModifiers.ModifierType; import apex.jorje.semantic.symbol.type.ModifierOrAnnotationTypeInfo; /** * Finds Apex class that do not define sharing * * @author sergey.gorbaty */ public class ApexSharingViolationsRule extends AbstractApexRule { private WeakHashMap<ApexNode<?>, Object> localCacheOfReportedNodes = new WeakHashMap<>(); public ApexSharingViolationsRule() { setProperty(CODECLIMATE_CATEGORIES, new String[] { "Security" }); setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 100); setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false); } @Override public Object visit(ASTUserClass node, Object data) { if (Helper.isTestMethodOrClass(node) || Helper.isSystemLevelClass(node)) { return data; // stops all the rules } if (!Helper.isTestMethodOrClass(node)) { boolean sharingFound = isSharingPresent(node); checkForSharingDeclaration(node, data, sharingFound); checkForDatabaseMethods(node, data, sharingFound); } localCacheOfReportedNodes.clear(); return data; } /** * Check if class contains any Database.query / Database.insert [ Database.* * ] methods * * @param node * @param data */ private void checkForDatabaseMethods(ASTUserClass node, Object data, boolean sharingFound) { List<ASTMethodCallExpression> calls = node.findDescendantsOfType(ASTMethodCallExpression.class); for (ASTMethodCallExpression call : calls) { if (Helper.isMethodName(call, "Database", Helper.ANY_METHOD)) { if (!sharingFound) { reportViolation(node, data); } } } } private void reportViolation(ApexNode<?> node, Object data) { ASTModifierNode modifier = node.getFirstChildOfType(ASTModifierNode.class); if (modifier != null) { if (localCacheOfReportedNodes.put(modifier, data) == null) { addViolation(data, modifier); } } else { if (localCacheOfReportedNodes.put(node, data) == null) { addViolation(data, node); } } } /** * Check if class has no sharing declared * * @param node * @param data */ private void checkForSharingDeclaration(ApexNode<?> node, Object data, boolean sharingFound) { final boolean foundAnyDMLorSOQL = Helper.foundAnyDML(node) || Helper.foundAnySOQLorSOSL(node); if (!sharingFound && !Helper.isTestMethodOrClass(node) && foundAnyDMLorSOQL) { reportViolation(node, data); } } /** * Does class have sharing keyword declared? * * @param node * @return */ private boolean isSharingPresent(ApexNode<?> node) { boolean sharingFound = false; for (ModifierOrAnnotationTypeInfo type : node.getNode().getDefiningType().getModifiers().all()) { if (type.getBytecodeName().equalsIgnoreCase(ModifierType.WithoutSharing.toString())) { sharingFound = true; break; } if (type.getBytecodeName().equalsIgnoreCase(ModifierType.WithSharing.toString())) { sharingFound = true; break; } } return sharingFound; } }