/**
* 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.Set;
import java.util.regex.Pattern;
import net.sourceforge.pmd.lang.apex.ast.ASTAssignmentExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTBinaryExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.apex.ast.ASTLiteralExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression;
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;
/**
* Insecure HTTP endpoints passed to (req.setEndpoint)
* req.setHeader('Authorization') should use named credentials
*
* @author sergey.gorbaty
*
*/
public class ApexInsecureEndpointRule extends AbstractApexRule {
private static final String SET_ENDPOINT = "setEndpoint";
private static final Pattern PATTERN = Pattern.compile("^http://.+?$", Pattern.CASE_INSENSITIVE);
private final Set<String> httpEndpointStrings = new HashSet<>();
public ApexInsecureEndpointRule() {
setProperty(CODECLIMATE_CATEGORIES, new String[] { "Security" });
setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 100);
setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
}
@Override
public Object visit(ASTAssignmentExpression node, Object data) {
findInsecureEndpoints(node, data);
return data;
}
@Override
public Object visit(ASTVariableDeclaration node, Object data) {
findInsecureEndpoints(node, data);
return data;
}
@Override
public Object visit(ASTFieldDeclaration node, Object data) {
findInsecureEndpoints(node, data);
return data;
}
private void findInsecureEndpoints(AbstractApexNode<?> node, Object data) {
ASTVariableExpression variableNode = node.getFirstChildOfType(ASTVariableExpression.class);
findInnerInsecureEndpoints(node, variableNode);
ASTBinaryExpression binaryNode = node.getFirstChildOfType(ASTBinaryExpression.class);
if (binaryNode != null) {
findInnerInsecureEndpoints(binaryNode, variableNode);
}
}
private void findInnerInsecureEndpoints(AbstractApexNode<?> node, ASTVariableExpression variableNode) {
ASTLiteralExpression literalNode = node.getFirstChildOfType(ASTLiteralExpression.class);
if (literalNode != null && variableNode != null) {
Object o = literalNode.getNode().getLiteral();
if (o instanceof String) {
String literal = (String) o;
if (PATTERN.matcher(literal).matches()) {
httpEndpointStrings.add(Helper.getFQVariableName(variableNode));
}
}
}
}
@Override
public Object visit(ASTMethodCallExpression node, Object data) {
processInsecureEndpoint(node, data);
return data;
}
private void processInsecureEndpoint(ASTMethodCallExpression node, Object data) {
if (!Helper.isMethodName(node, SET_ENDPOINT)) {
return;
}
ASTBinaryExpression binaryNode = node.getFirstChildOfType(ASTBinaryExpression.class);
if (binaryNode != null) {
runChecks(binaryNode, data);
}
runChecks(node, data);
}
private void runChecks(AbstractApexNode<?> node, Object data) {
ASTLiteralExpression literalNode = node.getFirstChildOfType(ASTLiteralExpression.class);
if (literalNode != null) {
Object o = literalNode.getNode().getLiteral();
if (o instanceof String) {
String literal = (String) o;
if (PATTERN.matcher(literal).matches()) {
addViolation(data, literalNode);
}
}
}
ASTVariableExpression variableNode = node.getFirstChildOfType(ASTVariableExpression.class);
if (variableNode != null) {
if (httpEndpointStrings.contains(Helper.getFQVariableName(variableNode))) {
addViolation(data, variableNode);
}
}
}
}