@@ -18,20 +18,48 @@ class StartsWithSanitizer extends DataFlow::BarrierGuard {
1818}
1919
2020/**
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.
2222 *
2323 * E.g: `"redirect:" + redirectUrl`
2424 */
2525class RedirectBuilderExpr extends AddExpr {
2626 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+ ]
2846 }
2947}
3048
3149/** A URL redirection sink from spring controller method. */
3250class SpringUrlRedirectSink extends DataFlow:: Node {
3351 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+ )
3563 or
3664 exists ( MethodAccess ma |
3765 ma .getMethod ( ) .hasName ( "setUrl" ) and
@@ -50,8 +78,40 @@ class SpringUrlRedirectSink extends DataFlow::Node {
5078 or
5179 exists ( ClassInstanceExpr cie |
5280 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 ( )
55115 )
56116 }
57117}
0 commit comments