Skip to content

Commit 679652e

Browse files
haby0smowton
authored andcommitted
Modify Sanitizer
1 parent 952b34a commit 679652e

File tree

4 files changed

+156
-34
lines changed

4 files changed

+156
-34
lines changed

java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ import UnsafeUrlForward
1515
import semmle.code.java.dataflow.FlowSources
1616
import DataFlow::PathGraph
1717

18+
private class StartsWithSanitizer extends DataFlow::BarrierGuard {
19+
StartsWithSanitizer() {
20+
this.(MethodAccess).getMethod().hasName("startsWith") and
21+
this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and
22+
this.(MethodAccess).getMethod().getNumberOfParameters() = 1
23+
}
24+
25+
override predicate checks(Expr e, boolean branch) {
26+
e = this.(MethodAccess).getQualifier() and branch = true
27+
}
28+
}
29+
1830
class UnsafeUrlForwardFlowConfig extends TaintTracking::Configuration {
1931
UnsafeUrlForwardFlowConfig() { this = "UnsafeUrlForwardFlowConfig" }
2032

@@ -25,39 +37,18 @@ class UnsafeUrlForwardFlowConfig extends TaintTracking::Configuration {
2537
ma.getMethod()
2638
.getDeclaringType()
2739
.getASupertype*()
28-
.hasQualifiedName("javax.servlet.http", "HttpServletRequest")
40+
.hasQualifiedName("javax.servlet.http", "HttpServletRequest") and
41+
ma = source.asExpr()
2942
)
3043
}
3144

3245
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeUrlForwardSink }
3346

34-
override predicate isSanitizer(DataFlow::Node node) {
35-
node.getType() instanceof BoxedType
36-
or
37-
node.getType() instanceof PrimitiveType
38-
or
39-
exists(AddExpr ae |
40-
ae.getRightOperand() = node.asExpr() and
41-
(
42-
not ae.getLeftOperand().(CompileTimeConstantExpr).getStringValue().matches("/WEB-INF/%") and
43-
not ae.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "forward:"
44-
)
45-
)
46-
or
47-
exists(MethodAccess ma, int i |
48-
ma.getMethod().hasName("format") and
49-
ma.getMethod().getDeclaringType() instanceof TypeString and
50-
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "redirect:" and
51-
ma.getArgument(i) = node.asExpr() and
52-
i != 0
53-
)
54-
or
55-
exists(StringBuilderAppendCall ma1, StringBuilderAppendCall ma2 |
56-
DataFlow2::localExprFlow(ma1.getQualifier(), ma2.getQualifier()) and
57-
ma1.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "redirect:" and
58-
ma2.getArgument(0) = node.asExpr()
59-
)
47+
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
48+
guard instanceof StartsWithSanitizer
6049
}
50+
51+
override predicate isSanitizer(DataFlow::Node node) { node instanceof UnsafeUrlForwardSanitizer }
6152
}
6253

6354
from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeUrlForwardFlowConfig conf

java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qll

Lines changed: 136 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,141 @@ import DataFlow
33
import semmle.code.java.dataflow.FlowSources
44
import semmle.code.java.frameworks.Servlets
55

