@@ -9,6 +9,7 @@ import semmle.code.java.frameworks.ApacheHttp
99private import semmle.code.java.dataflow.ExternalFlow
1010private import semmle.code.java.dataflow.FlowSinks
1111private import semmle.code.java.dataflow.StringPrefixes
12+ private import semmle.code.java.dataflow.TaintTracking
1213private import semmle.code.java.frameworks.JaxWS
1314private import semmle.code.java.frameworks.spring.SpringController
1415private import semmle.code.java.security.RequestForgery
@@ -52,14 +53,26 @@ private class ApacheUrlRedirectSink extends UrlRedirectSink {
5253 }
5354}
5455
55- /**
56- * An expression appended to a Spring `"redirect:"` view-name prefix from a request handler or a
57- * helper called by one.
58- */
56+ /** An expression appended to a Spring `"redirect:"` view-name returned by a request handler. */
5957private class SpringUrlRedirectPrefixSink extends UrlRedirectSink {
6058 SpringUrlRedirectPrefixSink ( ) {
61- isSpringMvcViewResult ( this .asExpr ( ) ) and
62- appendedToRedirectPrefix ( this )
59+ appendedToRedirectPrefix ( this ) and
60+ (
61+ isSpringMvcReturnedString ( this .asExpr ( ) )
62+ or
63+ isSpringModelAndViewName ( this .asExpr ( ) )
64+ )
65+ }
66+ }
67+
68+ /** A call to a helper that returns a Spring `"redirect:"` view name. */
69+ private class SpringUrlRedirectHelperSink extends UrlRedirectSink {
70+ SpringUrlRedirectHelperSink ( ) {
71+ exists ( MethodCall call |
72+ this .asExpr ( ) = call and
73+ isSpringMvcViewResult ( call ) and
74+ returnsRedirectViewName ( call .getCallee ( ) .getSourceDeclaration ( ) )
75+ )
6376 }
6477}
6578
@@ -77,11 +90,15 @@ private class SpringRedirectPrefix extends InterestingPrefix {
7790private predicate contributesToReturn ( Expr value ) {
7891 exists ( ReturnStmt ret |
7992 ret .getEnclosingCallable ( ) = value .getEnclosingCallable ( ) and
80- (
81- value .getParent * ( ) = ret .getExpr ( )
82- or
83- DataFlow:: localFlow ( DataFlow:: exprNode ( value ) , DataFlow:: exprNode ( ret .getExpr ( ) ) )
84- )
93+ DataFlow:: localFlow ( DataFlow:: exprNode ( value ) , DataFlow:: exprNode ( ret .getExpr ( ) ) )
94+ )
95+ }
96+
97+ /** Holds if `value` contributes string content to the return value of its callable. */
98+ private predicate contributesToReturnedString ( Expr value ) {
99+ exists ( ReturnStmt ret |
100+ ret .getEnclosingCallable ( ) = value .getEnclosingCallable ( ) and
101+ TaintTracking:: localTaint ( DataFlow:: exprNode ( value ) , DataFlow:: exprNode ( ret .getExpr ( ) ) )
85102 )
86103}
87104
@@ -91,25 +108,51 @@ private predicate isSpringMvcViewResult(Expr value) {
91108 contributesToReturn ( value ) and
92109 value .getEnclosingCallable ( ) instanceof SpringRequestMappingMethod and
93110 not value .getEnclosingCallable ( ) .( SpringRequestMappingMethod ) .isResponseBody ( )
94- or
95- contributesToReturn ( value ) and
96- exists ( MethodCall call |
97- call .getCallee ( ) .getSourceDeclaration ( ) = value .getEnclosingCallable ( ) and
98- isSpringMvcViewResult ( call )
111+ }
112+
113+ /** Holds if `value` contributes string content to a view name returned by a request handler. */
114+ private predicate isSpringMvcReturnedString ( Expr value ) {
115+ contributesToReturnedString ( value ) and
116+ value .getEnclosingCallable ( ) instanceof SpringRequestMappingMethod and
117+ not value .getEnclosingCallable ( ) .( SpringRequestMappingMethod ) .isResponseBody ( )
118+ }
119+
120+ /** Holds if `value` contributes to the view name of a returned `ModelAndView`. */
121+ private predicate isSpringModelAndViewName ( Expr value ) {
122+ exists ( ClassInstanceExpr newModelAndView |
123+ newModelAndView
124+ .getConstructedType ( )
125+ .hasQualifiedName ( "org.springframework.web.servlet" , "ModelAndView" ) and
126+ TaintTracking:: localTaint ( DataFlow:: exprNode ( value ) ,
127+ DataFlow:: exprNode ( newModelAndView .getArgument ( 0 ) ) ) and
128+ isSpringMvcViewResult ( newModelAndView )
99129 )
100130}
101131
102- private class SpringRedirectViewType extends RefType {
103- SpringRedirectViewType ( ) {
104- this .getASupertype * ( ) .hasQualifiedName ( "org.springframework.web.servlet.view" , "RedirectView" )
105- }
132+ /** Holds if `callable` returns a view name constructed with the Spring `"redirect:"` prefix. */
133+ pragma [ nomagic]
134+ private predicate returnsRedirectViewName ( Callable callable ) {
135+ exists ( DataFlow:: ExprNode appended , ReturnStmt ret |
136+ appendedToRedirectPrefix ( appended ) and
137+ appended .asExpr ( ) .getEnclosingCallable ( ) = callable and
138+ ret .getEnclosingCallable ( ) = callable and
139+ TaintTracking:: localTaint ( appended , DataFlow:: exprNode ( ret .getExpr ( ) ) )
140+ )
141+ or
142+ exists ( MethodCall call |
143+ call .getEnclosingCallable ( ) = callable and
144+ contributesToReturn ( call ) and
145+ returnsRedirectViewName ( call .getCallee ( ) .getSourceDeclaration ( ) )
146+ )
106147}
107148
108149/** A URL passed to a Spring `RedirectView` constructor. */
109150private class SpringRedirectViewSink extends UrlRedirectSink {
110151 SpringRedirectViewSink ( ) {
111152 exists ( ClassInstanceExpr newRedirectView |
112- newRedirectView .getConstructedType ( ) instanceof SpringRedirectViewType and
153+ newRedirectView
154+ .getConstructedType ( )
155+ .hasQualifiedName ( "org.springframework.web.servlet.view" , "RedirectView" ) and
113156 isSpringMvcViewResult ( newRedirectView ) and
114157 this .asExpr ( ) = newRedirectView .getArgument ( 0 )
115158 )
@@ -120,14 +163,16 @@ private class SpringRedirectViewSink extends UrlRedirectSink {
120163private class SpringRedirectViewSetUrlSink extends UrlRedirectSink {
121164 SpringRedirectViewSetUrlSink ( ) {
122165 exists ( MethodCall setUrl |
123- setUrl .getMethod ( ) .hasName ( "setUrl" ) and
124- setUrl .getMethod ( ) .getNumberOfParameters ( ) = 1 and
125166 setUrl
126167 .getMethod ( )
127- .getDeclaringType ( )
168+ .getSourceDeclaration ( )
169+ .hasQualifiedName ( "org.springframework.web.servlet.view" , "AbstractUrlBasedView" , "setUrl" ) and
170+ setUrl
171+ .getQualifier ( )
172+ .getType ( )
173+ .( RefType )
128174 .getASupertype * ( )
129- .hasQualifiedName ( "org.springframework.web.servlet.view" , "AbstractUrlBasedView" ) and
130- setUrl .getQualifier ( ) .getType ( ) instanceof SpringRedirectViewType and
175+ .hasQualifiedName ( "org.springframework.web.servlet.view" , "RedirectView" ) and
131176 isSpringMvcViewResult ( setUrl .getQualifier ( ) ) and
132177 this .asExpr ( ) = setUrl .getArgument ( 0 )
133178 )
0 commit comments