Skip to content

Commit 0a7350f

Browse files
authored
Merge pull request #10041 from smowton/AddSensitiveApiCalls
Java: support more libraries in hardcoded-credentials queries
2 parents 51ada5c + 131d604 commit 0a7350f

File tree

262 files changed

+5703
-345
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

262 files changed

+5703
-345
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The query `java/hardcoded-credential-api-call` now recognises methods that consume usernames, passwords and keys from the JSch, Ganymed, Apache SSHD, sshj, Trilead SSH-2, Apache FTPClient and MongoDB projects.

java/ql/src/Security/CWE/CWE-798/HardcodedCredentials.qll renamed to java/ql/lib/semmle/code/java/security/HardcodedCredentials.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* Provides classes and predicates relating to hardcoded credentials.
3+
*/
4+
15
import java
26
import SensitiveApi
37

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+
* A data-flow configuration that 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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Provides classes and predicates to detect comparing a parameter to a hard-coded credential.
3+
*/
4+
5+
import java
6+
import HardcodedCredentials
7+
8+
/**
9+
* A call to a method that is or overrides `java.lang.Object.equals`.
10+
*/
11+
class EqualsAccess extends MethodAccess {
12+
EqualsAccess() { getMethod() instanceof EqualsMethod }
13+
}
14+
15+
/**
16+
* Holds if `sink` compares password `p` against a hardcoded expression `source`.
17+
*/
18+
predicate isHardcodedCredentialsComparison(
19+
EqualsAccess sink, HardcodedExpr source, PasswordVariable p
20+
) {
21+
source = sink.getQualifier() and
22+
p.getAnAccess() = sink.getArgument(0)
23+
or
24+
source = sink.getArgument(0) and
25+
p.getAnAccess() = sink.getQualifier()
26+
}
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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Provides a predicate identifying assignments of harcoded values to password fields.
3+
*/
4+
5+
import java
6+
import HardcodedCredentials
7+
8+
/**
9+
* Holds if non-empty constant value `e` is assigned to password field `f`.
10+
*/
11+
predicate passwordFieldAssignedHardcodedValue(PasswordVariable f, CompileTimeConstantExpr e) {
12+
f instanceof Field and
13+
f.getAnAssignedValue() = e and
14+
not e.(StringLiteral).getValue() = ""
15+
}

java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll renamed to java/ql/lib/semmle/code/java/security/SensitiveApi.qll

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* Provides predicates defining methods that consume sensitive data, such as usernames and passwords.
3+
*/
4+
15
import java
26

37
/**
@@ -438,6 +442,49 @@ private predicate otherApiCallableCredentialParam(string s) {
438442
"com.azure.identity.UsernamePasswordCredentialBuilder;username(String);0",
439443
"com.azure.identity.UsernamePasswordCredentialBuilder;password(String);0",
440444
"com.azure.identity.ClientSecretCredentialBuilder;clientSecret(String);0",
441-
"org.apache.shiro.mgt.AbstractRememberMeManager;setCipherKey(byte[]);0"
445+
"org.apache.shiro.mgt.AbstractRememberMeManager;setCipherKey(byte[]);0",
446+
"com.jcraft.jsch.JSch;getSession(String, String, int);0",
447+
"com.jcraft.jsch.JSch;getSession(String, String);0",
448+
"ch.ethz.ssh2.Connection;authenticateWithPassword(String, String);0",
449+
"org.apache.sshd.client.session.ClientSessionCreator;connect(String, String, int);0",
450+
"org.apache.sshd.client.session.ClientSessionCreator;connect(String, SocketAddress);0",
451+
"net.schmizz.sshj.SSHClient;authPassword(String, char[]);0",
452+
"net.schmizz.sshj.SSHClient;authPassword(String, String);0",
453+
"com.sshtools.j2ssh.authentication.SshAuthenticationClient;setUsername(String);0",
454+
"com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;setUsername(String);0",
455+
"com.trilead.ssh2.Connection;authenticateWithPassword(String, String);0",
456+
"com.trilead.ssh2.Connection;authenticateWithDSA(String, String, String);0",
457+
"com.trilead.ssh2.Connection;authenticateWithNone(String);0",
458+
"com.trilead.ssh2.Connection;getRemainingAuthMethods(String);0",
459+
"com.trilead.ssh2.Connection;isAuthMethodAvailable(String, String);0",
460+
"com.trilead.ssh2.Connection;authenticateWithPublicKey(String, char[], String);0",
461+
"com.trilead.ssh2.Connection;authenticateWithPublicKey(String, File, String);0",
462+
"com.jcraft.jsch.Session;setPassword(byte[]);0",
463+
"com.jcraft.jsch.Session;setPassword(String);0",
464+
"ch.ethz.ssh2.Connection;authenticateWithPassword(String, String);1",
465+
"org.apache.sshd.client.session.AbstractClientSession;addPasswordIdentity(String);0",
466+
"net.schmizz.sshj.SSHClient;authPassword(String, char[]);1",
467+
"net.schmizz.sshj.SSHClient;authPassword(String, String);1",
468+
"com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;setPassword(String);0",
469+
"com.trilead.ssh2.Connection;authenticateWithPassword(String, String);1",
470+
"com.trilead.ssh2.Connection;authenticateWithDSA(String, String, String);2",
471+
"com.trilead.ssh2.Connection;authenticateWithPublicKey(String, char[], String);2",
472+
"com.trilead.ssh2.Connection;authenticateWithPublicKey(String, File, String);2",
473+
"com.trilead.ssh2.Connection;authenticateWithDSA(String, String, String);1",
474+
"com.trilead.ssh2.Connection;authenticateWithPublicKey(String, char[], String);1",
475+
"org.apache.commons.net.ftp.FTPClient;login(String, String);0",
476+
"org.apache.commons.net.ftp.FTPClient;login(String, String, String);0",
477+
"org.apache.commons.net.ftp.FTPClient;login(String, String);1",
478+
"org.apache.commons.net.ftp.FTPClient;login(String, String, String);1",
479+
"com.mongodb.MongoCredential;createCredential(String, String, char[]);0",
480+
"com.mongodb.MongoCredential;createMongoCRCredential(String, String, char[]);0",
481+
"com.mongodb.MongoCredential;createPlainCredential(String, String, char[]);0",
482+
"com.mongodb.MongoCredential;createScramSha1Credential(String, String, char[]);0",
483+
"com.mongodb.MongoCredential;createGSSAPICredential(String);0",
484+
"com.mongodb.MongoCredential;createMongoX509Credential(String);0",
485+
"com.mongodb.MongoCredential;createCredential(String, String, char[]);2",
486+
"com.mongodb.MongoCredential;createMongoCRCredential(String, String, char[]);2",
487+
"com.mongodb.MongoCredential;createPlainCredential(String, String, char[]);2",
488+
"com.mongodb.MongoCredential;createScramSha1Credential(String, String, char[]);2"
442489
]
443490
}

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.HardcodedCredentialsSourceCallQuery
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

0 commit comments

Comments
 (0)