Skip to content

Commit 532a10b

Browse files
committed
Java SSRF query: Provide hook for custom taint-propagating steps; make all default sinks/sanitizers/steps private.
1 parent 5bdd9da commit 532a10b

File tree

2 files changed

+42
-34
lines changed

2 files changed

+42
-34
lines changed

java/ql/src/Security/CWE/CWE-918/RequestForgery.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class RequestForgeryConfiguration extends TaintTracking::Configuration {
2929
override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgerySink }
3030

3131
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
32-
requestForgeryStep(pred, succ)
32+
any(RequestForgeryAdditionalTaintStep r).propagatesTaint(pred, succ)
3333
}
3434

3535
override predicate isSanitizer(DataFlow::Node node) { node instanceof RequestForgerySanitizer }

java/ql/src/Security/CWE/CWE-918/RequestForgery.qll

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,47 @@ import semmle.code.java.dataflow.DataFlow
88
import semmle.code.java.dataflow.TaintTracking
99
private import semmle.code.java.StringFormat
1010

11-
/**
12-
* Holds if taint is propagated from `pred` to `succ`.
13-
*/
14-
predicate requestForgeryStep(DataFlow::Node pred, DataFlow::Node succ) {
15-
// propagate to a URI when its host is assigned to
16-
exists(UriCreation c | c.getHostArg() = pred.asExpr() | succ.asExpr() = c)
17-
or
18-
// propagate to a URL when its host is assigned to
19-
exists(UrlConstructorCall c | c.getHostArg() = pred.asExpr() | succ.asExpr() = c)
20-
or
21-
// propagate to a RequestEntity when its url is assigned to
22-
exists(MethodAccess m |
23-
m.getMethod().getDeclaringType() instanceof SpringRequestEntity and
24-
(
25-
m.getMethod().hasName(["get", "post", "head", "delete", "options", "patch", "put"]) and
26-
m.getArgument(0) = pred.asExpr() and
27-
m = succ.asExpr()
28-
or
29-
m.getMethod().hasName("method") and
30-
m.getArgument(1) = pred.asExpr() and
11+
abstract class RequestForgeryAdditionalTaintStep extends string {
12+
bindingset[this]
13+
RequestForgeryAdditionalTaintStep() { any() }
14+
15+
abstract predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ);
16+
}
17+
18+
private class DefaultRequestForgeryAdditionalTaintStep extends RequestForgeryAdditionalTaintStep {
19+
DefaultRequestForgeryAdditionalTaintStep() { this = "DefaultRequestForgeryAdditionalTaintStep" }
20+
21+
override predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ) {
22+
// propagate to a URI when its host is assigned to
23+
exists(UriCreation c | c.getHostArg() = pred.asExpr() | succ.asExpr() = c)
24+
or
25+
// propagate to a URL when its host is assigned to
26+
exists(UrlConstructorCall c | c.getHostArg() = pred.asExpr() | succ.asExpr() = c)
27+
or
28+
// propagate to a RequestEntity when its url is assigned to
29+
exists(MethodAccess m |
30+
m.getMethod().getDeclaringType() instanceof SpringRequestEntity and
31+
(
32+
m.getMethod().hasName(["get", "post", "head", "delete", "options", "patch", "put"]) and
33+
m.getArgument(0) = pred.asExpr() and
34+
m = succ.asExpr()
35+
or
36+
m.getMethod().hasName("method") and
37+
m.getArgument(1) = pred.asExpr() and
38+
m = succ.asExpr()
39+
)
40+
)
41+
or
42+
// propagate from a `RequestEntity<>$BodyBuilder` to a `RequestEntity`
43+
// when the builder is tainted
44+
exists(MethodAccess m, RefType t |
45+
m.getMethod().getDeclaringType() = t and
46+
t.hasQualifiedName("org.springframework.http", "RequestEntity<>$BodyBuilder") and
47+
m.getMethod().hasName("body") and
48+
m.getQualifier() = pred.asExpr() and
3149
m = succ.asExpr()
3250
)
33-
)
34-
or
35-
// propagate from a `RequestEntity<>$BodyBuilder` to a `RequestEntity`
36-
// when the builder is tainted
37-
exists(MethodAccess m, RefType t |
38-
m.getMethod().getDeclaringType() = t and
39-
t.hasQualifiedName("org.springframework.http", "RequestEntity<>$BodyBuilder") and
40-
m.getMethod().hasName("body") and
41-
m.getQualifier() = pred.asExpr() and
42-
m = succ.asExpr()
43-
)
51+
}
4452
}
4553

4654
/** A data flow sink for request forgery vulnerabilities. */
@@ -257,7 +265,7 @@ private MethodAccess getAChainedAppend(Expr e) {
257265
* a hostname or a URL separator, preventing the appended string from arbitrarily controlling
258266
* the addressed server.
259267
*/
260-
class HostnameSanitizedExpr extends Expr {
268+
private class HostnameSanitizedExpr extends Expr {
261269
HostnameSanitizedExpr() {
262270
// Sanitize expressions that come after a sanitizing prefix in a tree of string additions:
263271
this = getASanitizedAddOperand()
@@ -311,6 +319,6 @@ class HostnameSanitizedExpr extends Expr {
311319
* A value that is the result of prepending a string that prevents any value from controlling the
312320
* host of a URL.
313321
*/
314-
class HostnameSantizer extends RequestForgerySanitizer {
322+
private class HostnameSantizer extends RequestForgerySanitizer {
315323
HostnameSantizer() { this.asExpr() instanceof HostnameSanitizedExpr }
316324
}

0 commit comments

Comments
 (0)