Skip to content

Commit 90020b6

Browse files
committed
Make block lists work with substring matching too
A block list approach doesn't need to restrict itself to prefix matching
1 parent 69d1895 commit 90020b6

File tree

2 files changed

+14
-20
lines changed

2 files changed

+14
-20
lines changed

java/ql/lib/semmle/code/java/security/PathSanitizer.qll

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ private class ExactPathMatchSanitizer extends PathInjectionSanitizer {
7272
private class AllowListGuard extends Guard instanceof MethodAccess {
7373
AllowListGuard() {
7474
(isStringPrefixMatch(this) or isPathPrefixMatch(this)) and
75-
not isDisallowedPrefix(super.getAnArgument()) and
7675
not isDisallowedWord(super.getAnArgument())
7776
}
7877

@@ -127,10 +126,7 @@ private class DotDotCheckSanitizer extends PathInjectionSanitizer {
127126

128127
private class BlockListGuard extends Guard instanceof MethodAccess {
129128
BlockListGuard() {
130-
(isStringPrefixMatch(this) or isPathPrefixMatch(this)) and
131-
isDisallowedPrefix(super.getAnArgument())
132-
or
133-
isStringPartialMatch(this) and
129+
(isStringPartialMatch(this) or isPathPrefixMatch(this)) and
134130
isDisallowedWord(super.getAnArgument())
135131
}
136132

@@ -178,6 +174,8 @@ private predicate isStringPrefixMatch(MethodAccess ma) {
178174
* Holds if `ma` is a call to a method that checks a partial string match.
179175
*/
180176
private predicate isStringPartialMatch(MethodAccess ma) {
177+
isStringPrefixMatch(ma)
178+
or
181179
ma.getMethod().getDeclaringType() instanceof TypeString and
182180
ma.getMethod().hasName(["contains", "matches", "regionMatches", "indexOf", "lastIndexOf"])
183181
}
@@ -196,12 +194,8 @@ private predicate isPathPrefixMatch(MethodAccess ma) {
196194
)
197195
}
198196

199-
private predicate isDisallowedPrefix(CompileTimeConstantExpr prefix) {
200-
prefix.getStringValue().matches(["%WEB-INF%", "/data%"])
201-
}
202-
203197
private predicate isDisallowedWord(CompileTimeConstantExpr word) {
204-
word.getStringValue().matches(["/", "\\"])
198+
word.getStringValue().matches(["/", "\\", "%WEB-INF%", "%/data%"])
205199
}
206200

207201
/** A complementary guard that protects against path traversal, by looking for the literal `..`. */

java/ql/test/library-tests/pathsanitizer/Test.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -421,13 +421,13 @@ public void blockListGuard() throws Exception {
421421
sink(normalized); // $ hasTaintFlow
422422
}
423423
}
424-
// PathInjectionSanitizer + partial string match with prefixes is considered unsafe
424+
// PathInjectionSanitizer + partial string match with disallowed prefixes
425425
{
426426
String source = (String) source();
427427
String normalized = Paths.get(source).normalize().toString();
428-
if (normalized.contains("/data")) {
429-
sink(source); // $ hasTaintFlow
430-
sink(normalized); // $ hasTaintFlow
428+
if (!normalized.contains("/data")) {
429+
sink(source); // Safe
430+
sink(normalized); // Safe
431431
} else {
432432
sink(source); // $ hasTaintFlow
433433
sink(normalized); // $ hasTaintFlow
@@ -436,9 +436,9 @@ public void blockListGuard() throws Exception {
436436
{
437437
String source = (String) source();
438438
String normalized = Paths.get(source).normalize().toString();
439-
if (normalized.regionMatches(1, "/data", 0, 5)) {
440-
sink(source); // $ hasTaintFlow
441-
sink(normalized); // $ hasTaintFlow
439+
if (!normalized.regionMatches(1, "/data", 0, 5)) {
440+
sink(source); // Safe
441+
sink(normalized); // Safe
442442
} else {
443443
sink(source); // $ hasTaintFlow
444444
sink(normalized); // $ hasTaintFlow
@@ -447,9 +447,9 @@ public void blockListGuard() throws Exception {
447447
{
448448
String source = (String) source();
449449
String normalized = Paths.get(source).normalize().toString();
450-
if (normalized.matches(".*/data/.*")) {
451-
sink(source); // $ hasTaintFlow
452-
sink(normalized); // $ hasTaintFlow
450+
if (!normalized.matches(".*/data/.*")) {
451+
sink(source); // Safe
452+
sink(normalized); // Safe
453453
} else {
454454
sink(source); // $ hasTaintFlow
455455
sink(normalized); // $ hasTaintFlow

0 commit comments

Comments
 (0)