@@ -4,6 +4,7 @@ import semmle.code.java.dataflow.FlowSources
44import semmle.code.java.frameworks.Servlets
55import semmle.code.java.frameworks.spring.SpringWeb
66import semmle.code.java.security.RequestForgery
7+ private import semmle.code.java.dataflow.StringPrefixes
78
89/** A sanitizer for unsafe url forward vulnerabilities. */
910abstract 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
8633abstract class UnsafeUrlForwardSink extends DataFlow:: Node { }
8734
88- /** A Unsafe url forward sink from getRequestDispatcher method . */
35+ /** An argument to `ServletRequest. getRequestDispatcher` . */
8936private 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