/**
* 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.ASTFieldDeclaration;
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;
/**
* Finds encryption schemes using hardcoded IV, hardcoded key
*
* @author sergey.gorbaty
*
*/
public class ApexBadCryptoRule extends AbstractApexRule {
private static final String VALUE_OF = "valueOf";
private static final String BLOB = "Blob";
private static final String ENCRYPT = "encrypt";
private static final String DECRYPT = "decrypt";
private static final String CRYPTO = "Crypto";
private static final String ENCRYPT_WITH_MANAGED_IV = "encryptWithManagedIV";
private static final String DECRYPT_WITH_MANAGED_IV = "decryptWithManagedIV";
private final Set<String> potentiallyStaticBlob = new HashSet<>();
public ApexBadCryptoRule() {
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<ASTFieldDeclaration> fieldDecl = node.findDescendantsOfType(ASTFieldDeclaration.class);
for (ASTFieldDeclaration var : fieldDecl) {
findSafeVariables(var);
}
List<ASTVariableDeclaration> variableDecl = node.findDescendantsOfType(ASTVariableDeclaration.class);
for (ASTVariableDeclaration var : variableDecl) {
findSafeVariables(var);
}
List<ASTMethodCallExpression> methodCalls = node.findDescendantsOfType(ASTMethodCallExpression.class);
for (ASTMethodCallExpression methodCall : methodCalls) {
if (Helper.isMethodName(methodCall, CRYPTO, ENCRYPT) || Helper.isMethodName(methodCall, CRYPTO, DECRYPT)
|| Helper.isMethodName(methodCall, CRYPTO, ENCRYPT_WITH_MANAGED_IV)
|| Helper.isMethodName(methodCall, CRYPTO, DECRYPT_WITH_MANAGED_IV)) {
validateStaticIVorKey(methodCall, data);
}
}
potentiallyStaticBlob.clear();
return data;
}
private void findSafeVariables(AbstractApexNode<?> var) {
ASTMethodCallExpression methodCall = var.getFirstChildOfType(ASTMethodCallExpression.class);
if (methodCall != null && Helper.isMethodName(methodCall, BLOB, VALUE_OF)) {
ASTVariableExpression variable = var.getFirstChildOfType(ASTVariableExpression.class);
if (variable != null) {
potentiallyStaticBlob.add(Helper.getFQVariableName(variable));
}
}
}
private void validateStaticIVorKey(ASTMethodCallExpression methodCall, Object data) {
// .encrypt('AES128', key, exampleIv, data);
int numberOfChildren = methodCall.jjtGetNumChildren();
switch (numberOfChildren) {
// matching signature to encrypt(
case 5:
Object potentialIV = methodCall.jjtGetChild(3);
reportIfHardCoded(data, potentialIV);
Object potentialKey = methodCall.jjtGetChild(2);
reportIfHardCoded(data, potentialKey);
break;
// matching signature to encryptWithManagedIV(
case 4:
Object key = methodCall.jjtGetChild(2);
reportIfHardCoded(data, key);
break;
default:
break;
}
}
private void reportIfHardCoded(Object data, Object potentialIV) {
if (potentialIV instanceof ASTVariableExpression) {
ASTVariableExpression variable = (ASTVariableExpression) potentialIV;
if (potentiallyStaticBlob.contains(Helper.getFQVariableName(variable))) {
addViolation(data, variable);
}
}
}
}