Skip to content

Commit 057d0fb

Browse files
committed
Rewrite query to use shared StringPrefixes library
1 parent 8a4fa0a commit 057d0fb

File tree

1 file changed

+28
-82
lines changed

1 file changed

+28
-82
lines changed

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

Lines changed: 28 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import semmle.code.java.dataflow.FlowSources
44
import semmle.code.java.frameworks.Servlets
55
import semmle.code.java.frameworks.spring.SpringWeb
66
import semmle.code.java.security.RequestForgery
7+
private import semmle.code.java.dataflow.StringPrefixes
78

89
/** A sanitizer for unsafe url forward vulnerabilities. */
910
abstract class UnsafeUrlForwardSanitizer extends DataFlow::Node { }
@@ -16,76 +17,22 @@ private class PrimitiveSanitizer extends UnsafeUrlForwardSanitizer {
1617
}
1718
}
1819

19-
private class UnsafeUrlForwardSantizer extends UnsafeUrlForwardSanitizer {
20-
UnsafeUrlForwardSantizer() { this.asExpr() instanceof UnsafeUrlForwardSanitizedExpr }
21-
}
22-
23-
private class UnsafeUrlForwardSanitizingConstantPrefix extends CompileTimeConstantExpr {
24-
UnsafeUrlForwardSanitizingConstantPrefix() {
20+
private class SanitizingPrefix extends InterestingPrefix {
21+
SanitizingPrefix() {
2522
not this.getStringValue().matches("/WEB-INF/%") and
2623
not this.getStringValue() = "forward:"
2724
}
28-
}
2925

30-
private Expr getAUnsafeUrlForwardSanitizingPrefix() {
31-
result instanceof UnsafeUrlForwardSanitizingConstantPrefix
32-
or
33-
result.(AddExpr).getAnOperand() = getAUnsafeUrlForwardSanitizingPrefix()
26+
override int getOffset() { result = 0 }
3427
}
3528

36-
private class UnsafeUrlForwardSanitizedExpr extends Expr {
37-
UnsafeUrlForwardSanitizedExpr() {
38-
// Sanitize expressions that come after a sanitizing prefix in a tree of string additions:
39-
this =
40-
any(AddExpr add | add.getLeftOperand() = getAUnsafeUrlForwardSanitizingPrefix())
41-
.getRightOperand()
42-
or
43-
// Sanitize expressions that come after a sanitizing prefix in a sequence of StringBuilder operations:
44-
exists(
45-
StringBuilderConstructorOrAppend appendSanitizingConstant,
46-
StringBuilderAppend subsequentAppend, StringBuilderVarExt v
47-
|
48-
appendSanitizingConstant = v.getAConstructorOrAppend() and
49-
appendSanitizingConstant.getArgument(0) = getAUnsafeUrlForwardSanitizingPrefix() and
50-
v.getSubsequentAppendIncludingAssignmentChains(appendSanitizingConstant) = subsequentAppend and
51-
this = subsequentAppend.getArgument(0)
52-
)
53-
or
54-
exists(MethodAccess ma, int i |
55-
ma.getMethod().hasName("format") and
56-
ma.getMethod().getDeclaringType() instanceof TypeString and
57-
ma.getArgument(0) instanceof UnsafeUrlForwardSanitizingConstantPrefix and
58-
ma.getArgument(i) = this and
59-
i != 0
60-
)
61-
}
62-
}
63-
64-
/**
65-
* A concatenate expression using the string `forward:` on the left.
66-
*
67-
* For example, `"forward:" + url`.
68-
*/
69-
private class ForwardBuilderExpr extends AddExpr {
70-
ForwardBuilderExpr() {
71-
this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "forward:"
72-
}
73-
}
74-
75-
/**
76-
* A call to `StringBuilder.append` or `StringBuffer.append` method, and the parameter value is `"forward:"`.
77-
*
78-
* For example, `StringBuilder.append("forward:")`.
79-
*/
80-
private class ForwardAppendCall extends StringBuilderAppend {
81-
ForwardAppendCall() {
82-
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "forward:"
83-
}
29+
private class FollowsSanitizingPrefix extends UnsafeUrlForwardSanitizer {
30+
FollowsSanitizingPrefix() { this.asExpr() = any(SanitizingPrefix fp).getAnAppendedExpression() }
8431
}
8532

8633
abstract class UnsafeUrlForwardSink extends DataFlow::Node { }
8734

88-
/** A Unsafe url forward sink from getRequestDispatcher method. */
35+
/** An argument to `ServletRequest.getRequestDispatcher`. */
8936
private class RequestDispatcherSink extends UnsafeUrlForwardSink {
9037
RequestDispatcherSink() {
9138
exists(MethodAccess ma |
@@ -95,32 +42,31 @@ private class RequestDispatcherSink extends UnsafeUrlForwardSink {
9542
}
9643
}
9744

98-
/** A Unsafe url forward sink from spring controller method. */
99-
private class SpringUrlForwardSink extends UnsafeUrlForwardSink {
100-
SpringUrlForwardSink() {
101-
exists(ForwardBuilderExpr rbe |
102-
rbe.getRightOperand() = this.asExpr() and
103-
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable())
104-
)
105-
or
106-
exists(MethodAccess ma, ForwardAppendCall rac |
107-
DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and
108-
ma.getMethod().hasName("append") and
109-
ma.getArgument(0) = this.asExpr() and
110-
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable())
111-
)
112-
or
45+
/** An argument to `new ModelAndView` or `ModelAndView.setViewName`. */
46+
private class SpringModelAndViewSink extends UnsafeUrlForwardSink {
47+
SpringModelAndViewSink() {
11348
exists(ClassInstanceExpr cie |
11449
cie.getConstructedType() instanceof ModelAndView and
115-
(
116-
exists(ForwardBuilderExpr rbe |
117-
rbe = cie.getArgument(0) and rbe.getRightOperand() = this.asExpr()
118-
)
119-
or
120-
cie.getArgument(0) = this.asExpr()
121-
)
50+
cie.getArgument(0) = this.asExpr()
12251
)
12352
or
12453
exists(SpringModelAndViewSetViewNameCall smavsvnc | smavsvnc.getArgument(0) = this.asExpr())
12554
}
12655
}
56+
57+
private class ForwardPrefix extends InterestingPrefix {
58+
ForwardPrefix() { this.getStringValue() = "forward:" }
59+
60+
override int getOffset() { result = 0 }
61+
}
62+
63+
/**
64+
* An expression appended (perhaps indirectly) to `"forward:"`, and which
65+
* is reachable from a Spring entry point.
66+
*/
67+
private class SpringUrlForwardSink extends UnsafeUrlForwardSink {
68+
SpringUrlForwardSink() {
69+
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) and
70+
this.asExpr() = any(ForwardPrefix fp).getAnAppendedExpression()
71+
}
72+
}

0 commit comments

Comments
 (0)