diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentials.qll b/java/ql/lib/semmle/code/java/security/HardcodedCredentials.qll similarity index 100% rename from java/ql/src/Security/CWE/CWE-798/HardcodedCredentials.qll rename to java/ql/lib/semmle/code/java/security/HardcodedCredentials.qll diff --git a/java/ql/lib/semmle/code/java/security/HardcodedCredentialsApiCallQuery.qll b/java/ql/lib/semmle/code/java/security/HardcodedCredentialsApiCallQuery.qll new file mode 100644 index 00000000000..2fdec983b31 --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/HardcodedCredentialsApiCallQuery.qll @@ -0,0 +1,54 @@ +/** + * Provides a data-flow configuration for tracking a hard-coded credential in a call to a sensitive Java API which may compromise security. + */ + +import java +import semmle.code.java.dataflow.DataFlow +import HardcodedCredentials + +/** + * Tracks flow from a hard-coded credential in a call to a sensitive Java API which may compromise security. + */ +class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration { + HardcodedCredentialApiCallConfiguration() { this = "HardcodedCredentialApiCallConfiguration" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr() instanceof HardcodedExpr and + not n.asExpr().getEnclosingCallable() instanceof ToStringMethod + } + + override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsApiSink } + + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.asExpr().getType() instanceof TypeString and + ( + exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) | + node2.asExpr() = ma and + ma.getQualifier() = node1.asExpr() + ) + or + // These base64 routines are usually taint propagators, and this is not a general + // TaintTracking::Configuration, so we must specifically include them here + // as a common transform applied to a constant before passing to a remote API. + exists(MethodAccess ma | + ma.getMethod() + .hasQualifiedName([ + "java.util", "cn.hutool.core.codec", "org.apache.shiro.codec", + "apache.commons.codec.binary", "org.springframework.util" + ], ["Base64$Encoder", "Base64$Decoder", "Base64", "Base64Utils"], + [ + "encode", "encodeToString", "decode", "decodeBase64", "encodeBase64", + "encodeBase64Chunked", "encodeBase64String", "encodeBase64URLSafe", + "encodeBase64URLSafeString" + ]) + | + node1.asExpr() = ma.getArgument(0) and + node2.asExpr() = ma + ) + ) + } + + override predicate isBarrier(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod() instanceof MethodSystemGetenv + } +} diff --git a/java/ql/lib/semmle/code/java/security/HardcodedCredentialsComparison.qll b/java/ql/lib/semmle/code/java/security/HardcodedCredentialsComparison.qll new file mode 100644 index 00000000000..7f668b90bc1 --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/HardcodedCredentialsComparison.qll @@ -0,0 +1,25 @@ +/** + * Provides classes and predicates to detect comparing a parameter to a hard-coded credential. + */ + +import HardcodedCredentials + +/** + * A call to a method that is or overrides `java.lang.Object.equals`. + */ +class EqualsAccess extends MethodAccess { + EqualsAccess() { getMethod() instanceof EqualsMethod } +} + +/** + * Holds if `sink` compares password `p` against a hardcoded expression `source`. + */ +predicate isHardcodedCredentialsComparison( + EqualsAccess sink, HardcodedExpr source, PasswordVariable p +) { + source = sink.getQualifier() and + p.getAnAccess() = sink.getArgument(0) + or + source = sink.getArgument(0) and + p.getAnAccess() = sink.getQualifier() +} diff --git a/java/ql/lib/semmle/code/java/security/HardcodedCredentialsSourceCall.qll b/java/ql/lib/semmle/code/java/security/HardcodedCredentialsSourceCall.qll new file mode 100644 index 00000000000..7125ffda088 --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/HardcodedCredentialsSourceCall.qll @@ -0,0 +1,51 @@ +/** + * Provides classes to detect using a hard-coded credential in a sensitive call. + */ + +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.DataFlow2 +import HardcodedCredentials + +/** + * A data-flow configuration that tracks hardcoded expressions flowing to a parameter whose name suggests + * it may be a credential, excluding those which flow on to other such insecure usage sites. + */ +class HardcodedCredentialSourceCallConfiguration extends DataFlow::Configuration { + HardcodedCredentialSourceCallConfiguration() { + this = "HardcodedCredentialSourceCallConfiguration" + } + + override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof HardcodedExpr } + + override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof FinalCredentialsSourceSink } +} + +/** + * A data-flow configuration that tracks flow from an argument whose corresponding parameter name suggests + * a credential, to an argument to a sensitive call. + */ +class HardcodedCredentialSourceCallConfiguration2 extends DataFlow2::Configuration { + HardcodedCredentialSourceCallConfiguration2() { + this = "HardcodedCredentialSourceCallConfiguration2" + } + + override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof CredentialsSourceSink } + + override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsSink } +} + +/** + * An argument to a call, where the parameter name corresponding + * to the argument indicates that it may contain credentials, and + * where this expression does not flow on to another `CredentialsSink`. + */ +class FinalCredentialsSourceSink extends CredentialsSourceSink { + FinalCredentialsSourceSink() { + not exists(HardcodedCredentialSourceCallConfiguration2 conf, CredentialsSink other | + this != other + | + conf.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(other)) + ) + } +} diff --git a/java/ql/lib/semmle/code/java/security/HardcodedPasswordField.qll b/java/ql/lib/semmle/code/java/security/HardcodedPasswordField.qll new file mode 100644 index 00000000000..4127718af0b --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/HardcodedPasswordField.qll @@ -0,0 +1,14 @@ +/** + * Provides a predicate identifying assignments of harcoded values to password fields. + */ + +import HardcodedCredentials + +/** + * Holds if non-empty constant value `e` is assigned to password field `f`. + */ +predicate passwordFieldAssignedHardcodedValue(PasswordVariable f, CompileTimeConstantExpr e) { + f instanceof Field and + f.getAnAssignedValue() = e and + not e.(StringLiteral).getValue() = "" +} diff --git a/java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll b/java/ql/lib/semmle/code/java/security/SensitiveApi.qll similarity index 100% rename from java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll rename to java/ql/lib/semmle/code/java/security/SensitiveApi.qll diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql index a787d2ddfd3..2b84f4b8065 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql @@ -10,55 +10,9 @@ * external/cwe/cwe-798 */ -import java -import semmle.code.java.dataflow.DataFlow -import HardcodedCredentials +import semmle.code.java.security.HardcodedCredentialsApiCallQuery import DataFlow::PathGraph -class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration { - HardcodedCredentialApiCallConfiguration() { this = "HardcodedCredentialApiCallConfiguration" } - - override predicate isSource(DataFlow::Node n) { - n.asExpr() instanceof HardcodedExpr and - not n.asExpr().getEnclosingCallable() instanceof ToStringMethod - } - - override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsApiSink } - - override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - node1.asExpr().getType() instanceof TypeString and - ( - exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) | - node2.asExpr() = ma and - ma.getQualifier() = node1.asExpr() - ) - or - // These base64 routines are usually taint propagators, and this is not a general - // TaintTracking::Configuration, so we must specifically include them here - // as a common transform applied to a constant before passing to a remote API. - exists(MethodAccess ma | - ma.getMethod() - .hasQualifiedName([ - "java.util", "cn.hutool.core.codec", "org.apache.shiro.codec", - "apache.commons.codec.binary", "org.springframework.util" - ], ["Base64$Encoder", "Base64$Decoder", "Base64", "Base64Utils"], - [ - "encode", "encodeToString", "decode", "decodeBase64", "encodeBase64", - "encodeBase64Chunked", "encodeBase64String", "encodeBase64URLSafe", - "encodeBase64URLSafeString" - ]) - | - node1.asExpr() = ma.getArgument(0) and - node2.asExpr() = ma - ) - ) - } - - override predicate isBarrier(DataFlow::Node n) { - n.asExpr().(MethodAccess).getMethod() instanceof MethodSystemGetenv - } -} - from DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedCredentialApiCallConfiguration conf where conf.hasFlowPath(source, sink) diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql index d43530f7d69..4a21fcc5e92 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql @@ -11,17 +11,8 @@ */ import java -import HardcodedCredentials - -class EqualsAccess extends MethodAccess { - EqualsAccess() { getMethod() instanceof EqualsMethod } -} +import semmle.code.java.security.HardcodedCredentialsComparison from EqualsAccess sink, HardcodedExpr source, PasswordVariable p -where - source = sink.getQualifier() and - p.getAnAccess() = sink.getArgument(0) - or - source = sink.getArgument(0) and - p.getAnAccess() = sink.getQualifier() +where isHardcodedCredentialsComparison(sink, source, p) select source, "Hard-coded value is $@ with password variable $@.", sink, "compared", p, p.getName() diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql index e14188905fa..6b5c78c1f05 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql @@ -11,41 +11,9 @@ */ import java -import semmle.code.java.dataflow.DataFlow -import semmle.code.java.dataflow.DataFlow2 -import HardcodedCredentials +import semmle.code.java.security.HardcodedCredentialsSourceCall import DataFlow::PathGraph -class HardcodedCredentialSourceCallConfiguration extends DataFlow::Configuration { - HardcodedCredentialSourceCallConfiguration() { - this = "HardcodedCredentialSourceCallConfiguration" - } - - override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof HardcodedExpr } - - override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof FinalCredentialsSourceSink } -} - -class HardcodedCredentialSourceCallConfiguration2 extends DataFlow2::Configuration { - HardcodedCredentialSourceCallConfiguration2() { - this = "HardcodedCredentialSourceCallConfiguration2" - } - - override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof CredentialsSourceSink } - - override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsSink } -} - -class FinalCredentialsSourceSink extends CredentialsSourceSink { - FinalCredentialsSourceSink() { - not exists(HardcodedCredentialSourceCallConfiguration2 conf, CredentialsSink other | - this != other - | - conf.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(other)) - ) - } -} - from DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedCredentialSourceCallConfiguration conf diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql index 0a98c000300..d148b8d605d 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql @@ -11,11 +11,8 @@ */ import java -import HardcodedCredentials +import semmle.code.java.security.HardcodedPasswordField from PasswordVariable f, CompileTimeConstantExpr e -where - f instanceof Field and - f.getAnAssignedValue() = e and - not e.(StringLiteral).getValue() = "" +where passwordFieldAssignedHardcodedValue(f, e) select f, "Sensitive field is assigned a hard-coded $@.", e, "value"