11/**
2- * @name Unsafe url forward or dispatch from remote source
2+ * @name Unsafe URL forward or dispatch from remote source
33 * @description URL forward or dispatch based on unvalidated user-input
44 * may cause file information disclosure.
55 * @kind path-problem
1313import java
1414import UnsafeUrlForward
1515import semmle.code.java.dataflow.FlowSources
16- import semmle.code.java.frameworks.Servlets
17- import semmle.code.java.controlflow.Guards
18- import semmle.code.java.dataflow.NullGuards
1916import DataFlow:: PathGraph
2017
21- /**
22- * Holds if `ma` is a call to a method that checks exact match of string.
23- */
24- predicate isExactStringPathMatch ( MethodAccess ma ) {
25- ma .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
26- ma .getMethod ( ) .getName ( ) = [ "equals" , "equalsIgnoreCase" ]
27- }
28-
29- /**
30- * Holds if `ma` is a call to a method that checks a path string.
31- */
32- predicate isStringPathMatch ( MethodAccess ma ) {
33- ma .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
34- ma .getMethod ( ) .getName ( ) =
35- [ "contains" , "startsWith" , "matches" , "regionMatches" , "indexOf" , "lastIndexOf" ]
36- }
37-
38- /**
39- * Holds if `ma` is a call to a method of `java.nio.file.Path` that checks a path.
40- */
41- predicate isFilePathMatch ( MethodAccess ma ) {
42- ma .getMethod ( ) .getDeclaringType ( ) instanceof TypePath and
43- ma .getMethod ( ) .getName ( ) = "startsWith"
44- }
45-
46- /**
47- * Holds if `ma` protects against path traversal, by either:
48- * * looking for the literal `..`
49- * * performing path normalization
50- */
51- predicate isPathTraversalCheck ( MethodAccess ma , Expr checked ) {
52- ma .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
53- ma .getMethod ( ) .hasName ( [ "contains" , "indexOf" ] ) and
54- ma .getAnArgument ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) = ".." and
55- ma .( Guard ) .controls ( checked .getBasicBlock ( ) , false )
56- or
57- ma .getMethod ( ) instanceof PathNormalizeMethod and
58- checked = ma
59- }
60-
61- /**
62- * Holds if `ma` protects against double URL encoding, by either:
63- * * looking for the literal `%`
64- * * performing URL decoding
65- */
66- predicate isURLEncodingCheck ( MethodAccess ma , Expr checked ) {
67- // Search the special character `%` used in url encoding
68- ma .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
69- ma .getMethod ( ) .hasName ( [ "contains" , "indexOf" ] ) and
70- ma .getAnArgument ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) = "%" and
71- ma .( Guard ) .controls ( checked .getBasicBlock ( ) , false )
72- or
73- // Call to `URLDecoder` assuming the implementation handles double encoding correctly
74- ma .getMethod ( ) .getDeclaringType ( ) .hasQualifiedName ( "java.net" , "URLDecoder" ) and
75- ma .getMethod ( ) .hasName ( "decode" ) and
76- checked = ma
77- }
78-
79- /** The Java method `normalize` of `java.nio.file.Path`. */
80- class PathNormalizeMethod extends Method {
81- PathNormalizeMethod ( ) {
82- this .getDeclaringType ( ) .hasQualifiedName ( "java.nio.file" , "Path" ) and
83- this .hasName ( "normalize" )
84- }
85- }
86-
87- private predicate isDisallowedWord ( CompileTimeConstantExpr word ) {
88- word .getStringValue ( ) .matches ( [ "%WEB-INF%" , "%META-INF%" , "%..%" ] )
89- }
90-
91- private predicate isAllowListCheck ( MethodAccess ma ) {
92- ( isStringPathMatch ( ma ) or isFilePathMatch ( ma ) ) and
93- not isDisallowedWord ( ma .getAnArgument ( ) )
94- }
95-
96- private predicate isDisallowListCheck ( MethodAccess ma ) {
97- ( isStringPathMatch ( ma ) or isFilePathMatch ( ma ) ) and
98- isDisallowedWord ( ma .getAnArgument ( ) )
99- }
100-
101- /**
102- * A guard that checks a path with the following methods:
103- * 1. Exact string match
104- * 2. Path matches allowed values (needs to protect against path traversal)
105- * 3. Path matches disallowed values (needs to protect against URL encoding)
106- */
107- private class PathMatchGuard extends DataFlow:: BarrierGuard {
108- PathMatchGuard ( ) {
109- isExactStringPathMatch ( this ) or isStringPathMatch ( this ) or isFilePathMatch ( this )
110- }
111-
112- override predicate checks ( Expr e , boolean branch ) {
113- e = this .( MethodAccess ) .getQualifier ( ) and
114- (
115- isExactStringPathMatch ( this ) and
116- branch = true
117- or
118- isAllowListCheck ( this ) and
119- exists ( MethodAccess ma , Expr checked | isPathTraversalCheck ( ma , checked ) |
120- DataFlow:: localExprFlow ( checked , e )
121- or
122- ma .getParent * ( ) .( BinaryExpr ) = this .( MethodAccess ) .getParent * ( )
123- ) and
124- branch = true
125- or
126- isDisallowListCheck ( this ) and
127- exists ( MethodAccess ma , Expr checked | isURLEncodingCheck ( ma , checked ) |
128- DataFlow:: localExprFlow ( checked , e )
129- or
130- ma .getParent * ( ) .( BinaryExpr ) = this .( MethodAccess ) .getParent * ( )
131- ) and
132- branch = false
133- )
134- }
135- }
136-
13718class UnsafeUrlForwardFlowConfig extends TaintTracking:: Configuration {
13819 UnsafeUrlForwardFlowConfig ( ) { this = "UnsafeUrlForwardFlowConfig" }
13920
@@ -154,7 +35,7 @@ class UnsafeUrlForwardFlowConfig extends TaintTracking::Configuration {
15435 override predicate isSanitizer ( DataFlow:: Node node ) { node instanceof UnsafeUrlForwardSanitizer }
15536
15637 override predicate isSanitizerGuard ( DataFlow:: BarrierGuard guard ) {
157- guard instanceof PathMatchGuard
38+ guard instanceof UnsafeUrlForwardBarrierGuard
15839 }
15940
16041 override DataFlow:: FlowFeature getAFeature ( ) {
0 commit comments