Skip to content

Commit f143d1b

Browse files
Jami CogswellJami Cogswell
authored andcommitted
Java: partial generalization of replace/replaceAll
1 parent 2d8e437 commit f143d1b

File tree

2 files changed

+37
-14
lines changed

2 files changed

+37
-14
lines changed

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

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -390,22 +390,40 @@ private class FileConstructorChildArgumentStep extends AdditionalTaintStep {
390390
*/
391391
private class ReplaceDirectoryCharactersSanitizer extends MethodCall {
392392
ReplaceDirectoryCharactersSanitizer() {
393-
exists(MethodCall mc |
394-
// TODO: "java.lang.String.replace" as well
395-
mc.getMethod().hasQualifiedName("java.lang", "String", "replaceAll") and
396-
// TODO: unhardcode all of the below to handle more valid replacements and several calls
393+
exists(
394+
MethodCall mc, Method m, CompileTimeConstantExpr target, CompileTimeConstantExpr replacement
395+
|
396+
m = mc.getMethod() and
397+
m.getDeclaringType() instanceof TypeString and
398+
m.hasName(["replaceAll", "replace"]) and
399+
// TODO: make sure handling each arg 0 correctly, only replaceAll is a regex, replace is char or CharSequence
400+
// TODO: add tests for replace
401+
target = mc.getArgument(0) and
402+
replacement = mc.getArgument(1) and
403+
this = mc
404+
|
397405
(
398-
mc.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "\\.\\.|[/\\\\]"
406+
// replace with single call
407+
target.getStringValue().matches("[%]") and
408+
target.getStringValue().matches("[%\\.%]%") and
409+
target.getStringValue().matches("[%/%]%") and
410+
target.getStringValue().matches("[%\\\\%]%")
411+
or
412+
target.getStringValue().matches("%|%") and
413+
target.getStringValue().matches("%" + ["\\.\\.", "[\\.][\\.]", "\\."] + "%") and
414+
target.getStringValue().matches("%/%") and
415+
target.getStringValue().matches("%\\\\%")
399416
or
400-
exists(MethodCall mc2 |
401-
mc2.getMethod().hasQualifiedName("java.lang", "String", "replaceAll") and
402-
mc.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "\\." and
403-
mc2.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "/"
404-
)
417+
// replace with multiple calls
418+
// TODO: handle both as call chain and as separate line? (presumably a max of three calls?)
419+
target.getStringValue() = ["\\.", "/", "\\\\"] and
420+
mc.getQualifier() =
421+
any(MethodCall mc2 |
422+
mc2.getMethod().hasQualifiedName("java.lang", "String", ["replaceAll", "replace"]) and
423+
mc2.getArgument(0).(CompileTimeConstantExpr).getStringValue() = ["\\.", "/", "\\\\"]
424+
)
405425
) and
406-
// TODO: accept more replacement characters?
407-
mc.getArgument(1).(CompileTimeConstantExpr).getStringValue() = ["", "_"] and
408-
this = mc
426+
replacement.getStringValue() = ["", "_", "-"]
409427
)
410428
}
411429
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,11 @@ public void directoryCharsSanitizer() throws Exception {
662662
source = source.replaceAll("\\.\\.|[/\\\\]", "");
663663
sink(source); // Safe
664664
}
665+
{
666+
String source = (String) source();
667+
source = source.replaceAll("[\\./\\\\]", "");
668+
sink(source); // Safe
669+
}
665670
{
666671
String source = (String) source();
667672
source = source.replaceAll("\\.", "").replaceAll("/", "");
@@ -670,7 +675,7 @@ public void directoryCharsSanitizer() throws Exception {
670675
{
671676
String source = (String) source();
672677
// Bypassable with ".../...//"
673-
source = source.replaceAll("../", "");
678+
source = source.replaceAll("\\.\\./", "");
674679
sink(source); // $ hasTaintFlow
675680
}
676681
{

0 commit comments

Comments
 (0)