New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[java] New security rule for finding hard-coded keys used for cryptographic operations #1046
Conversation
@jsotuyod heads up, one more rule. |
@sgorbaty the next release (PMD 6.4.0) is scheduled for May 27th. You can check all our next-release schedules (roughly once a month) on https://github.com/pmd/pmd/milestones This PR will definitely be part of 6.4.0, Andreas already scheduled it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, but I think I've some good feedback. We are still on plan to make it to the next release (6.4.0)
|
||
private <T> void extractPrimitiveTypesInner(Set<T> retVal, T field, List<ASTPrimitiveType> types) { | ||
for (ASTPrimitiveType type : types) { | ||
if (type.hasImageEqualTo("byte") || type.hasImageEqualTo("String")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a primitive type can never be a string
ASTPrimaryPrefix prefix = init.getFirstChildOfType(ASTPrimaryPrefix.class); | ||
if (prefix != null) { | ||
ASTLiteral literal = prefix.getFirstChildOfType(ASTLiteral.class); | ||
if (literal != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this check seems too relaxed... maybe also check if literal.isStringLiteral()
?
List<ASTAllocationExpression> allocations = node.findDescendantsOfType(ASTAllocationExpression.class); | ||
for (ASTAllocationExpression allocation : allocations) { | ||
|
||
ASTClassOrInterfaceType declClassName = allocation.getFirstDescendantOfType(ASTClassOrInterfaceType.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather than ask the rulechain to visit all ASTClassOrInterfaceBodyDeclaration
and then manually look for ASTAllocationExpression
, you could ask the rulechain to visit ASTAllocationExpression
for you and move this hole block to visit(ASTAllocationExpression node, Object data)
, which will probably be cleaner / easier to read
// intentionally | ||
} | ||
|
||
public static Set<String> findVariablesPassedToAnyParam(ASTClassOrInterfaceBodyDeclaration node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this utility class a little troubling for a number of reasons
- The name
Util
is overly generic - The name is misleading
findVariablesPassedToAnyParam
actually finds any variables passed to a constructor of the given type that's allocated anywhere within this node. I for one didn't expect that from the call inHardCodedCryptoKeyRule
until I read this... - I'm unsure this is common enough to require a utility or even considering moving it somewhere else (the AST?)
} | ||
|
||
for (ASTLocalVariableDeclaration foundLocalVar : foundLocalVars) { | ||
if (passedInIvVarNames.contains(foundLocalVar.getVariableName())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ASTLocalVariableDeclaration.getVariableName()
returns the name of the first variable declarator id, but there may be more in the case of byte var1, var2, var3;
and even the types may be slightly different thanks to arrays (ie: byte var1, var2[], var3[][];
).
The Javadoc for getVariableName
says this, but I fail to find a use-case where only the first one is going to be needed... I think ASTLocalVariableDeclaration
should implement Iterable<ASTVariableDeclarator>
and we should just use those...
Class<?> classToFind) { | ||
Set<String> passedInIvVarNames = new HashSet<>(); | ||
|
||
// find new javax.crypto.spec.SecretKeySpec(...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left over from copy-paste?
|
||
public static Set<String> findVariablesPassedToAnyParam(ASTClassOrInterfaceBodyDeclaration node, | ||
Class<?> classToFind) { | ||
Set<String> passedInIvVarNames = new HashSet<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the name remains too attached to a single case to be a generic utility
validateProperKey(data, var.getFirstDescendantOfType(ASTVariableInitializer.class)); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these 3 for loops are somewhat redundant and fail to deal with nested scopes properly (specially in the presence of lambdas and nested classes).
I'd just keep this second loop, but making sure to also iterate over the parent scopes.
If reworking this to start directly from the allocation instead of ASTClassOrInterfaceBodyDeclaration
you automatically start from the inner-most scope and can easily work your way up
This change is to identify and report hard-coded keys used in HMAC and encryption/decryption operations.