1
1
/**
2
- * @name Unsafe url forward or dispatch from remote source
2
+ * @name Unsafe URL forward or dispatch from remote source
3
3
* @description URL forward or dispatch based on unvalidated user-input
4
4
* may cause file information disclosure.
5
5
* @kind path-problem
13
13
import java
14
14
import UnsafeUrlForward
15
15
import 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
19
16
import DataFlow:: PathGraph
20
17
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
-
137
18
class UnsafeUrlForwardFlowConfig extends TaintTracking:: Configuration {
138
19
UnsafeUrlForwardFlowConfig ( ) { this = "UnsafeUrlForwardFlowConfig" }
139
20
@@ -154,7 +35,7 @@ class UnsafeUrlForwardFlowConfig extends TaintTracking::Configuration {
154
35
override predicate isSanitizer ( DataFlow:: Node node ) { node instanceof UnsafeUrlForwardSanitizer }
155
36
156
37
override predicate isSanitizerGuard ( DataFlow:: BarrierGuard guard ) {
157
- guard instanceof PathMatchGuard
38
+ guard instanceof UnsafeUrlForwardBarrierGuard
158
39
}
159
40
160
41
override DataFlow:: FlowFeature getAFeature ( ) {
0 commit comments