Skip to content

Commit 4cc8866

Browse files
authored
Merge pull request #5557 from tamasvajk/feature/java-sinks-csv
Java: convert sinks to CSV
2 parents 0509a12 + 5b79094 commit 4cc8866

File tree

7 files changed

+103
-121
lines changed

7 files changed

+103
-121
lines changed

java/ql/src/Security/CWE/CWE-022/ZipSlip.ql

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import semmle.code.java.dataflow.SSA
1717
import semmle.code.java.dataflow.TaintTracking
1818
import DataFlow
1919
import PathGraph
20+
private import semmle.code.java.dataflow.ExternalFlow
2021

2122
/**
2223
* A method that returns the name of an archive entry.
@@ -33,34 +34,6 @@ class ArchiveEntryNameMethod extends Method {
3334
}
3435
}
3536

36-
/**
37-
* An expression that will be treated as the destination of a write.
38-
*/
39-
class WrittenFileName extends Expr {
40-
WrittenFileName() {
41-
// Constructors that write to their first argument.
42-
exists(ConstructorCall ctr | this = ctr.getArgument(0) |
43-
exists(Class c | ctr.getConstructor() = c.getAConstructor() |
44-
c.hasQualifiedName("java.io", "FileOutputStream") or
45-
c.hasQualifiedName("java.io", "RandomAccessFile") or
46-
c.hasQualifiedName("java.io", "FileWriter")
47-
)
48-
)
49-
or
50-
// Methods that write to their n'th argument
51-
exists(MethodAccess call, int n | this = call.getArgument(n) |
52-
call.getMethod().getDeclaringType().hasQualifiedName("java.nio.file", "Files") and
53-
(
54-
call.getMethod().getName().regexpMatch("new.*Reader|newOutputStream|create.*") and n = 0
55-
or
56-
call.getMethod().hasName("copy") and n = 1
57-
or
58-
call.getMethod().hasName("move") and n = 1
59-
)
60-
)
61-
}
62-
}
63-
6437
/**
6538
* Holds if `n1` to `n2` is a dataflow step that converts between `String`,
6639
* `File`, and `Path`.
@@ -151,7 +124,7 @@ class ZipSlipConfiguration extends TaintTracking::Configuration {
151124
source.asExpr().(MethodAccess).getMethod() instanceof ArchiveEntryNameMethod
152125
}
153126

154-
override predicate isSink(Node sink) { sink.asExpr() instanceof WrittenFileName }
127+
override predicate isSink(Node sink) { sink instanceof FileCreationSink }
155128

156129
override predicate isAdditionalTaintStep(Node n1, Node n2) {
157130
filePathStep(n1, n2) or fileTaintStep(n1, n2)
@@ -173,6 +146,13 @@ class ZipSlipConfiguration extends TaintTracking::Configuration {
173146
}
174147
}
175148

149+
/**
150+
* A sink that represents a file creation, such as a file write, copy or move operation.
151+
*/
152+
private class FileCreationSink extends DataFlow::Node {
153+
FileCreationSink() { sinkNode(this, "create-file") }
154+
}
155+
176156
from PathNode source, PathNode sink
177157
where any(ZipSlipConfiguration c).hasFlowPath(source, sink)
178158
select source.getNode(), source, sink,

java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java
1313
import semmle.code.java.dataflow.TaintTracking
1414
import semmle.code.java.dataflow.FlowSources
1515
import DataFlow::PathGraph
16+
private import semmle.code.java.dataflow.ExternalFlow
1617

