/** * 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.Set; import net.sourceforge.pmd.lang.apex.ast.ASTBinaryExpression; import net.sourceforge.pmd.lang.apex.ast.ASTField; import net.sourceforge.pmd.lang.apex.ast.ASTLiteralExpression; 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.ast.AbstractApexNode; import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; /** * Flags usage of http request.setHeader('Authorization',..) and suggests using * named credentials which helps store credentials for the callout in a safe * place. * * @author sergey.gorbaty * */ public class ApexSuggestUsingNamedCredRule extends AbstractApexRule { private static final String SET_HEADER = "setHeader"; private static final String AUTHORIZATION = "Authorization"; private final Set<String> listOfAuthorizationVariables = new HashSet<>(); public ApexSuggestUsingNamedCredRule() { super.addRuleChainVisit(ASTUserClass.class); 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)) { return data; } List<ASTVariableDeclaration> variableDecls = node.findDescendantsOfType(ASTVariableDeclaration.class); for (ASTVariableDeclaration varDecl : variableDecls) { findAuthLiterals(varDecl); } List<ASTField> fieldDecl = node.findDescendantsOfType(ASTField.class); for (ASTField fDecl : fieldDecl) { findFieldLiterals(fDecl); } List<ASTMethodCallExpression> methodCalls = node.findDescendantsOfType(ASTMethodCallExpression.class); for (ASTMethodCallExpression method : methodCalls) { flagAuthorizationHeaders(method, data); } listOfAuthorizationVariables.clear(); return data; } private void findFieldLiterals(final ASTField fDecl) { Object f = fDecl.getNode().getFieldInfo().getValue(); if (f != null && f instanceof String) { final String fieldValue = (String) f; if (AUTHORIZATION.equalsIgnoreCase(fieldValue)) { listOfAuthorizationVariables.add(Helper.getFQVariableName(fDecl)); } } } private void flagAuthorizationHeaders(final ASTMethodCallExpression node, Object data) { if (!Helper.isMethodName(node, SET_HEADER)) { return; } final ASTBinaryExpression binaryNode = node.getFirstChildOfType(ASTBinaryExpression.class); if (binaryNode != null) { runChecks(binaryNode, data); } runChecks(node, data); } private void findAuthLiterals(final AbstractApexNode<?> node) { ASTLiteralExpression literal = node.getFirstChildOfType(ASTLiteralExpression.class); if (literal != null) { ASTVariableExpression variable = node.getFirstChildOfType(ASTVariableExpression.class); if (variable != null) { if (isAuthorizationLiteral(literal)) { listOfAuthorizationVariables.add(Helper.getFQVariableName(variable)); } } } } private void runChecks(final AbstractApexNode<?> node, Object data) { ASTLiteralExpression literalNode = node.getFirstChildOfType(ASTLiteralExpression.class); if (literalNode != null) { if (isAuthorizationLiteral(literalNode)) { addViolation(data, literalNode); } } final ASTVariableExpression varNode = node.getFirstChildOfType(ASTVariableExpression.class); if (varNode != null) { if (listOfAuthorizationVariables.contains(Helper.getFQVariableName(varNode))) { addViolation(data, varNode); } } } private boolean isAuthorizationLiteral(final ASTLiteralExpression literal) { Object o = literal.getNode().getLiteral(); if (o instanceof String) { String lit = (String) o; if (lit.equalsIgnoreCase(AUTHORIZATION)) { return true; } } return false; } }