6+
/** A sanitizer for unsafe url forward vulnerabilities. */
7+
abstract class UnsafeUrlForwardSanitizer extends DataFlow::Node { }
8+
9+
private class PrimitiveSanitizer extends UnsafeUrlForwardSanitizer {
10+
PrimitiveSanitizer() {
11+
this.getType() instanceof PrimitiveType or
12+
this.getType() instanceof BoxedType or
13+
this.getType() instanceof NumberType
14+
}
15+
}
16+
17+
private class UnsafeUrlForwardSantizer extends UnsafeUrlForwardSanitizer {
18+
UnsafeUrlForwardSantizer() { this.asExpr() instanceof UnsafeUrlForwardSanitizedExpr }
19+
}
20+
21+
private class UnsafeUrlForwardSanitizingConstantPrefix extends CompileTimeConstantExpr {
22+
UnsafeUrlForwardSanitizingConstantPrefix() {
23+
not this.getStringValue().matches("/WEB-INF/%") and
24+
not this.getStringValue() = "forward:"
25+
}
26+
}
27+
28+
private Expr getAUnsafeUrlForwardSanitizingPrefix() {
29+
result instanceof UnsafeUrlForwardSanitizingConstantPrefix
30+
or
31+
result.(AddExpr).getAnOperand() = getAUnsafeUrlForwardSanitizingPrefix()
32+
}
33+
634
/** A call to `StringBuilder.append` method. */
7-
class StringBuilderAppendCall extends MethodAccess {
8-
StringBuilderAppendCall() {
9-
this.getMethod().hasName("append") and
10-
this.getMethod().getDeclaringType() instanceof StringBuildingType
35+
class StringBuilderAppend extends MethodAccess {
36+
StringBuilderAppend() {
37+
this.getMethod().getDeclaringType() instanceof StringBuildingType and
38+
this.getMethod().hasName("append")
39+
}
40+
}
41+
42+
private Expr getQualifier(Expr e) { result = e.(MethodAccess).getQualifier() }
43+
44+
/**
45+
* An extension of `StringBuilderVar` that also accounts for strings appended in StringBuilder/Buffer's constructor
46+
* and in `append` calls chained onto the constructor call.
47+
*
48+
* The original `StringBuilderVar` doesn't care about these because it is designed to model taint, and
49+
* in taint rules terms these are not needed, as the connection between construction, appends and the
50+
* eventual `toString` is more obvious.
51+
*/
52+
private class StringBuilderVarExt extends StringBuilderVar {
53+
/**
54+
* Returns a first assignment after this StringBuilderVar is first assigned.
55+
*
56+
* For example, for `StringBuilder sbv = new StringBuilder("1").append("2"); sbv.append("3").append("4");`
57+
* this returns the append of `"3"`.
58+
*/
59+
private StringBuilderAppend getAFirstAppendAfterAssignment() {
60+
result = this.getAnAppend() and not result = this.getNextAppend(_)
61+
}
62+
63+
/**
64+
* Gets the next `append` after `prev`, where `prev` is, perhaps after some more `append` or other
65+
* chained calls, assigned to this `StringBuilderVar`.
66+
*/
67+
private StringBuilderAppend getNextAssignmentChainedAppend(StringBuilderConstructorOrAppend prev) {
68+
getQualifier*(result) = this.getAnAssignedValue() and
69+
result.getQualifier() = prev
70+
}
71+
72+
/**
73+
* Get a constructor call or `append` call that contributes a string to this string builder.
74+
*/
75+
StringBuilderConstructorOrAppend getAConstructorOrAppend() {
76+
exists(this.getNextAssignmentChainedAppend(result)) or
77+
result = this.getAnAssignedValue() or
78+
result = this.getAnAppend()
79+
}
80+
81+
/**
82+
* Like `StringBuilderVar.getNextAppend`, except including appends and constructors directly
83+
* assigned to this `StringBuilderVar`.
84+
*/
85+
private StringBuilderAppend getNextAppendIncludingAssignmentChains(
86+
StringBuilderConstructorOrAppend prev
87+
) {
88+
result = getNextAssignmentChainedAppend(prev)
89+
or
90+
prev = this.getAnAssignedValue() and
91+
result = this.getAFirstAppendAfterAssignment()
92+
or
93+
result = this.getNextAppend(prev)
94+
}
95+
96+
/**
97+
* Implements `StringBuilderVarExt.getNextAppendIncludingAssignmentChains+(prev)`.
98+
*/
99+
pragma[nomagic]
100+
StringBuilderAppend getSubsequentAppendIncludingAssignmentChains(
101+
StringBuilderConstructorOrAppend prev
102+
) {
103+
result = this.getNextAppendIncludingAssignmentChains(prev) or
104+
result =
105+
this.getSubsequentAppendIncludingAssignmentChains(this.getNextAppendIncludingAssignmentChains(prev))
106+
}
107+
}
108+
109+
private class StringBuilderConstructorOrAppend extends Call {
110+
StringBuilderConstructorOrAppend() {
111+
this instanceof StringBuilderAppend or
112+
this.(ClassInstanceExpr).getConstructedType() instanceof StringBuildingType
113+
}
114+
}
115+
116+
private class UnsafeUrlForwardSanitizedExpr extends Expr {
117+
UnsafeUrlForwardSanitizedExpr() {
118+
// Sanitize expressions that come after a sanitizing prefix in a tree of string additions:
119+
this =
120+
any(AddExpr add | add.getLeftOperand() = getAUnsafeUrlForwardSanitizingPrefix())
121+
.getRightOperand()
122+
or
123+
// Sanitize expressions that come after a sanitizing prefix in a sequence of StringBuilder operations:
124+
exists(
125+
StringBuilderConstructorOrAppend appendSanitizingConstant,
126+
StringBuilderAppend subsequentAppend, StringBuilderVarExt v
127+
|
128+
appendSanitizingConstant = v.getAConstructorOrAppend() and
129+
appendSanitizingConstant.getArgument(0) = getAUnsafeUrlForwardSanitizingPrefix() and
130+
v.getSubsequentAppendIncludingAssignmentChains(appendSanitizingConstant) = subsequentAppend and
131+
this = subsequentAppend.getArgument(0)
132+
)
133+
or
134+
exists(MethodAccess ma, int i |
135+
ma.getMethod().hasName("format") and
136+
ma.getMethod().getDeclaringType() instanceof TypeString and
137+
ma.getArgument(0) instanceof UnsafeUrlForwardSanitizingConstantPrefix and
138+
ma.getArgument(i) = this and
139+
i != 0
140+
)
11141
}
12142
}
13143

@@ -16,7 +146,7 @@ class StringBuilderAppendCall extends MethodAccess {
16146
*
17147
* E.g: `"forward:" + url`
18148
*/
19-
class ForwardBuilderExpr extends AddExpr {
149+
private class ForwardBuilderExpr extends AddExpr {
20150
ForwardBuilderExpr() {
21151
this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "forward:"
22152
}
@@ -27,7 +157,7 @@ class ForwardBuilderExpr extends AddExpr {
27157
*
28158
* E.g: `StringBuilder.append("forward:")`
29159
*/
30-
class ForwardAppendCall extends StringBuilderAppendCall {
160+
private class ForwardAppendCall extends StringBuilderAppend {
31161
ForwardAppendCall() {
32162
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "forward:"
33163
}

java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ nodes
2020
| UnsafeUrlForward.java:38:33:38:35 | url | semmle.label | url |
2121
| UnsafeUrlForward.java:47:19:47:28 | url : String | semmle.label | url : String |
2222
| UnsafeUrlForward.java:49:33:49:62 | ... + ... | semmle.label | ... + ... |
23+
subpaths
2324
#select
2425
| UnsafeUrlForward.java:14:27:14:29 | url | UnsafeUrlForward.java:13:27:13:36 | url : String | UnsafeUrlForward.java:14:27:14:29 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:13:27:13:36 | url | user-provided value |
2526
| UnsafeUrlForward.java:20:28:20:30 | url | UnsafeUrlForward.java:18:27:18:36 | url : String | UnsafeUrlForward.java:20:28:20:30 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:18:27:18:36 | url | user-provided value |
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.3.8/

0 commit comments

Comments
 (0)