@@ -18,20 +18,48 @@ class StartsWithSanitizer extends DataFlow::BarrierGuard {
18
18
}
19
19
20
20
/**
21
- * A concatenate expression using the string `redirect:` on the left.
21
+ * A concatenate expression using the string `redirect:` or `ajaxredirect:` or `forward:` on the left.
22
22
*
23
23
* E.g: `"redirect:" + redirectUrl`
24
24
*/
25
25
class RedirectBuilderExpr extends AddExpr {
26
26
RedirectBuilderExpr ( ) {
27
- this .getLeftOperand ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) = "redirect:"
27
+ this .getLeftOperand ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) in [
28
+ "redirect:" , "ajaxredirect:" , "forward:"
29
+ ]
30
+ }
31
+ }
32
+
33
+ /**
34
+ * A call to `StringBuilder.append` or `StringBuffer.append` method, and the parameter value is
35
+ * `"redirect:"` or `"ajaxredirect:"` or `"forward:"`.
36
+ *
37
+ * E.g: `StringBuilder.append("redirect:")`
38
+ */
39
+ class RedirectAppendCall extends MethodAccess {
40
+ RedirectAppendCall ( ) {
41
+ this .getMethod ( ) .hasName ( "append" ) and
42
+ this .getMethod ( ) .getDeclaringType ( ) instanceof StringBuildingType and
43
+ this .getArgument ( 0 ) .( CompileTimeConstantExpr ) .getStringValue ( ) in [
44
+ "redirect:" , "ajaxredirect:" , "forward:"
45
+ ]
28
46
}
29
47
}
30
48
31
49
/** A URL redirection sink from spring controller method. */
32
50
class SpringUrlRedirectSink extends DataFlow:: Node {
33
51
SpringUrlRedirectSink ( ) {
34
- exists ( RedirectBuilderExpr rbe | rbe .getRightOperand ( ) = this .asExpr ( ) )
52
+ exists ( RedirectBuilderExpr rbe |
53
+ rbe .getRightOperand ( ) = this .asExpr ( ) and
54
+ exists ( RedirectBuilderFlowConfig rbfc | rbfc .hasFlow ( exprNode ( rbe ) , _) )
55
+ )
56
+ or
57
+ exists ( MethodAccess ma , RedirectAppendCall rac |
58
+ DataFlow2:: localExprFlow ( rac .getQualifier ( ) , ma .getQualifier ( ) ) and
59
+ ma .getMethod ( ) .hasName ( "append" ) and
60
+ ma .getArgument ( 0 ) = this .asExpr ( ) and
61
+ exists ( RedirectBuilderFlowConfig rbfc | rbfc .hasFlow ( exprNode ( ma .getQualifier ( ) ) , _) )
62
+ )
35
63
or
36
64
exists ( MethodAccess ma |
37
65
ma .getMethod ( ) .hasName ( "setUrl" ) and
@@ -50,8 +78,40 @@ class SpringUrlRedirectSink extends DataFlow::Node {
50
78
or
51
79
exists ( ClassInstanceExpr cie |
52
80
cie .getConstructedType ( ) .hasQualifiedName ( "org.springframework.web.servlet" , "ModelAndView" ) and
53
- cie .getArgument ( 0 ) = this .asExpr ( ) and
54
- exists ( RedirectBuilderExpr rbe | rbe .getRightOperand ( ) = this .asExpr ( ) )
81
+ exists ( RedirectBuilderExpr rbe |
82
+ rbe = cie .getArgument ( 0 ) and rbe .getRightOperand ( ) = this .asExpr ( )
83
+ )
84
+ )
85
+ }
86
+ }
87
+
88
+ /** A data flow configuration tracing flow from redirect builder expression to spring controller method return expression. */
89
+ private class RedirectBuilderFlowConfig extends DataFlow2:: Configuration {
90
+ RedirectBuilderFlowConfig ( ) { this = "RedirectBuilderFlowConfig" }
91
+
92
+ override predicate isSource ( DataFlow:: Node src ) {
93
+ exists ( RedirectBuilderExpr rbe | rbe = src .asExpr ( ) )
94
+ or
95
+ exists ( MethodAccess ma , RedirectAppendCall rac |
96
+ DataFlow2:: localExprFlow ( rac .getQualifier ( ) , ma .getQualifier ( ) ) and
97
+ ma .getMethod ( ) .hasName ( "append" ) and
98
+ ma .getQualifier ( ) = src .asExpr ( )
99
+ )
100
+ }
101
+
102
+ override predicate isSink ( DataFlow:: Node sink ) {
103
+ exists ( ReturnStmt rs , SpringRequestMappingMethod sqmm |
104
+ rs .getResult ( ) = sink .asExpr ( ) and
105
+ sqmm .getBody ( ) .getAStmt ( ) = rs
106
+ )
107
+ }
108
+
109
+ override predicate isAdditionalFlowStep ( Node prod , Node succ ) {
110
+ exists ( MethodAccess ma |
111
+ ma .getMethod ( ) .hasName ( "toString" ) and
112
+ ma .getMethod ( ) .getDeclaringType ( ) instanceof StringBuildingType and
113
+ ma .getQualifier ( ) = prod .asExpr ( ) and
114
+ ma = succ .asExpr ( )
55
115
)
56
116
}
57
117
}
0 commit comments