1718
/**
1819
* A message interpolator Type that perform Expression Language (EL) evaluations
@@ -50,19 +51,6 @@ class SetMessageInterpolatorCall extends MethodAccess {
5051
predicate isSafe() { not this.getAnArgument().getType() instanceof ELMessageInterpolatorType }
5152
}
5253

53-
/**
54-
* A method named `buildConstraintViolationWithTemplate` declared on a subtype
55-
* of `javax.validation.ConstraintValidatorContext`.
56-
*/
57-
class BuildConstraintViolationWithTemplateMethod extends Method {
58-
BuildConstraintViolationWithTemplateMethod() {
59-
this.getDeclaringType()
60-
.getASupertype*()
61-
.hasQualifiedName("javax.validation", "ConstraintValidatorContext") and
62-
this.hasName("buildConstraintViolationWithTemplate")
63-
}
64-
}
65-
6654
/**
6755
* Taint tracking BeanValidationConfiguration describing the flow of data from user input
6856
* to the argument of a method that builds constraint error messages.
@@ -72,12 +60,15 @@ class BeanValidationConfig extends TaintTracking::Configuration {
7260

7361
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
7462

75-
override predicate isSink(DataFlow::Node sink) {
76-
exists(MethodAccess ma |
77-
ma.getMethod() instanceof BuildConstraintViolationWithTemplateMethod and
78-
sink.asExpr() = ma.getArgument(0)
79-
)
80-
}
63+
override predicate isSink(DataFlow::Node sink) { sink instanceof BeanValidationSink }
64+
}
65+
66+
/**
67+
* A bean validation sink, such as method `buildConstraintViolationWithTemplate`
68+
* declared on a subtype of `javax.validation.ConstraintValidatorContext`.
69+
*/
70+
private class BeanValidationSink extends DataFlow::Node {
71+
BeanValidationSink() { sinkNode(this, "bean-validation") }
8172
}
8273

8374
from BeanValidationConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink

java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import semmle.code.java.dataflow.DataFlow
1515
import semmle.code.java.dataflow.FlowSources
1616
import semmle.code.java.security.Encryption
1717
import DataFlow::PathGraph
18+
private import semmle.code.java.dataflow.ExternalFlow
1819

