1
+ /** Provides classes and predicates to reason about sanitization of path injection vulnerabilities. */
2
+
1
3
import java
2
4
private import semmle.code.java.controlflow.Guards
3
5
private import semmle.code.java.dataflow.FlowSources
4
6
private import semmle.code.java.dataflow.ExternalFlow
5
7
6
- /**
7
- * DEPRECATED: Use `PathTraversalSanitizer` instead.
8
- *
9
- * A barrier guard that protects against path traversal vulnerabilities.
10
- */
11
- abstract deprecated class PathTraversalBarrierGuard extends DataFlow:: BarrierGuard { }
12
-
13
- /** A sanitizer that protects against path traversal vulnerabilities. */
14
- abstract class PathTraversalSanitizer extends DataFlow:: Node { }
8
+ /** A sanitizer that protects against path injection vulnerabilities. */
9
+ abstract class PathInjectionSanitizer extends DataFlow:: Node { }
15
10
16
11
/**
17
12
* Holds if `g` is guard that compares a string to a trusted value.
@@ -26,7 +21,7 @@ private predicate exactStringPathMatchGuard(Guard g, Expr e, boolean branch) {
26
21
)
27
22
}
28
23
29
- private class ExactStringPathMatchSanitizer extends PathTraversalSanitizer {
24
+ private class ExactStringPathMatchSanitizer extends PathInjectionSanitizer {
30
25
ExactStringPathMatchSanitizer ( ) {
31
26
this = DataFlow:: BarrierGuard< exactStringPathMatchGuard / 3 > :: getABarrierNode ( )
32
27
}
@@ -38,7 +33,7 @@ private class ExactStringPathMatchSanitizer extends PathTraversalSanitizer {
38
33
* This is used to look through field accessors such as `uri.getPath()`.
39
34
*/
40
35
private Expr getUnderlyingVarAccess ( Expr e ) {
41
- result = getUnderlyingVarAccess ( e .( MethodAccess ) .getQualifier ( ) )
36
+ result = getUnderlyingVarAccess ( e .( MethodAccess ) .getQualifier ( ) . getUnderlyingExpr ( ) )
42
37
or
43
38
result = e .( VarAccess )
44
39
}
@@ -49,7 +44,9 @@ private class AllowListGuard extends Guard instanceof MethodAccess {
49
44
not isDisallowedWord ( super .getAnArgument ( ) )
50
45
}
51
46
52
- Expr getCheckedExpr ( ) { result = getUnderlyingVarAccess ( super .getQualifier ( ) ) }
47
+ Expr getCheckedExpr ( ) {
48
+ result = getUnderlyingVarAccess ( super .getQualifier ( ) .getUnderlyingExpr ( ) )
49
+ }
53
50
}
54
51
55
52
/**
@@ -72,7 +69,7 @@ private predicate allowListGuard(Guard g, Expr e, boolean branch) {
72
69
)
73
70
}
74
71
75
- private class AllowListSanitizer extends PathTraversalSanitizer {
72
+ private class AllowListSanitizer extends PathInjectionSanitizer {
76
73
AllowListSanitizer ( ) { this = DataFlow:: BarrierGuard< allowListGuard / 3 > :: getABarrierNode ( ) }
77
74
}
78
75
@@ -90,7 +87,7 @@ private predicate dotDotCheckGuard(Guard g, Expr e, boolean branch) {
90
87
)
91
88
}
92
89
93
- private class DotDotCheckSanitizer extends PathTraversalSanitizer {
90
+ private class DotDotCheckSanitizer extends PathInjectionSanitizer {
94
91
DotDotCheckSanitizer ( ) { this = DataFlow:: BarrierGuard< dotDotCheckGuard / 3 > :: getABarrierNode ( ) }
95
92
}
96
93
@@ -100,7 +97,9 @@ private class BlockListGuard extends Guard instanceof MethodAccess {
100
97
isDisallowedWord ( super .getAnArgument ( ) )
101
98
}
102
99
103
- Expr getCheckedExpr ( ) { result = getUnderlyingVarAccess ( super .getQualifier ( ) ) }
100
+ Expr getCheckedExpr ( ) {
101
+ result = getUnderlyingVarAccess ( super .getQualifier ( ) .getUnderlyingExpr ( ) )
102
+ }
104
103
}
105
104
106
105
/**
@@ -123,7 +122,7 @@ private predicate blockListGuard(Guard g, Expr e, boolean branch) {
123
122
)
124
123
}
125
124
126
- private class BlockListSanitizer extends PathTraversalSanitizer {
125
+ private class BlockListSanitizer extends PathInjectionSanitizer {
127
126
BlockListSanitizer ( ) { this = DataFlow:: BarrierGuard< blockListGuard / 3 > :: getABarrierNode ( ) }
128
127
}
129
128
@@ -140,7 +139,7 @@ private predicate urlEncodingGuard(Guard g, Expr e, boolean branch) {
140
139
)
141
140
}
142
141
143
- private class UrlEncodingSanitizer extends PathTraversalSanitizer {
142
+ private class UrlEncodingSanitizer extends PathInjectionSanitizer {
144
143
UrlEncodingSanitizer ( ) { this = DataFlow:: BarrierGuard< urlEncodingGuard / 3 > :: getABarrierNode ( ) }
145
144
}
146
145
@@ -149,38 +148,51 @@ private class UrlEncodingSanitizer extends PathTraversalSanitizer {
149
148
*/
150
149
private predicate isStringPartialMatch ( MethodAccess ma ) {
151
150
ma .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
152
- ma .getMethod ( ) . getName ( ) =
153
- [ "contains" , "startsWith" , "matches" , "regionMatches" , "indexOf" , "lastIndexOf" ]
151
+ ma .getMethod ( )
152
+ . hasName ( [ "contains" , "startsWith" , "matches" , "regionMatches" , "indexOf" , "lastIndexOf" ] )
154
153
}
155
154
156
155
/**
157
- * Holds if `ma` is a call to a method of `java.nio.file.Path` that checks a partial path match.
156
+ * Holds if `ma` is a call to a method that checks a partial path match.
158
157
*/
159
158
private predicate isPathPartialMatch ( MethodAccess ma ) {
160
159
ma .getMethod ( ) .getDeclaringType ( ) instanceof TypePath and
161
- ma .getMethod ( ) .getName ( ) = "startsWith"
160
+ ma .getMethod ( ) .hasName ( "startsWith" )
161
+ or
162
+ ma .getMethod ( ) .getDeclaringType ( ) .hasQualifiedName ( "kotlin.io" , "FilesKt" ) and
163
+ ma .getMethod ( ) .hasName ( "startsWith" )
162
164
}
163
165
164
166
private predicate isDisallowedWord ( CompileTimeConstantExpr word ) {
165
167
word .getStringValue ( ) .matches ( [ "%WEB-INF%" , "%META-INF%" , "%..%" ] )
166
168
}
167
169
168
170
/** A complementary guard that protects against path traversal, by looking for the literal `..`. */
169
- class PathTraversalGuard extends Guard instanceof MethodAccess {
171
+ private class PathTraversalGuard extends Guard instanceof MethodAccess {
170
172
PathTraversalGuard ( ) {
171
173
super .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
172
174
super .getMethod ( ) .hasName ( [ "contains" , "indexOf" ] ) and
173
175
super .getAnArgument ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) = ".."
174
176
}
175
177
176
- Expr getCheckedExpr ( ) { result = getUnderlyingVarAccess ( super .getQualifier ( ) ) }
178
+ Expr getCheckedExpr ( ) {
179
+ result = getUnderlyingVarAccess ( super .getQualifier ( ) .getUnderlyingExpr ( ) )
180
+ }
177
181
}
178
182
179
183
/** A complementary sanitizer that protects against path traversal using path normalization. */
180
184
private class PathNormalizeSanitizer extends MethodAccess {
181
185
PathNormalizeSanitizer ( ) {
182
- this .getMethod ( ) .getDeclaringType ( ) .hasQualifiedName ( "java.nio.file" , "Path" ) and
183
- this .getMethod ( ) .hasName ( "normalize" )
186
+ exists ( RefType t |
187
+ t instanceof TypePath or
188
+ t .hasQualifiedName ( "kotlin.io" , "FilesKt" )
189
+ |
190
+ this .getMethod ( ) .getDeclaringType ( ) = t and
191
+ this .getMethod ( ) .hasName ( "normalize" )
192
+ )
193
+ or
194
+ this .getMethod ( ) .getDeclaringType ( ) instanceof TypeFile and
195
+ this .getMethod ( ) .hasName ( [ "getCanonicalPath" , "getCanonicalFile" ] )
184
196
}
185
197
}
186
198
@@ -198,8 +210,13 @@ private class UrlEncodingGuard extends Guard instanceof MethodAccess {
198
210
/** A complementary sanitizer that protects against double URL encoding using URL decoding. */
199
211
private class UrlDecodeSanitizer extends MethodAccess {
200
212
UrlDecodeSanitizer ( ) {
201
- this .getMethod ( ) .getDeclaringType ( ) .hasQualifiedName ( "java.net" , "URLDecoder" ) and
202
- this .getMethod ( ) .hasName ( "decode" )
213
+ exists ( RefType t |
214
+ this .getMethod ( ) .getDeclaringType ( ) = t and
215
+ this .getMethod ( ) .hasName ( "decode" )
216
+ |
217
+ t .hasQualifiedName ( "java.net" , "URLDecoder" ) or
218
+ t .hasQualifiedName ( "android.net" , "Uri" )
219
+ )
203
220
}
204
221
}
205
222
@@ -209,14 +226,3 @@ class NormalizedPathNode extends DataFlow::Node {
209
226
TaintTracking:: localExprTaint ( this .asExpr ( ) , any ( PathNormalizeSanitizer ma ) )
210
227
}
211
228
}
212
-
213
- /** Data model related to `java.nio.file.Path`. */
214
- private class PathDataModel extends SummaryModelCsv {
215
- override predicate row ( string row ) {
216
- row =
217
- [
218
- "java.nio.file;Paths;true;get;;;Argument[0];ReturnValue;taint;manual" ,
219
- "java.nio.file;Path;true;normalize;;;Argument[-1];ReturnValue;taint;manual"
220
- ]
221
- }
222
- }
0 commit comments