Skip to content

Commit ddb0846

Browse files
committed
Split up hardcoded creds queries, ready for conversion to inline expectations
1 parent 0a6ccbc commit ddb0846

10 files changed

+150
-96
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Provides a data-flow configuration for tracking a hard-coded credential in a call to a sensitive Java API which may compromise security.
3+
*/
4+
5+
import java
6+
import semmle.code.java.dataflow.DataFlow
7+
import HardcodedCredentials
8+
9+
/**
10+
* Tracks flow from a hard-coded credential in a call to a sensitive Java API which may compromise security.
11+
*/
12+
class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration {
13+
HardcodedCredentialApiCallConfiguration() { this = "HardcodedCredentialApiCallConfiguration" }
14+
15+
override predicate isSource(DataFlow::Node n) {
16+
n.asExpr() instanceof HardcodedExpr and
17+
not n.asExpr().getEnclosingCallable() instanceof ToStringMethod
18+
}
19+
20+
override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsApiSink }
21+
22+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
23+
node1.asExpr().getType() instanceof TypeString and
24+
(
25+
exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) |
26+
node2.asExpr() = ma and
27+
ma.getQualifier() = node1.asExpr()
28+
)
29+
or
30+
// These base64 routines are usually taint propagators, and this is not a general
31+
// TaintTracking::Configuration, so we must specifically include them here
32+
// as a common transform applied to a constant before passing to a remote API.
33+
exists(MethodAccess ma |
34+
ma.getMethod()
35+
.hasQualifiedName([
36+
"java.util", "cn.hutool.core.codec", "org.apache.shiro.codec",
37+
"apache.commons.codec.binary", "org.springframework.util"
38+
], ["Base64$Encoder", "Base64$Decoder", "Base64", "Base64Utils"],
39+
[
40+
"encode", "encodeToString", "decode", "decodeBase64", "encodeBase64",
41+
"encodeBase64Chunked", "encodeBase64String", "encodeBase64URLSafe",
42+
"encodeBase64URLSafeString"
43+
])
44+
|
45+
node1.asExpr() = ma.getArgument(0) and
46+
node2.asExpr() = ma
47+
)
48+
)
49+
}
50+
51+
override predicate isBarrier(DataFlow::Node n) {
52+
n.asExpr().(MethodAccess).getMethod() instanceof MethodSystemGetenv
53+
}
54+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Provides classes and predicates to detect comparing a parameter to a hard-coded credential.
3+
*/
4+
5+
import HardcodedCredentials
6+
7+
/**
8+
* A call to a method that is or overrides `java.lang.Object.equals`.
9+
*/
10+
class EqualsAccess extends MethodAccess {
11+
EqualsAccess() { getMethod() instanceof EqualsMethod }
12+
}
13+
14+
/**
15+
* Holds if `sink` compares password `p` against a hardcoded expression `source`.
16+
*/
17+
predicate isHardcodedCredentialsComparison(
18+
EqualsAccess sink, HardcodedExpr source, PasswordVariable p
19+
) {
20+
source = sink.getQualifier() and
21+
p.getAnAccess() = sink.getArgument(0)
22+
or
23+
source = sink.getArgument(0) and
24+
p.getAnAccess() = sink.getQualifier()
25+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Provides classes to detect using a hard-coded credential in a sensitive call.
3+
*/
4+
5+
import java
6+
import semmle.code.java.dataflow.DataFlow
7+
import semmle.code.java.dataflow.DataFlow2
8+
import HardcodedCredentials
9+
10+
/**
11+
* A data-flow configuration that tracks hardcoded expressions flowing to a parameter whose name suggests
12+
* it may be a credential, excluding those which flow on to other such insecure usage sites.
13+
*/
14+
class HardcodedCredentialSourceCallConfiguration extends DataFlow::Configuration {
15+
HardcodedCredentialSourceCallConfiguration() {
16+
this = "HardcodedCredentialSourceCallConfiguration"
17+
}
18+
19+
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof HardcodedExpr }
20+
21+
override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof FinalCredentialsSourceSink }
22+
}
23+
24+
/**
25+
* A data-flow configuration that tracks flow from an argument whose corresponding parameter name suggests
26+
* a credential, to an argument to a sensitive call.
27+
*/
28+
class HardcodedCredentialSourceCallConfiguration2 extends DataFlow2::Configuration {
29+
HardcodedCredentialSourceCallConfiguration2() {
30+
this = "HardcodedCredentialSourceCallConfiguration2"
31+
}
32+
33+
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof CredentialsSourceSink }
34+
35+
override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsSink }
36+
}
37+
38+
/**
39+
* An argument to a call, where the parameter name corresponding
40+
* to the argument indicates that it may contain credentials, and
41+
* where this expression does not flow on to another `CredentialsSink`.
42+
*/
43+
class FinalCredentialsSourceSink extends CredentialsSourceSink {
44+
FinalCredentialsSourceSink() {
45+
not exists(HardcodedCredentialSourceCallConfiguration2 conf, CredentialsSink other |
46+
this != other
47+
|
48+
conf.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(other))
49+
)
50+
}
51+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Provides a predicate identifying assignments of harcoded values to password fields.
3+
*/
4+
5+
import HardcodedCredentials
6+
7+
/**
8+
* Holds if non-empty constant value `e` is assigned to password field `f`.
9+
*/
10+
predicate passwordFieldAssignedHardcodedValue(PasswordVariable f, CompileTimeConstantExpr e) {
11+
f instanceof Field and
12+
f.getAnAssignedValue() = e and
13+
not e.(StringLiteral).getValue() = ""
14+
}