1920
/**
2021
* Holds if `m` always returns `true` ignoring any exceptional flow.
@@ -49,14 +50,7 @@ class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration {
4950
source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof TrustAllHostnameVerifier
5051
}
5152

52-
override predicate isSink(DataFlow::Node sink) {
53-
exists(MethodAccess ma, Method m |
54-
(m instanceof SetDefaultHostnameVerifierMethod or m instanceof SetHostnameVerifierMethod) and
55-
ma.getMethod() = m
56-
|
57-
ma.getArgument(0) = sink.asExpr()
58-
)
59-
}
53+
override predicate isSink(DataFlow::Node sink) { sink instanceof HostnameVerifierSink }
6054

6155
override predicate isBarrier(DataFlow::Node barrier) {
6256
// ignore nodes that are in functions that intentionally disable hostname verification
@@ -84,6 +78,13 @@ class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration {
8478
}
8579
}
8680

81+
/**
82+
* A sink that sets the `HostnameVerifier` on `HttpsURLConnection`.
83+
*/
84+
private class HostnameVerifierSink extends DataFlow::Node {
85+
HostnameVerifierSink() { sinkNode(this, "set-hostname-verifier") }
86+
}
87+
8788
bindingset[result]
8889
private string getAFlagName() {
8990
result

java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import java
1313
import semmle.code.java.dataflow.TaintTracking
1414
import semmle.code.java.frameworks.Networking
1515
import DataFlow::PathGraph
16+
private import semmle.code.java.dataflow.ExternalFlow
1617

17-
class HTTPString extends StringLiteral {
18-
HTTPString() {
18+
class HttpString extends StringLiteral {
19+
HttpString() {
1920
// Avoid matching "https" here.
2021
exists(string s | this.getRepresentedString() = s |
2122
(
@@ -30,26 +31,12 @@ class HTTPString extends StringLiteral {
3031
}
3132
}
3233

33-
class URLOpenMethod extends Method {
34-
URLOpenMethod() {
35-
this.getDeclaringType().getQualifiedName() = "java.net.URL" and
36-
(
37-
this.getName() = "openConnection" or
38-
this.getName() = "openStream"
39-
)
40-
}
41-
}
34+
class HttpStringToUrlOpenMethodFlowConfig extends TaintTracking::Configuration {
35+
HttpStringToUrlOpenMethodFlowConfig() { this = "HttpsUrls::HttpStringToUrlOpenMethodFlowConfig" }
4236

43-
class HTTPStringToURLOpenMethodFlowConfig extends TaintTracking::Configuration {
44-
HTTPStringToURLOpenMethodFlowConfig() { this = "HttpsUrls::HTTPStringToURLOpenMethodFlowConfig" }
37+
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HttpString }
4538

46-
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HTTPString }
47-
48-
override predicate isSink(DataFlow::Node sink) {
49-
exists(MethodAccess m |
50-
sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenMethod
51-
)
52-
}
39+
override predicate isSink(DataFlow::Node sink) { sink instanceof UrlOpenSink }
5340

5441
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
5542
exists(UrlConstructorCall u |
@@ -63,10 +50,17 @@ class HTTPStringToURLOpenMethodFlowConfig extends TaintTracking::Configuration {
6350
}
6451
}
6552

66-
from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess m, HTTPString s
53+
/**
54+
* A sink that represents a URL opening method call, such as a call to `java.net.URL.openConnection()`.
55+
*/
56+
private class UrlOpenSink extends DataFlow::Node {
57+
UrlOpenSink() { sinkNode(this, "open-url") }
58+
}
59+
60+
from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess m, HttpString s
6761
where
6862
source.getNode().asExpr() = s and
6963
sink.getNode().asExpr() = m.getQualifier() and
70-
any(HTTPStringToURLOpenMethodFlowConfig c).hasFlowPath(source, sink)
64+
any(HttpStringToUrlOpenMethodFlowConfig c).hasFlowPath(source, sink)
7165
select m, source, sink, "URL may have been constructed with HTTP protocol, using $@.", s,
7266
"this source"

java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ private module Frameworks {
7777
private import semmle.code.java.frameworks.ApacheHttp
7878
private import semmle.code.java.frameworks.apache.Lang
7979
private import semmle.code.java.frameworks.guava.Guava
80+
private import semmle.code.java.security.ResponseSplitting
81+
private import semmle.code.java.security.XSS
8082
private import semmle.code.java.security.LdapInjection
8183
}
8284

@@ -186,7 +188,33 @@ private predicate sourceModelCsv(string row) {
186188
]
187189
}
188190

189-
private predicate sinkModelCsv(string row) { none() }
191+
private predicate sinkModelCsv(string row) {
192+
row =
193+
[
194+
// Open URL
195+
"java.net;URL;false;openConnection;;;Argument[-1];open-url",
196+
"java.net;URL;false;openStream;;;Argument[-1];open-url",
197+
// Create file
198+
"java.io;FileOutputStream;false;FileOutputStream;;;Argument[0];create-file",
199+
"java.io;RandomAccessFile;false;RandomAccessFile;;;Argument[0];create-file",
200+
"java.io;FileWriter;false;FileWriter;;;Argument[0];create-file",
201+
"java.nio.file;Files;false;move;;;Argument[1];create-file",
202+
"java.nio.file;Files;false;copy;;;Argument[1];create-file",
203+
"java.nio.file;Files;false;newOutputStream;;;Argument[0];create-file",
204+
"java.nio.file;Files;false;newBufferedReader;;;Argument[0];create-file",
205+
"java.nio.file;Files;false;createDirectory;;;Argument[0];create-file",
206+
"java.nio.file;Files;false;createFile;;;Argument[0];create-file",
207+
"java.nio.file;Files;false;createLink;;;Argument[0];create-file",
208+
"java.nio.file;Files;false;createSymbolicLink;;;Argument[0];create-file",
209+
"java.nio.file;Files;false;createTempDirectory;;;Argument[0];create-file",
210+
"java.nio.file;Files;false;createTempFile;;;Argument[0];create-file",
211+
// Bean validation
212+
"javax.validation;ConstraintValidatorContext;true;buildConstraintViolationWithTemplate;;;Argument[0];bean-validation",
213+
// Set hostname
214+
"javax.net.ssl;HttpsURLConnection;true;setDefaultHostnameVerifier;;;Argument[0];set-hostname-verifier",
215+
"javax.net.ssl;HttpsURLConnection;true;setHostnameVerifier;;;Argument[0];set-hostname-verifier"
216+
]
217+
}
190218

191219
private predicate summaryModelCsv(string row) {
192220
row =

java/ql/src/semmle/code/java/security/ResponseSplitting.qll

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,32 @@ import semmle.code.java.dataflow.DataFlow
55
import semmle.code.java.dataflow.FlowSources
66
import semmle.code.java.frameworks.Servlets
77
import semmle.code.java.frameworks.JaxWS
8+
private import semmle.code.java.dataflow.ExternalFlow
89

910
/** A sink that is vulnerable to an HTTP header splitting attack. */
1011
abstract class HeaderSplittingSink extends DataFlow::Node { }
1112

12-
/** A source that introduces data considered safe to use by a header splitting source. */
13-
abstract class SafeHeaderSplittingSource extends DataFlow::Node {
14-
SafeHeaderSplittingSource() { this instanceof RemoteFlowSource }
13+
private class DefaultHeaderSplittingSink extends HeaderSplittingSink {
14+
DefaultHeaderSplittingSink() { sinkNode(this, "header-splitting") }
1515
}
1616

17-
/** A sink that identifies a Java Servlet or JaxWs method that is vulnerable to an HTTP header splitting attack. */
18-
private class ServletHeaderSplittingSink extends HeaderSplittingSink {
19-
ServletHeaderSplittingSink() {
20-
exists(ResponseAddCookieMethod m, MethodAccess ma |
21-
ma.getMethod() = m and
22-
this.asExpr() = ma.getArgument(0)
23-
)
24-
or
25-
exists(ResponseAddHeaderMethod m, MethodAccess ma |
26-
ma.getMethod() = m and
27-
this.asExpr() = ma.getAnArgument()
28-
)
29-
or
30-
exists(ResponseSetHeaderMethod m, MethodAccess ma |
31-
ma.getMethod() = m and
32-
this.asExpr() = ma.getAnArgument()
33-
)
34-
or
35-
exists(JaxRsResponseBuilder builder, Method m |
36-
m = builder.getAMethod() and m.getName() = "header"
37-
|
38-
this.asExpr() = m.getAReference().getArgument(1)
39-
)
17+
private class HeaderSplittingSinkModel extends SinkModelCsv {
18+
override predicate row(string row) {
19+
row =
20+
[
21+
"javax.servlet.http;HttpServletResponse;false;addCookie;;;Argument[0];header-splitting",
22+
"javax.servlet.http;HttpServletResponse;false;addHeader;;;Argument[0..1];header-splitting",
23+
"javax.servlet.http;HttpServletResponse;false;setHeader;;;Argument[0..1];header-splitting",
24+
"javax.ws.rs.core;ResponseBuilder;false;header;;;Argument[1];header-splitting"
25+
]
4026
}
4127
}
4228

29+
/** A source that introduces data considered safe to use by a header splitting source. */
30+
abstract class SafeHeaderSplittingSource extends DataFlow::Node {
31+
SafeHeaderSplittingSource() { this instanceof RemoteFlowSource }
32+
}
33+
4334
/** A default source that introduces data considered safe to use by a header splitting source. */
4435
private class DefaultSafeHeaderSplittingSource extends SafeHeaderSplittingSource {
4536
DefaultSafeHeaderSplittingSource() {

java/ql/src/semmle/code/java/security/XSS.qll

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,30 @@ class XssAdditionalTaintStep extends Unit {
2929
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
3030
}
3131

32+
/** CSV sink models representing methods susceptible to XSS attacks. */
33+
private class DefaultXssSinkModel extends SinkModelCsv {
34+
override predicate row(string row) {
35+
row =
36+
[
37+
"javax.servlet.http;HttpServletResponse;false;sendError;(int,String);;Argument[1];xss",
38+
"android.webkit;WebView;false;loadData;;;Argument[0];xss",
39+
"android.webkit;WebView;false;loadUrl;;;Argument[0];xss",
40+
"android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss"
41+
]
42+
}
43+
}
44+
3245
/** A default sink representing methods susceptible to XSS attacks. */
3346
private class DefaultXssSink extends XssSink {
3447
DefaultXssSink() {
3548
sinkNode(this, "xss")
3649
or
37-
exists(HttpServletResponseSendErrorMethod m, MethodAccess ma |
38-
ma.getMethod() = m and
39-
this.asExpr() = ma.getArgument(1)
40-
)
41-
or
4250
exists(ServletWriterSourceToWritingMethodFlowConfig writer, MethodAccess ma |
4351
ma.getMethod() instanceof WritingMethod and
4452
writer.hasFlowToExpr(ma.getQualifier()) and
4553
this.asExpr() = ma.getArgument(_)
4654
)
4755
or
48-
exists(Method m |
49-
m.getDeclaringType() instanceof TypeWebView and
50-
(
51-
m.getAReference().getArgument(0) = this.asExpr() and m.getName() = "loadData"
52-
or
53-
m.getAReference().getArgument(0) = this.asExpr() and m.getName() = "loadUrl"
54-
or
55-
m.getAReference().getArgument(1) = this.asExpr() and m.getName() = "loadDataWithBaseURL"
56-
)
57-
)
58-
or
5956
exists(SpringRequestMappingMethod requestMappingMethod, ReturnStmt rs |
6057
requestMappingMethod = rs.getEnclosingCallable() and
6158
this.asExpr() = rs.getResult() and

0 commit comments

Comments
 (0)