/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.apex.rule.security; import java.util.HashSet; import java.util.List; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.apex.ast.ASTField; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTVariableExpression; import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; /** * Flags dangerous method calls, e.g. FinancialForce * Configuration.disableTriggerCRUDSecurity() or System.debug with sensitive * input * * * @author sergey.gorbaty * */ public class ApexDangerousMethodsRule extends AbstractApexRule { private static final String BOOLEAN = "boolean"; private static final Pattern REGEXP = Pattern.compile("^.*?(pass|pwd|crypt|auth|session|token|saml)(?!id|user).*?$", Pattern.CASE_INSENSITIVE); private static final String DISABLE_CRUD = "disableTriggerCRUDSecurity"; private static final String CONFIGURATION = "Configuration"; private static final String SYSTEM = "System"; private static final String DEBUG = "debug"; private final HashSet<String> whiteListedVariables = new HashSet<>(); public ApexDangerousMethodsRule() { super.addRuleChainVisit(ASTUserClass.class); setProperty(CODECLIMATE_CATEGORIES, new String[] { "Security" }); setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 100); setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false); } public Object visit(ASTUserClass node, Object data) { if (Helper.isTestMethodOrClass(node)) { return data; } collectBenignVariables(node); List<ASTMethodCallExpression> methodCalls = node.findDescendantsOfType(ASTMethodCallExpression.class); for (ASTMethodCallExpression methodCall : methodCalls) { if (Helper.isMethodName(methodCall, CONFIGURATION, DISABLE_CRUD)) { addViolation(data, methodCall); } if (Helper.isMethodName(methodCall, SYSTEM, DEBUG)) { validateParameters(methodCall, data); } } whiteListedVariables.clear(); return data; } private void collectBenignVariables(ASTUserClass node) { List<ASTField> fields = node.findDescendantsOfType(ASTField.class); for (ASTField field : fields) { if (BOOLEAN.equalsIgnoreCase(field.getNode().getFieldInfo().getType().getApexName())) { whiteListedVariables.add(Helper.getFQVariableName(field)); } } List<ASTVariableDeclaration> declarations = node.findDescendantsOfType(ASTVariableDeclaration.class); for (ASTVariableDeclaration decl : declarations) { if (BOOLEAN.equalsIgnoreCase(decl.getNode().getLocalInfo().getType().getApexName())) { whiteListedVariables.add(Helper.getFQVariableName(decl)); } } } private void validateParameters(ASTMethodCallExpression methodCall, Object data) { List<ASTVariableExpression> variables = methodCall.findDescendantsOfType(ASTVariableExpression.class); for (ASTVariableExpression var : variables) { if (REGEXP.matcher(var.getNode().getIdentifier().value).matches()) { if (!whiteListedVariables.contains(Helper.getFQVariableName(var))) { addViolation(data, methodCall); } } } } }