java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,55 +10,9 @@
1010
* external/cwe/cwe-798
1111
*/
1212

13-
import java
14-
import semmle.code.java.dataflow.DataFlow
15-
import HardcodedCredentials
13+
import semmle.code.java.security.HardcodedCredentialsApiCallQuery
1614
import DataFlow::PathGraph
1715

18-
class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration {
19-
HardcodedCredentialApiCallConfiguration() { this = "HardcodedCredentialApiCallConfiguration" }
20-
21-
override predicate isSource(DataFlow::Node n) {
22-
n.asExpr() instanceof HardcodedExpr and
23-
not n.asExpr().getEnclosingCallable() instanceof ToStringMethod
24-
}
25-
26-
override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsApiSink }
27-
28-
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
29-
node1.asExpr().getType() instanceof TypeString and
30-
(
31-
exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) |
32-
node2.asExpr() = ma and
33-
ma.getQualifier() = node1.asExpr()
34-
)
35-
or
36-
// These base64 routines are usually taint propagators, and this is not a general
37-
// TaintTracking::Configuration, so we must specifically include them here
38-
// as a common transform applied to a constant before passing to a remote API.
39-
exists(MethodAccess ma |
40-
ma.getMethod()
41-
.hasQualifiedName([
42-
"java.util", "cn.hutool.core.codec", "org.apache.shiro.codec",
43-
"apache.commons.codec.binary", "org.springframework.util"
44-
], ["Base64$Encoder", "Base64$Decoder", "Base64", "Base64Utils"],
45-
[
46-
"encode", "encodeToString", "decode", "decodeBase64", "encodeBase64",
47-
"encodeBase64Chunked", "encodeBase64String", "encodeBase64URLSafe",
48-
"encodeBase64URLSafeString"
49-
])
50-
|
51-
node1.asExpr() = ma.getArgument(0) and
52-
node2.asExpr() = ma
53-
)
54-
)
55-
}
56-
57-
override predicate isBarrier(DataFlow::Node n) {
58-
n.asExpr().(MethodAccess).getMethod() instanceof MethodSystemGetenv
59-
}
60-
}
61-
6216
from
6317
DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedCredentialApiCallConfiguration conf
6418
where conf.hasFlowPath(source, sink)

java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@
1111
*/
1212

1313
import java
14-
import HardcodedCredentials
15-
16-
class EqualsAccess extends MethodAccess {
17-
EqualsAccess() { getMethod() instanceof EqualsMethod }
18-
}
14+
import semmle.code.java.security.HardcodedCredentialsComparison
1915

2016
from EqualsAccess sink, HardcodedExpr source, PasswordVariable p
21-
where
22-
source = sink.getQualifier() and
23-
p.getAnAccess() = sink.getArgument(0)
24-
or
25-
source = sink.getArgument(0) and
26-
p.getAnAccess() = sink.getQualifier()
17+
where isHardcodedCredentialsComparison(sink, source, p)
2718
select source, "Hard-coded value is $@ with password variable $@.", sink, "compared", p, p.getName()

java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,9 @@
1111
*/
1212

1313
import java
14-
import semmle.code.java.dataflow.DataFlow
15-
import semmle.code.java.dataflow.DataFlow2
16-
import HardcodedCredentials
14+
import semmle.code.java.security.HardcodedCredentialsSourceCall
1715
import DataFlow::PathGraph
1816

19-
class HardcodedCredentialSourceCallConfiguration extends DataFlow::Configuration {
20-
HardcodedCredentialSourceCallConfiguration() {
21-
this = "HardcodedCredentialSourceCallConfiguration"
22-
}
23-
24-
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof HardcodedExpr }
25-
26-
override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof FinalCredentialsSourceSink }
27-
}
28-
29-
class HardcodedCredentialSourceCallConfiguration2 extends DataFlow2::Configuration {
30-
HardcodedCredentialSourceCallConfiguration2() {
31-
this = "HardcodedCredentialSourceCallConfiguration2"
32-
}
33-
34-
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof CredentialsSourceSink }
35-
36-
override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsSink }
37-
}
38-
39-
class FinalCredentialsSourceSink extends CredentialsSourceSink {
40-
FinalCredentialsSourceSink() {
41-
not exists(HardcodedCredentialSourceCallConfiguration2 conf, CredentialsSink other |
42-
this != other
43-
|
44-
conf.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(other))
45-
)
46-
}
47-
}
48-
4917
from
5018
DataFlow::PathNode source, DataFlow::PathNode sink,
5119
HardcodedCredentialSourceCallConfiguration conf

java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,8 @@
1111
*/
1212

1313
import java
14-
import HardcodedCredentials
14+
import semmle.code.java.security.HardcodedPasswordField
1515

1616
from PasswordVariable f, CompileTimeConstantExpr e
17-
where
18-
f instanceof Field and
19-
f.getAnAssignedValue() = e and
20-
not e.(StringLiteral).getValue() = ""
17+
where passwordFieldAssignedHardcodedValue(f, e)
2118
select f, "Sensitive field is assigned a hard-coded $@.", e, "value"

0 commit comments

Comments
 (0)