Split up hardcoded creds queries, ready for conversion to inline expectations

This commit is contained in:
Chris Smowton
2022-05-30 21:06:45 +01:00
parent 0a6ccbca45
commit ddb0846e06
10 changed files with 150 additions and 96 deletions

View File

@@ -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
}
}

View File

@@ -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()
}

View File

@@ -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))
)
}
}

View File

@@ -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() = ""
}

View File

@@ -10,55 +10,9 @@
* external/cwe/cwe-798 * external/cwe/cwe-798
*/ */
import java import semmle.code.java.security.HardcodedCredentialsApiCallQuery
import semmle.code.java.dataflow.DataFlow
import HardcodedCredentials
import DataFlow::PathGraph 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 from
DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedCredentialApiCallConfiguration conf DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedCredentialApiCallConfiguration conf
where conf.hasFlowPath(source, sink) where conf.hasFlowPath(source, sink)

View File

@@ -11,17 +11,8 @@
*/ */
import java import java
import HardcodedCredentials import semmle.code.java.security.HardcodedCredentialsComparison
class EqualsAccess extends MethodAccess {
EqualsAccess() { getMethod() instanceof EqualsMethod }
}
from EqualsAccess sink, HardcodedExpr source, PasswordVariable p from EqualsAccess sink, HardcodedExpr source, PasswordVariable p
where where isHardcodedCredentialsComparison(sink, source, p)
source = sink.getQualifier() and
p.getAnAccess() = sink.getArgument(0)
or
source = sink.getArgument(0) and
p.getAnAccess() = sink.getQualifier()
select source, "Hard-coded value is $@ with password variable $@.", sink, "compared", p, p.getName() select source, "Hard-coded value is $@ with password variable $@.", sink, "compared", p, p.getName()

View File

@@ -11,41 +11,9 @@
*/ */
import java import java
import semmle.code.java.dataflow.DataFlow import semmle.code.java.security.HardcodedCredentialsSourceCall
import semmle.code.java.dataflow.DataFlow2
import HardcodedCredentials
import DataFlow::PathGraph 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 from
DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::PathNode source, DataFlow::PathNode sink,
HardcodedCredentialSourceCallConfiguration conf HardcodedCredentialSourceCallConfiguration conf

View File

@@ -11,11 +11,8 @@
*/ */
import java import java
import HardcodedCredentials import semmle.code.java.security.HardcodedPasswordField
from PasswordVariable f, CompileTimeConstantExpr e from PasswordVariable f, CompileTimeConstantExpr e
where where passwordFieldAssignedHardcodedValue(f, e)
f instanceof Field and
f.getAnAssignedValue() = e and
not e.(StringLiteral).getValue() = ""
select f, "Sensitive field is assigned a hard-coded $@.", e, "value" select f, "Sensitive field is assigned a hard-coded $@.", e, "value"