From 88ef6cffb4847f3b69d431ac405351d6dbcb03b2 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 16 Oct 2026 14:56:49 +0100 Subject: [PATCH 1/5] Test command injection through delegating launchers Document supported simple forms and known limitations with inline expectations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../security/CWE-078/ExecRelative.expected | 2 +- .../security/CWE-078/ExecTainted.expected | 80 +++---- .../security/CWE-078/ExecUnescaped.expected | 4 +- .../query-tests/security/CWE-078/Test.java | 204 +++++++++++++++++- 4 files changed, 240 insertions(+), 50 deletions(-) diff --git a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected index e5b842b4395e..4ff84d888b6c 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected @@ -1,2 +1,2 @@ | TaintedEnvironment.java:39:35:39:55 | new String[] | Command with a relative path 'ls' is executed. | -| Test.java:50:46:50:49 | "ls" | Command with a relative path 'ls' is executed. | +| Test.java:219:46:219:49 | "ls" | Command with a relative path 'ls' is executed. | diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected index e65a61d76988..fd2dd9d59447 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected @@ -1,48 +1,48 @@ #select -| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:25:7:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | -| Test.java:10:29:10:74 | new String[] | Test.java:57:27:57:39 | args : String[] | Test.java:10:29:10:74 | new String[] | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | -| Test.java:18:29:18:31 | cmd | Test.java:57:27:57:39 | args : String[] | Test.java:18:29:18:31 | cmd | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | -| Test.java:24:29:24:32 | cmd1 | Test.java:57:27:57:39 | args : String[] | Test.java:24:29:24:32 | cmd1 | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | -| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:25:29:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | +| Test.java:8:44:8:69 | ... + ... | Test.java:226:27:226:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:11:29:11:74 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:19:29:19:31 | cmd | Test.java:226:27:226:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:25:29:25:32 | cmd1 | Test.java:226:27:226:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:30:44:30:64 | ... + ... | Test.java:226:27:226:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | edges -| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... : String | provenance | | -| Test.java:6:35:6:44 | arg : String | Test.java:10:61:10:73 | ... + ... : String | provenance | | -| Test.java:6:35:6:44 | arg : String | Test.java:16:13:16:25 | ... + ... : String | provenance | | -| Test.java:6:35:6:44 | arg : String | Test.java:22:15:22:27 | ... + ... : String | provenance | | -| Test.java:7:44:7:69 | ... + ... : String | Test.java:7:25:7:70 | new ..[] { .. } | provenance | Sink:MaD:2 | -| Test.java:10:29:10:74 | {...} : String[] [[]] : String | Test.java:10:29:10:74 | new String[] | provenance | Sink:MaD:2 | -| Test.java:10:61:10:73 | ... + ... : String | Test.java:10:29:10:74 | {...} : String[] [[]] : String | provenance | | -| Test.java:16:5:16:7 | cmd [post update] : ArrayList [] : String | Test.java:18:29:18:31 | cmd | provenance | Sink:MaD:1 | -| Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : ArrayList [] : String | provenance | MaD:3 | -| Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | Test.java:24:29:24:32 | cmd1 | provenance | Sink:MaD:2 | -| Test.java:22:15:22:27 | ... + ... : String | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | provenance | | -| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... : String | provenance | | -| Test.java:29:44:29:64 | ... + ... : String | Test.java:29:25:29:65 | new ..[] { .. } | provenance | Sink:MaD:2 | -| Test.java:57:27:57:39 | args : String[] | Test.java:60:20:60:22 | arg : String | provenance | | -| Test.java:57:27:57:39 | args : String[] | Test.java:61:23:61:25 | arg : String | provenance | | -| Test.java:60:20:60:22 | arg : String | Test.java:6:35:6:44 | arg : String | provenance | | -| Test.java:61:23:61:25 | arg : String | Test.java:28:38:28:47 | arg : String | provenance | | +| Test.java:7:35:7:44 | arg : String | Test.java:8:44:8:69 | ... + ... : String | provenance | | +| Test.java:7:35:7:44 | arg : String | Test.java:11:61:11:73 | ... + ... : String | provenance | | +| Test.java:7:35:7:44 | arg : String | Test.java:17:13:17:25 | ... + ... : String | provenance | | +| Test.java:7:35:7:44 | arg : String | Test.java:23:15:23:27 | ... + ... : String | provenance | | +| Test.java:8:44:8:69 | ... + ... : String | Test.java:8:25:8:70 | new ..[] { .. } | provenance | Sink:MaD:2 | +| Test.java:11:29:11:74 | {...} : String[] [[]] : String | Test.java:11:29:11:74 | new String[] | provenance | Sink:MaD:2 | +| Test.java:11:61:11:73 | ... + ... : String | Test.java:11:29:11:74 | {...} : String[] [[]] : String | provenance | | +| Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | Test.java:19:29:19:31 | cmd | provenance | Sink:MaD:1 | +| Test.java:17:13:17:25 | ... + ... : String | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | provenance | MaD:3 | +| Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | Test.java:25:29:25:32 | cmd1 | provenance | Sink:MaD:2 | +| Test.java:23:15:23:27 | ... + ... : String | Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | provenance | | +| Test.java:29:38:29:47 | arg : String | Test.java:30:44:30:64 | ... + ... : String | provenance | | +| Test.java:30:44:30:64 | ... + ... : String | Test.java:30:25:30:65 | new ..[] { .. } | provenance | Sink:MaD:2 | +| Test.java:226:27:226:39 | args : String[] | Test.java:229:20:229:22 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:230:23:230:25 | arg : String | provenance | | +| Test.java:229:20:229:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | +| Test.java:230:23:230:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | models | 1 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (List); ; Argument[0]; command-injection; ai-manual | | 2 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (String[]); ; Argument[0]; command-injection; ai-manual | | 3 | Summary: java.util; Collection; true; add; ; ; Argument[0]; Argument[this].Element; value; manual | nodes -| Test.java:6:35:6:44 | arg : String | semmle.label | arg : String | -| Test.java:7:25:7:70 | new ..[] { .. } | semmle.label | new ..[] { .. } | -| Test.java:7:44:7:69 | ... + ... : String | semmle.label | ... + ... : String | -| Test.java:10:29:10:74 | new String[] | semmle.label | new String[] | -| Test.java:10:29:10:74 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | -| Test.java:10:61:10:73 | ... + ... : String | semmle.label | ... + ... : String | -| Test.java:16:5:16:7 | cmd [post update] : ArrayList [] : String | semmle.label | cmd [post update] : ArrayList [] : String | -| Test.java:16:13:16:25 | ... + ... : String | semmle.label | ... + ... : String | -| Test.java:18:29:18:31 | cmd | semmle.label | cmd | -| Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | semmle.label | cmd1 [post update] : String[] [[]] : String | -| Test.java:22:15:22:27 | ... + ... : String | semmle.label | ... + ... : String | -| Test.java:24:29:24:32 | cmd1 | semmle.label | cmd1 | -| Test.java:28:38:28:47 | arg : String | semmle.label | arg : String | -| Test.java:29:25:29:65 | new ..[] { .. } | semmle.label | new ..[] { .. } | -| Test.java:29:44:29:64 | ... + ... : String | semmle.label | ... + ... : String | -| Test.java:57:27:57:39 | args : String[] | semmle.label | args : String[] | -| Test.java:60:20:60:22 | arg : String | semmle.label | arg : String | -| Test.java:61:23:61:25 | arg : String | semmle.label | arg : String | +| Test.java:7:35:7:44 | arg : String | semmle.label | arg : String | +| Test.java:8:25:8:70 | new ..[] { .. } | semmle.label | new ..[] { .. } | +| Test.java:8:44:8:69 | ... + ... : String | semmle.label | ... + ... : String | +| Test.java:11:29:11:74 | new String[] | semmle.label | new String[] | +| Test.java:11:29:11:74 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:11:61:11:73 | ... + ... : String | semmle.label | ... + ... : String | +| Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | semmle.label | cmd [post update] : ArrayList [] : String | +| Test.java:17:13:17:25 | ... + ... : String | semmle.label | ... + ... : String | +| Test.java:19:29:19:31 | cmd | semmle.label | cmd | +| Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | semmle.label | cmd1 [post update] : String[] [[]] : String | +| Test.java:23:15:23:27 | ... + ... : String | semmle.label | ... + ... : String | +| Test.java:25:29:25:32 | cmd1 | semmle.label | cmd1 | +| Test.java:29:38:29:47 | arg : String | semmle.label | arg : String | +| Test.java:30:25:30:65 | new ..[] { .. } | semmle.label | new ..[] { .. } | +| Test.java:30:44:30:64 | ... + ... : String | semmle.label | ... + ... : String | +| Test.java:226:27:226:39 | args : String[] | semmle.label | args : String[] | +| Test.java:229:20:229:22 | arg : String | semmle.label | arg : String | +| Test.java:230:23:230:25 | arg : String | semmle.label | arg : String | subpaths diff --git a/java/ql/test/query-tests/security/CWE-078/ExecUnescaped.expected b/java/ql/test/query-tests/security/CWE-078/ExecUnescaped.expected index 5058f1bb26e9..d3e20e36fc98 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecUnescaped.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecUnescaped.expected @@ -1,2 +1,2 @@ -| Test.java:7:44:7:69 | ... + ... | Command line is built with string concatenation. | -| Test.java:29:44:29:64 | ... + ... | Command line is built with string concatenation. | +| Test.java:8:44:8:69 | ... + ... | Command line is built with string concatenation. | +| Test.java:30:44:30:64 | ... + ... | Command line is built with string concatenation. | diff --git a/java/ql/test/query-tests/security/CWE-078/Test.java b/java/ql/test/query-tests/security/CWE-078/Test.java index 6850a3a19e3b..853ad08cd515 100644 --- a/java/ql/test/query-tests/security/CWE-078/Test.java +++ b/java/ql/test/query-tests/security/CWE-078/Test.java @@ -1,6 +1,7 @@ import java.lang.ProcessBuilder; import java.util.List; import java.util.ArrayList; +import java.util.Arrays; class Test { public static void shellCommand(String arg) throws java.io.IOException { @@ -28,22 +29,190 @@ public static void shellCommand(String arg) throws java.io.IOException { public static void nonShellCommand(String arg) throws java.io.IOException { ProcessBuilder pb = new ProcessBuilder("./customTool " + arg); // $ Alert[java/concatenated-command-line] Alert[java/command-line-injection] pb.start(); + } - pb = new ProcessBuilder(new String[]{"./customTool", arg}); - pb.start(); + public static void nonShellCommandLiteralArguments(String arg) throws java.io.IOException { + new ProcessBuilder(new String[]{"./customTool", arg}).start(); // GOOD + } + public static void nonShellCommandListArguments(String arg) throws java.io.IOException { List cmd = new ArrayList(); cmd.add("./customTool"); cmd.add(arg); + new ProcessBuilder(cmd).start(); // GOOD + } - pb = new ProcessBuilder(cmd); - pb.start(); - + public static void nonShellCommandArrayArguments(String arg) throws java.io.IOException { String[] cmd1 = new String[]{"./customTool", ""}; cmd1[1] = arg; + new ProcessBuilder(cmd1).start(); // GOOD + } - pb = new ProcessBuilder(cmd1); - pb.start(); + public static void nonShellCommandWithIntermediateRead(String arg) throws java.io.IOException { + List readList = new ArrayList(); + readList.add("./customTool"); + readList.size(); + readList.add(arg); + new ProcessBuilder(readList).start(); // GOOD + } + + public static void nonShellCommandWithAddAll(String arg) throws java.io.IOException { + List addAllList = new ArrayList(); + addAllList.add("./customTool"); + addAllList.addAll(Arrays.asList("-v")); + addAllList.add(arg); + new ProcessBuilder(addAllList).start(); // GOOD + } + + public static void delegatingLauncherLiterals(String arg) throws java.io.IOException { + new ProcessBuilder(new String[]{"/usr/bin/env", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/sudo", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/xargs", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/sbin/chroot", "/tmp/root", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/nice", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/nohup", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/setsid", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/stdbuf", "-oL", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/timeout", "10", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/env", "/bin/sh", "-c", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static void fixedDelegatingLauncherLiteral(String arg) throws java.io.IOException { + new ProcessBuilder(new String[]{"/usr/bin/env", "/bin/cp", arg, "/tmp/destination"}).start(); // GOOD + } + + public static void fixedDelegatingLauncherLiteralWithPrefix(String arg) + throws java.io.IOException { + new ProcessBuilder(new String[]{"/usr/bin/timeout", "10", "/bin/cp", arg, "/tmp/destination"}).start(); // GOOD + } + + public static void delegatingLauncherListAlerts(String arg, boolean condition) + throws java.io.IOException { + List delegatedList = new ArrayList(); + delegatedList.add("/usr/bin/env"); + delegatedList.add(arg); + new ProcessBuilder(delegatedList).start(); // $ MISSING: Alert[java/command-line-injection] + + List branchingPosition = new ArrayList(); + branchingPosition.add("/usr/bin/timeout"); + if (condition) { + branchingPosition.add("10"); + } + branchingPosition.add(arg); + new ProcessBuilder(branchingPosition).start(); // $ MISSING: Alert[java/command-line-injection] + + List branchingLauncher = new ArrayList(); + if (condition) { + branchingLauncher.add("/usr/bin/env"); + } else { + branchingLauncher.add("/usr/bin/timeout"); + } + branchingLauncher.add("10"); + branchingLauncher.add(arg); + new ProcessBuilder(branchingLauncher).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static void fixedDelegatingLauncherLaterListArgument(String arg) + throws java.io.IOException { + List laterArgumentList = new ArrayList(); + laterArgumentList.add("/usr/bin/timeout"); + laterArgumentList.add("10"); + laterArgumentList.add("/bin/cp"); + laterArgumentList.add(arg); + laterArgumentList.add("/tmp/destination"); + new ProcessBuilder(laterArgumentList).start(); // GOOD + } + + public static void fixedDelegatingLauncherList(String arg) throws java.io.IOException { + List fixedList = new ArrayList(); + fixedList.add("/usr/bin/env"); + fixedList.add("/bin/cp"); + fixedList.add(arg); + fixedList.add("/tmp/destination"); + new ProcessBuilder(fixedList).start(); // GOOD + } + + public static void fixedDelegatingLauncherLoopList(String arg) throws java.io.IOException { + List loopList = new ArrayList(); + loopList.add("/usr/bin/env"); + loopList.add("/bin/cp"); + for (String value : Arrays.asList(arg)) { + loopList.add(value); + } + new ProcessBuilder(loopList).start(); // GOOD + } + + public static void delegatingLauncherArrayAlerts(String arg, boolean condition) + throws java.io.IOException { + String[] delegatedArray = new String[2]; + delegatedArray[0] = "/usr/bin/env"; + delegatedArray[1] = arg; + new ProcessBuilder(delegatedArray).start(); // $ MISSING: Alert[java/command-line-injection] + + String launcher; + if (condition) { + launcher = "/usr/bin/env"; + } else { + launcher = "/usr/bin/timeout"; + } + new ProcessBuilder(new String[]{launcher, "10", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static void fixedDelegatingLauncherArray(String arg) throws java.io.IOException { + String[] fixedArray = new String[]{"/usr/bin/env", "/bin/cp", "", "/tmp/destination"}; + fixedArray[2] = arg; + new ProcessBuilder(fixedArray).start(); // GOOD + } + + public static void changedDelegatedCommand(String arg) throws java.io.IOException { + String[] command = new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}; + command[1] = "/bin/sh"; + new ProcessBuilder(command).start(); // $ MISSING: Alert[java/command-line-injection] + + String[] copiedInline = + Arrays.copyOf(new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}, 4); + copiedInline[1] = "/bin/sh"; + new ProcessBuilder(copiedInline).start(); // $ MISSING: Alert[java/command-line-injection] + + String[] base = new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}; + String[] copiedStored = Arrays.copyOf(base, 4); + copiedStored[1] = "/bin/sh"; + new ProcessBuilder(copiedStored).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static String[] returnedNonShellCommand(String arg) { + return new String[]{"./customTool", arg}; + } + + public static void nestedNonShellCommand(String arg) throws java.io.IOException { + new ProcessBuilder( + arg.isEmpty() + ? new String[]{"./customTool", arg} + : new String[]{"./otherCustomTool", arg}) + .start(); // GOOD + } + + public static void useReturnedNonShellCommand(String arg) throws java.io.IOException { + new ProcessBuilder(returnedNonShellCommand(arg)).start(); // GOOD + } + + public static void unknownDelegatedCommand(String arg, String command) throws java.io.IOException { + new ProcessBuilder(new String[]{"/usr/bin/env", command, "-c", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static void unsupportedEnvAssignment(String arg) throws java.io.IOException { + new ProcessBuilder(new String[]{"/usr/bin/env", "LANG=C", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static void unsupportedSudoOption(String arg) throws java.io.IOException { + new ProcessBuilder(new String[]{"/usr/bin/sudo", "-u", "nobody", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + } + + public static void changedDelegatedCommandThroughAlias(String arg) throws java.io.IOException { + String[] command = new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}; + String[] alias = command; + alias[1] = "/bin/sh"; + new ProcessBuilder(command).start(); // $ MISSING: Alert[java/command-line-injection] } public static void relativeCommand() throws java.io.IOException { @@ -59,6 +228,27 @@ public static void main(String[] args) throws java.io.IOException { // $ Source[ shellCommand(arg); nonShellCommand(arg); + nonShellCommandLiteralArguments(arg); + nonShellCommandListArguments(arg); + nonShellCommandArrayArguments(arg); + nonShellCommandWithIntermediateRead(arg); + nonShellCommandWithAddAll(arg); + delegatingLauncherLiterals(arg); + fixedDelegatingLauncherLiteral(arg); + fixedDelegatingLauncherLiteralWithPrefix(arg); + delegatingLauncherListAlerts(arg, args.length > 2); + fixedDelegatingLauncherLaterListArgument(arg); + fixedDelegatingLauncherList(arg); + fixedDelegatingLauncherLoopList(arg); + delegatingLauncherArrayAlerts(arg, args.length > 2); + fixedDelegatingLauncherArray(arg); + changedDelegatedCommand(arg); + nestedNonShellCommand(arg); + useReturnedNonShellCommand(arg); + unknownDelegatedCommand(arg, "/bin/sh"); + unsupportedEnvAssignment(arg); + unsupportedSudoOption(arg); + changedDelegatedCommandThroughAlias(arg); relativeCommand(); } } From 8f59bbb81e39759d164bfcbb7774b6951009f53c Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 16 Oct 2026 14:57:41 +0100 Subject: [PATCH 2/5] Detect commands passed through simple launchers Recognize fixed command positions without parsing launcher options. Only sanitize later arguments when the delegated command is one unchanged string literal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../2026-10-16-delegating-launchers.md | 4 + java/ql/lib/semmle/code/java/JDK.qll | 14 +- .../code/java/security/CommandArguments.qll | 225 +++++++++++++++--- .../security/CWE-078/ExecTainted.expected | 177 +++++++++++++- .../query-tests/security/CWE-078/Test.java | 40 ++-- 5 files changed, 394 insertions(+), 66 deletions(-) create mode 100644 java/ql/lib/change-notes/2026-10-16-delegating-launchers.md diff --git a/java/ql/lib/change-notes/2026-10-16-delegating-launchers.md b/java/ql/lib/change-notes/2026-10-16-delegating-launchers.md new file mode 100644 index 000000000000..738b2226c8bd --- /dev/null +++ b/java/ql/lib/change-notes/2026-10-16-delegating-launchers.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `java/command-line-injection` query now detects command injection through simple invocations of delegating launchers such as `env`, `sudo`, and `timeout`. diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 93562c7c3d80..665623aa3206 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -104,6 +104,14 @@ class StringPartialMatchMethod extends Method { } } +/** The type `String[]`. */ +class ArrayOfStringType extends Array { + ArrayOfStringType() { + this.getDimension() = 1 and + this.getElementType() instanceof TypeString + } +} + /** The class `java.lang.StringBuffer`. */ class TypeStringBuffer extends Class { TypeStringBuffer() { this.hasQualifiedName("java.lang", "StringBuffer") } @@ -444,11 +452,7 @@ class MainMethod extends Method { this.getReturnType().hasName("void") and this.hasName("main") and this.getNumberOfParameters() = 1 and - exists(Array a | - a = this.getAParameter().getType() and - a.getDimension() = 1 and - a.getElementType() instanceof TypeString - ) + this.getAParameter().getType() instanceof ArrayOfStringType } } diff --git a/java/ql/lib/semmle/code/java/security/CommandArguments.qll b/java/ql/lib/semmle/code/java/security/CommandArguments.qll index 1ac42192e040..eb96c964a8e9 100644 --- a/java/ql/lib/semmle/code/java/security/CommandArguments.qll +++ b/java/ql/lib/semmle/code/java/security/CommandArguments.qll @@ -10,46 +10,145 @@ import semmle.code.java.Collections /** * Holds if `ex` is used safely as an argument to a command; - * i.e. it's not in the first position and it's not a shell command. + * i.e. it's not in a position that selects a command to execute. */ predicate isSafeCommandArgument(Expr ex) { - exists(ArrayInit ai, int i | - ex = ai.getInit(i) and - i > 0 and - not isShell(ai.getInit(0)) + exists(ArrayInit init, int index | + ex = init.getInit(index) and + index > 0 and + isNonDelegatingCommand(init.getInit(0)) ) or - exists(CommandArgumentList cal | - not cal.isShell() and - ex = cal.getASubsequentAdd().getArgument(0) + exists(CommandArgumentList list | + not list.isShell() and + not list.isDelegatingLauncher() and + ex = list.getASubsequentAdd().getArgument(0) ) or - exists(CommandArgArrayImmutableFirst caa | - not caa.isShell() and - ex = caa.getAWrite(any(int i | i > 0)) + exists(CommandArgArrayImmutableFirst array | + not array.isShell() and + not array.isDelegatingLauncher() and + ex = array.getAWrite(any(int index | index > 0)) + ) + or + forex(CommandArguments arguments, int index | ex = arguments.getArgument(index) | + index > 0 and arguments.hasSafeDelegatedArgument(index) ) } -/** - * Holds if the given expression is the name of a shell command such as bash or python - */ -private predicate isShell(Expr ex) { - exists(string cmd | cmd = ex.(StringLiteral).getValue() | - cmd.regexpMatch(".*(sh|javac?|python[23]?|osascript|cmd)(\\.exe)?$") - ) +private string commandName(Expr ex) { + result = ex.(StringLiteral).getValue() or exists(SsaDefinition ssa, SsaExplicitWrite def | ex = ssa.getARead() and - isShell(def.getDefiningExpr()) + result = commandName(def.getDefiningExpr()) | ssa.getAnUltimateDefinition() = def or ssa.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def ) or - isShell(ex.(Assignment).getRhs()) + result = commandName(ex.(Assignment).getRhs()) or - isShell(ex.(LocalVariableDeclExpr).getInit()) + result = commandName(ex.(LocalVariableDeclExpr).getInit()) +} + +/** + * Holds if the given expression is the name of a shell command such as bash or python + */ +private predicate isShell(Expr ex) { + commandName(ex).regexpMatch(".*(sh|javac?|python[23]?|osascript|cmd)(\\.exe)?$") +} + +/** + * Gets the fixed position of the command executed by a supported delegating launcher. + * + * Only simple invocation forms with a command at the expected position are recognized. Launcher + * options and environment assignments are intentionally not interpreted. + */ +private int delegatedCommandIndex(Expr ex) { + exists(string launcher | + launcher = commandName(ex).regexpCapture("(.*/)?([^/.]+)(\\.exe)?$", 2) and + ( + launcher = ["env", "nice", "nohup", "setsid", "sudo", "xargs"] and result = 1 + or + launcher = ["chroot", "stdbuf", "timeout"] and result = 2 + ) + ) +} + +private predicate isNonDelegatingCommand(Expr ex) { + exists(commandName(ex)) and + not isShell(ex) and + not exists(delegatedCommandIndex(ex)) +} + +/** Holds if `init` initializes an array passed directly to a call. */ +private predicate isInlineArgumentArray(ArrayInit init) { + exists(ArrayCreationExpr array, Call call | + array.getInit() = init and + array = call.getAnArgument() and + not exists(MethodCall methodCall | + call = methodCall and + methodCall.getMethod().getDeclaringType().hasQualifiedName("java.util", "Arrays") and + methodCall.getMethod().hasName("copyOf") + ) + ) +} + +private newtype TCommandArguments = + TLiteralArguments(ArrayInit init) { isInlineArgumentArray(init) } or + TListArguments(CommandArgumentList list) or + TMutableArguments(CommandArgArrayImmutableFirst array) + +/** An ordered sequence of command arguments. */ +private class CommandArguments extends TCommandArguments { + string toString() { result = this.getArgument(0).toString() } + + /** Gets a possible value at the given index. */ + Expr getArgument(int index) { + exists(ArrayInit init | + this = TLiteralArguments(init) and + result = init.getInit(index) + ) + or + exists(CommandArgumentList list | + this = TListArguments(list) and + ( + result = list.getArgument(index) + or + index = 3 and result = list.getAnArgumentAfterKnownPrefix().getArgument(0) + ) + ) + or + exists(CommandArgArrayImmutableFirst array | + this = TMutableArguments(array) and + result = array.getAnElement(index) + ) + } + + /** Gets the single string literal stored at the given index, if there is one. */ + private StringLiteral getFixedLiteralArgument(int index) { + forex(Expr value | value = this.getArgument(index) | value = result) + } + + predicate hasSafeDelegatedArgument(int index) { + exists(this.getArgument(index)) and + forex(Expr launcher | launcher = this.getArgument(0) | + forex(int commandIndex | commandIndex = delegatedCommandIndex(launcher) | + ( + index < commandIndex + or + index > commandIndex and + exists(StringLiteral command | + command = this.getFixedLiteralArgument(commandIndex) and + not isShell(command) and + not exists(delegatedCommandIndex(command)) + ) + ) + ) + ) + } } /** @@ -101,6 +200,39 @@ private class CommandArgumentList extends SsaExplicitWrite { result.getQualifier() = this.getAUseBeforeFirstAdd() } + /** Gets a single-element addition at the given index. */ + private MethodCall getAnAddAtIndex(int index) { + index = 0 and + result = this.getAFirstAdd() and + result.getMethod().hasName("add") and + result.getNumArgument() = 1 + or + index > 0 and + index <= 2 and + exists(MethodCall previous | + previous = this.getAnAddAtIndex(index - 1) and + result = this.getAnAdd() and + result.getMethod().hasName("add") and + result.getNumArgument() = 1 and + adjacentUseUse(previous.getQualifier(), result.getQualifier()) + ) + } + + /** Gets a single-element addition after the first three elements. */ + MethodCall getAnArgumentAfterKnownPrefix() { + result = this.getAnAdd() and + result.getMethod().hasName("add") and + result.getNumArgument() = 1 and + exists(MethodCall previous | + previous = this.getAnAddAtIndex(2) or previous = this.getAnArgumentAfterKnownPrefix() + | + adjacentUseUse(previous.getQualifier(), result.getQualifier()) + ) + } + + /** Gets the argument added at the given index. */ + Expr getArgument(int index) { result = this.getAnAddAtIndex(index).getArgument(0) } + /** Gets an addition to this list which is not the first element. */ MethodCall getASubsequentAdd() { result = this.getAnAdd() and @@ -109,15 +241,15 @@ private class CommandArgumentList extends SsaExplicitWrite { /** Holds if the first element of this list is a shell command. */ predicate isShell() { - exists(MethodCall ma | ma = this.getAFirstAdd() and isShell(ma.getArgument(0))) + exists(MethodCall add | add = this.getAFirstAdd() and isShell(add.getArgument(0))) } -} -/** - * The type `String[]`. - */ -private class ArrayOfStringType extends Array { - ArrayOfStringType() { this.getElementType() instanceof TypeString } + /** Holds if the first element of this list is a delegating launcher. */ + predicate isDelegatingLauncher() { + exists(MethodCall add | add = this.getAFirstAdd() | + exists(delegatedCommandIndex(add.getArgument(0))) + ) + } } private predicate arrayVarWrite(ArrayAccess acc) { exists(Assignment a | a.getDest() = acc) } @@ -153,7 +285,7 @@ private class CommandArgumentArray extends SsaExplicitWrite { */ private class CommandArgArrayImmutableFirst extends CommandArgumentArray { CommandArgArrayImmutableFirst() { - (exists(this.getAWrite(0)) or exists(firstElementOf(this.getDefiningExpr()))) and + (exists(this.getAWrite(0)) or exists(arrayElementOf(this.getDefiningExpr(), 0))) and forall(VarRead use | exists(this.getAWrite(0, use)) | use = ssaGetAFirstUse(this)) } @@ -162,23 +294,38 @@ private class CommandArgArrayImmutableFirst extends CommandArgumentArray { result = this.getAWrite(0) or not exists(this.getAWrite(0)) and - result = firstElementOf(this.getDefiningExpr()) + result = arrayElementOf(this.getDefiningExpr(), 0) } /** Holds if the first element of this array is a shell command. */ predicate isShell() { isShell(this.getFirstElement()) } + + /** Holds if the first element of this array is a delegating launcher. */ + predicate isDelegatingLauncher() { exists(delegatedCommandIndex(this.getFirstElement())) } + + /** Gets a possible value for the element at the given index. */ + Expr getAnElement(int index) { + index = 0 and result = this.getFirstElement() + or + index > 0 and + ( + result = this.getAWrite(index) + or + result = arrayElementOf(this.getDefiningExpr(), index) + ) + } } -/** Gets the first element of an imutable array of strings */ -private Expr firstElementOf(Expr arr) { +/** Gets an element of an immutable array of strings. */ +private Expr arrayElementOf(Expr arr, int index) { arr.getType() instanceof ArrayOfStringType and ( - result = firstElementOf(arr.(Assignment).getRhs()) + result = arrayElementOf(arr.(Assignment).getRhs(), index) or - result = firstElementOf(arr.(LocalVariableDeclExpr).getInit()) + result = arrayElementOf(arr.(LocalVariableDeclExpr).getInit(), index) or exists(CommandArgArrayImmutableFirst caa | arr = caa.getARead() | - result = caa.getFirstElement() + result = caa.getAnElement(index) ) or exists(MethodCall ma, Method m | @@ -186,17 +333,17 @@ private Expr firstElementOf(Expr arr) { ma.getMethod() = m and m.getDeclaringType().hasQualifiedName("java.util", "Arrays") and m.hasName("copyOf") and - result = firstElementOf(ma.getArgument(0)) + result = arrayElementOf(ma.getArgument(0), index) ) or exists(Field f | f.isStatic() and arr.(FieldRead).getField() = f and - result = firstElementOf(f.getInitializer()) + result = arrayElementOf(f.getInitializer(), index) ) or - result = arr.(ArrayInit).getInit(0) + result = arr.(ArrayInit).getInit(index) or - result = arr.(ArrayCreationExpr).getInit().getInit(0) + result = arr.(ArrayCreationExpr).getInit().getInit(index) ) } diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected index fd2dd9d59447..b7a288cefdf2 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected @@ -4,6 +4,26 @@ | Test.java:19:29:19:31 | cmd | Test.java:226:27:226:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | | Test.java:25:29:25:32 | cmd1 | Test.java:226:27:226:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | | Test.java:30:44:30:64 | ... + ... | Test.java:226:27:226:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:68:24:68:56 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:69:24:69:57 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:70:24:70:58 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:71:24:71:73 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:72:24:72:57 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:73:24:73:58 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:74:24:74:59 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:75:24:75:66 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:76:24:76:66 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:77:24:77:73 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:94:24:94:36 | delegatedList | Test.java:226:27:226:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:102:24:102:40 | branchingPosition | Test.java:226:27:226:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:112:24:112:40 | branchingLauncher | Test.java:226:27:226:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:150:24:150:37 | delegatedArray | Test.java:226:27:226:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:158:24:158:56 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:170:24:170:30 | command | Test.java:226:27:226:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:175:24:175:35 | copiedInline | Test.java:226:27:226:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:180:24:180:35 | copiedStored | Test.java:226:27:226:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:200:24:200:71 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:215:24:215:30 | command | Test.java:226:27:226:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | edges | Test.java:7:35:7:44 | arg : String | Test.java:8:44:8:69 | ... + ... : String | provenance | | | Test.java:7:35:7:44 | arg : String | Test.java:11:61:11:73 | ... + ... : String | provenance | | @@ -13,19 +33,96 @@ edges | Test.java:11:29:11:74 | {...} : String[] [[]] : String | Test.java:11:29:11:74 | new String[] | provenance | Sink:MaD:2 | | Test.java:11:61:11:73 | ... + ... : String | Test.java:11:29:11:74 | {...} : String[] [[]] : String | provenance | | | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | Test.java:19:29:19:31 | cmd | provenance | Sink:MaD:1 | -| Test.java:17:13:17:25 | ... + ... : String | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | provenance | MaD:3 | +| Test.java:17:13:17:25 | ... + ... : String | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | provenance | MaD:4 | | Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | Test.java:25:29:25:32 | cmd1 | provenance | Sink:MaD:2 | | Test.java:23:15:23:27 | ... + ... : String | Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | provenance | | | Test.java:29:38:29:47 | arg : String | Test.java:30:44:30:64 | ... + ... : String | provenance | | | Test.java:30:44:30:64 | ... + ... : String | Test.java:30:25:30:65 | new ..[] { .. } | provenance | Sink:MaD:2 | +| Test.java:67:49:67:58 | arg : String | Test.java:68:53:68:55 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:69:54:69:56 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:70:55:70:57 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:71:70:71:72 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:72:54:72:56 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:73:55:73:57 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:74:56:74:58 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:75:63:75:65 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:76:63:76:65 | arg : String | provenance | | +| Test.java:67:49:67:58 | arg : String | Test.java:77:70:77:72 | arg : String | provenance | | +| Test.java:68:24:68:56 | {...} : String[] [[]] : String | Test.java:68:24:68:56 | new String[] | provenance | Sink:MaD:2 | +| Test.java:68:53:68:55 | arg : String | Test.java:68:24:68:56 | {...} : String[] [[]] : String | provenance | | +| Test.java:69:24:69:57 | {...} : String[] [[]] : String | Test.java:69:24:69:57 | new String[] | provenance | Sink:MaD:2 | +| Test.java:69:54:69:56 | arg : String | Test.java:69:24:69:57 | {...} : String[] [[]] : String | provenance | | +| Test.java:70:24:70:58 | {...} : String[] [[]] : String | Test.java:70:24:70:58 | new String[] | provenance | Sink:MaD:2 | +| Test.java:70:55:70:57 | arg : String | Test.java:70:24:70:58 | {...} : String[] [[]] : String | provenance | | +| Test.java:71:24:71:73 | {...} : String[] [[]] : String | Test.java:71:24:71:73 | new String[] | provenance | Sink:MaD:2 | +| Test.java:71:70:71:72 | arg : String | Test.java:71:24:71:73 | {...} : String[] [[]] : String | provenance | | +| Test.java:72:24:72:57 | {...} : String[] [[]] : String | Test.java:72:24:72:57 | new String[] | provenance | Sink:MaD:2 | +| Test.java:72:54:72:56 | arg : String | Test.java:72:24:72:57 | {...} : String[] [[]] : String | provenance | | +| Test.java:73:24:73:58 | {...} : String[] [[]] : String | Test.java:73:24:73:58 | new String[] | provenance | Sink:MaD:2 | +| Test.java:73:55:73:57 | arg : String | Test.java:73:24:73:58 | {...} : String[] [[]] : String | provenance | | +| Test.java:74:24:74:59 | {...} : String[] [[]] : String | Test.java:74:24:74:59 | new String[] | provenance | Sink:MaD:2 | +| Test.java:74:56:74:58 | arg : String | Test.java:74:24:74:59 | {...} : String[] [[]] : String | provenance | | +| Test.java:75:24:75:66 | {...} : String[] [[]] : String | Test.java:75:24:75:66 | new String[] | provenance | Sink:MaD:2 | +| Test.java:75:63:75:65 | arg : String | Test.java:75:24:75:66 | {...} : String[] [[]] : String | provenance | | +| Test.java:76:24:76:66 | {...} : String[] [[]] : String | Test.java:76:24:76:66 | new String[] | provenance | Sink:MaD:2 | +| Test.java:76:63:76:65 | arg : String | Test.java:76:24:76:66 | {...} : String[] [[]] : String | provenance | | +| Test.java:77:24:77:73 | {...} : String[] [[]] : String | Test.java:77:24:77:73 | new String[] | provenance | Sink:MaD:2 | +| Test.java:77:70:77:72 | arg : String | Test.java:77:24:77:73 | {...} : String[] [[]] : String | provenance | | +| Test.java:89:51:89:60 | arg : String | Test.java:93:23:93:25 | arg : String | provenance | | +| Test.java:89:51:89:60 | arg : String | Test.java:101:27:101:29 | arg : String | provenance | | +| Test.java:89:51:89:60 | arg : String | Test.java:111:27:111:29 | arg : String | provenance | | +| Test.java:93:5:93:17 | delegatedList [post update] : ArrayList [] : String | Test.java:94:24:94:36 | delegatedList | provenance | Sink:MaD:1 | +| Test.java:93:23:93:25 | arg : String | Test.java:93:5:93:17 | delegatedList [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:101:5:101:21 | branchingPosition [post update] : ArrayList [] : String | Test.java:102:24:102:40 | branchingPosition | provenance | Sink:MaD:1 | +| Test.java:101:27:101:29 | arg : String | Test.java:101:5:101:21 | branchingPosition [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:111:5:111:21 | branchingLauncher [post update] : ArrayList [] : String | Test.java:112:24:112:40 | branchingLauncher | provenance | Sink:MaD:1 | +| Test.java:111:27:111:29 | arg : String | Test.java:111:5:111:21 | branchingLauncher [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:145:52:145:61 | arg : String | Test.java:149:25:149:27 | arg : String | provenance | | +| Test.java:145:52:145:61 | arg : String | Test.java:158:53:158:55 | arg : String | provenance | | +| Test.java:149:5:149:18 | delegatedArray [post update] : String[] [[]] : String | Test.java:150:24:150:37 | delegatedArray | provenance | Sink:MaD:2 | +| Test.java:149:25:149:27 | arg : String | Test.java:149:5:149:18 | delegatedArray [post update] : String[] [[]] : String | provenance | | +| Test.java:158:24:158:56 | {...} : String[] [[]] : String | Test.java:158:24:158:56 | new String[] | provenance | Sink:MaD:2 | +| Test.java:158:53:158:55 | arg : String | Test.java:158:24:158:56 | {...} : String[] [[]] : String | provenance | | +| Test.java:167:46:167:55 | arg : String | Test.java:168:70:168:72 | arg : String | provenance | | +| Test.java:167:46:167:55 | arg : String | Test.java:173:69:173:71 | arg : String | provenance | | +| Test.java:167:46:167:55 | arg : String | Test.java:177:67:177:69 | arg : String | provenance | | +| Test.java:168:24:168:73 | {...} : String[] [[]] : String | Test.java:170:24:170:30 | command | provenance | Sink:MaD:2 | +| Test.java:168:70:168:72 | arg : String | Test.java:168:24:168:73 | {...} : String[] [[]] : String | provenance | | +| Test.java:173:9:173:76 | copyOf(...) : String[] [[]] : String | Test.java:175:24:175:35 | copiedInline | provenance | Sink:MaD:2 | +| Test.java:173:23:173:72 | new String[] : String[] [[]] : String | Test.java:173:9:173:76 | copyOf(...) : String[] [[]] : String | provenance | MaD:3 | +| Test.java:173:23:173:72 | {...} : String[] [[]] : String | Test.java:173:23:173:72 | new String[] : String[] [[]] : String | provenance | | +| Test.java:173:69:173:71 | arg : String | Test.java:173:23:173:72 | {...} : String[] [[]] : String | provenance | | +| Test.java:177:21:177:70 | {...} : String[] [[]] : String | Test.java:178:43:178:46 | base : String[] [[]] : String | provenance | | +| Test.java:177:67:177:69 | arg : String | Test.java:177:21:177:70 | {...} : String[] [[]] : String | provenance | | +| Test.java:178:29:178:50 | copyOf(...) : String[] [[]] : String | Test.java:180:24:180:35 | copiedStored | provenance | Sink:MaD:2 | +| Test.java:178:43:178:46 | base : String[] [[]] : String | Test.java:178:29:178:50 | copyOf(...) : String[] [[]] : String | provenance | MaD:3 | +| Test.java:199:46:199:55 | arg : String | Test.java:200:68:200:70 | arg : String | provenance | | +| Test.java:200:24:200:71 | {...} : String[] [[]] : String | Test.java:200:24:200:71 | new String[] | provenance | Sink:MaD:2 | +| Test.java:200:68:200:70 | arg : String | Test.java:200:24:200:71 | {...} : String[] [[]] : String | provenance | | +| Test.java:211:58:211:67 | arg : String | Test.java:212:70:212:72 | arg : String | provenance | | +| Test.java:212:24:212:73 | {...} : String[] [[]] : String | Test.java:215:24:215:30 | command | provenance | Sink:MaD:2 | +| Test.java:212:70:212:72 | arg : String | Test.java:212:24:212:73 | {...} : String[] [[]] : String | provenance | | | Test.java:226:27:226:39 | args : String[] | Test.java:229:20:229:22 | arg : String | provenance | | | Test.java:226:27:226:39 | args : String[] | Test.java:230:23:230:25 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:236:34:236:36 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:239:36:239:38 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:243:37:243:39 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:245:31:245:33 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:248:31:248:33 | arg : String | provenance | | +| Test.java:226:27:226:39 | args : String[] | Test.java:251:43:251:45 | arg : String | provenance | | | Test.java:229:20:229:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | | Test.java:230:23:230:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | +| Test.java:236:34:236:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | +| Test.java:239:36:239:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | +| Test.java:243:37:243:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | +| Test.java:245:31:245:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | +| Test.java:248:31:248:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | +| Test.java:251:43:251:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | models | 1 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (List); ; Argument[0]; command-injection; ai-manual | | 2 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (String[]); ; Argument[0]; command-injection; ai-manual | -| 3 | Summary: java.util; Collection; true; add; ; ; Argument[0]; Argument[this].Element; value; manual | +| 3 | Summary: java.util; Arrays; false; copyOf; ; ; Argument[0].ArrayElement; ReturnValue.ArrayElement; value; manual | +| 4 | Summary: java.util; Collection; true; add; ; ; Argument[0]; Argument[this].Element; value; manual | nodes | Test.java:7:35:7:44 | arg : String | semmle.label | arg : String | | Test.java:8:25:8:70 | new ..[] { .. } | semmle.label | new ..[] { .. } | @@ -42,7 +139,83 @@ nodes | Test.java:29:38:29:47 | arg : String | semmle.label | arg : String | | Test.java:30:25:30:65 | new ..[] { .. } | semmle.label | new ..[] { .. } | | Test.java:30:44:30:64 | ... + ... : String | semmle.label | ... + ... : String | +| Test.java:67:49:67:58 | arg : String | semmle.label | arg : String | +| Test.java:68:24:68:56 | new String[] | semmle.label | new String[] | +| Test.java:68:24:68:56 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:68:53:68:55 | arg : String | semmle.label | arg : String | +| Test.java:69:24:69:57 | new String[] | semmle.label | new String[] | +| Test.java:69:24:69:57 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:69:54:69:56 | arg : String | semmle.label | arg : String | +| Test.java:70:24:70:58 | new String[] | semmle.label | new String[] | +| Test.java:70:24:70:58 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:70:55:70:57 | arg : String | semmle.label | arg : String | +| Test.java:71:24:71:73 | new String[] | semmle.label | new String[] | +| Test.java:71:24:71:73 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:71:70:71:72 | arg : String | semmle.label | arg : String | +| Test.java:72:24:72:57 | new String[] | semmle.label | new String[] | +| Test.java:72:24:72:57 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:72:54:72:56 | arg : String | semmle.label | arg : String | +| Test.java:73:24:73:58 | new String[] | semmle.label | new String[] | +| Test.java:73:24:73:58 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:73:55:73:57 | arg : String | semmle.label | arg : String | +| Test.java:74:24:74:59 | new String[] | semmle.label | new String[] | +| Test.java:74:24:74:59 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:74:56:74:58 | arg : String | semmle.label | arg : String | +| Test.java:75:24:75:66 | new String[] | semmle.label | new String[] | +| Test.java:75:24:75:66 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:75:63:75:65 | arg : String | semmle.label | arg : String | +| Test.java:76:24:76:66 | new String[] | semmle.label | new String[] | +| Test.java:76:24:76:66 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:76:63:76:65 | arg : String | semmle.label | arg : String | +| Test.java:77:24:77:73 | new String[] | semmle.label | new String[] | +| Test.java:77:24:77:73 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:77:70:77:72 | arg : String | semmle.label | arg : String | +| Test.java:89:51:89:60 | arg : String | semmle.label | arg : String | +| Test.java:93:5:93:17 | delegatedList [post update] : ArrayList [] : String | semmle.label | delegatedList [post update] : ArrayList [] : String | +| Test.java:93:23:93:25 | arg : String | semmle.label | arg : String | +| Test.java:94:24:94:36 | delegatedList | semmle.label | delegatedList | +| Test.java:101:5:101:21 | branchingPosition [post update] : ArrayList [] : String | semmle.label | branchingPosition [post update] : ArrayList [] : String | +| Test.java:101:27:101:29 | arg : String | semmle.label | arg : String | +| Test.java:102:24:102:40 | branchingPosition | semmle.label | branchingPosition | +| Test.java:111:5:111:21 | branchingLauncher [post update] : ArrayList [] : String | semmle.label | branchingLauncher [post update] : ArrayList [] : String | +| Test.java:111:27:111:29 | arg : String | semmle.label | arg : String | +| Test.java:112:24:112:40 | branchingLauncher | semmle.label | branchingLauncher | +| Test.java:145:52:145:61 | arg : String | semmle.label | arg : String | +| Test.java:149:5:149:18 | delegatedArray [post update] : String[] [[]] : String | semmle.label | delegatedArray [post update] : String[] [[]] : String | +| Test.java:149:25:149:27 | arg : String | semmle.label | arg : String | +| Test.java:150:24:150:37 | delegatedArray | semmle.label | delegatedArray | +| Test.java:158:24:158:56 | new String[] | semmle.label | new String[] | +| Test.java:158:24:158:56 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:158:53:158:55 | arg : String | semmle.label | arg : String | +| Test.java:167:46:167:55 | arg : String | semmle.label | arg : String | +| Test.java:168:24:168:73 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:168:70:168:72 | arg : String | semmle.label | arg : String | +| Test.java:170:24:170:30 | command | semmle.label | command | +| Test.java:173:9:173:76 | copyOf(...) : String[] [[]] : String | semmle.label | copyOf(...) : String[] [[]] : String | +| Test.java:173:23:173:72 | new String[] : String[] [[]] : String | semmle.label | new String[] : String[] [[]] : String | +| Test.java:173:23:173:72 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:173:69:173:71 | arg : String | semmle.label | arg : String | +| Test.java:175:24:175:35 | copiedInline | semmle.label | copiedInline | +| Test.java:177:21:177:70 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:177:67:177:69 | arg : String | semmle.label | arg : String | +| Test.java:178:29:178:50 | copyOf(...) : String[] [[]] : String | semmle.label | copyOf(...) : String[] [[]] : String | +| Test.java:178:43:178:46 | base : String[] [[]] : String | semmle.label | base : String[] [[]] : String | +| Test.java:180:24:180:35 | copiedStored | semmle.label | copiedStored | +| Test.java:199:46:199:55 | arg : String | semmle.label | arg : String | +| Test.java:200:24:200:71 | new String[] | semmle.label | new String[] | +| Test.java:200:24:200:71 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:200:68:200:70 | arg : String | semmle.label | arg : String | +| Test.java:211:58:211:67 | arg : String | semmle.label | arg : String | +| Test.java:212:24:212:73 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:212:70:212:72 | arg : String | semmle.label | arg : String | +| Test.java:215:24:215:30 | command | semmle.label | command | | Test.java:226:27:226:39 | args : String[] | semmle.label | args : String[] | | Test.java:229:20:229:22 | arg : String | semmle.label | arg : String | | Test.java:230:23:230:25 | arg : String | semmle.label | arg : String | +| Test.java:236:34:236:36 | arg : String | semmle.label | arg : String | +| Test.java:239:36:239:38 | arg : String | semmle.label | arg : String | +| Test.java:243:37:243:39 | arg : String | semmle.label | arg : String | +| Test.java:245:31:245:33 | arg : String | semmle.label | arg : String | +| Test.java:248:31:248:33 | arg : String | semmle.label | arg : String | +| Test.java:251:43:251:45 | arg : String | semmle.label | arg : String | subpaths diff --git a/java/ql/test/query-tests/security/CWE-078/Test.java b/java/ql/test/query-tests/security/CWE-078/Test.java index 853ad08cd515..fc4a409983ac 100644 --- a/java/ql/test/query-tests/security/CWE-078/Test.java +++ b/java/ql/test/query-tests/security/CWE-078/Test.java @@ -65,16 +65,16 @@ public static void nonShellCommandWithAddAll(String arg) throws java.io.IOExcept } public static void delegatingLauncherLiterals(String arg) throws java.io.IOException { - new ProcessBuilder(new String[]{"/usr/bin/env", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/sudo", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/xargs", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/sbin/chroot", "/tmp/root", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/nice", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/nohup", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/setsid", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/stdbuf", "-oL", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/timeout", "10", arg}).start(); // $ MISSING: Alert[java/command-line-injection] - new ProcessBuilder(new String[]{"/usr/bin/env", "/bin/sh", "-c", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/env", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/sudo", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/xargs", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/sbin/chroot", "/tmp/root", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/nice", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/nohup", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/setsid", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/stdbuf", "-oL", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/timeout", "10", arg}).start(); // $ Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/env", "/bin/sh", "-c", arg}).start(); // $ Alert[java/command-line-injection] } public static void fixedDelegatingLauncherLiteral(String arg) throws java.io.IOException { @@ -91,7 +91,7 @@ public static void delegatingLauncherListAlerts(String arg, boolean condition) List delegatedList = new ArrayList(); delegatedList.add("/usr/bin/env"); delegatedList.add(arg); - new ProcessBuilder(delegatedList).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(delegatedList).start(); // $ Alert[java/command-line-injection] List branchingPosition = new ArrayList(); branchingPosition.add("/usr/bin/timeout"); @@ -99,7 +99,7 @@ public static void delegatingLauncherListAlerts(String arg, boolean condition) branchingPosition.add("10"); } branchingPosition.add(arg); - new ProcessBuilder(branchingPosition).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(branchingPosition).start(); // $ Alert[java/command-line-injection] List branchingLauncher = new ArrayList(); if (condition) { @@ -109,7 +109,7 @@ public static void delegatingLauncherListAlerts(String arg, boolean condition) } branchingLauncher.add("10"); branchingLauncher.add(arg); - new ProcessBuilder(branchingLauncher).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(branchingLauncher).start(); // $ Alert[java/command-line-injection] } public static void fixedDelegatingLauncherLaterListArgument(String arg) @@ -147,7 +147,7 @@ public static void delegatingLauncherArrayAlerts(String arg, boolean condition) String[] delegatedArray = new String[2]; delegatedArray[0] = "/usr/bin/env"; delegatedArray[1] = arg; - new ProcessBuilder(delegatedArray).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(delegatedArray).start(); // $ Alert[java/command-line-injection] String launcher; if (condition) { @@ -155,7 +155,7 @@ public static void delegatingLauncherArrayAlerts(String arg, boolean condition) } else { launcher = "/usr/bin/timeout"; } - new ProcessBuilder(new String[]{launcher, "10", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{launcher, "10", arg}).start(); // $ Alert[java/command-line-injection] } public static void fixedDelegatingLauncherArray(String arg) throws java.io.IOException { @@ -167,17 +167,17 @@ public static void fixedDelegatingLauncherArray(String arg) throws java.io.IOExc public static void changedDelegatedCommand(String arg) throws java.io.IOException { String[] command = new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}; command[1] = "/bin/sh"; - new ProcessBuilder(command).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(command).start(); // $ Alert[java/command-line-injection] String[] copiedInline = Arrays.copyOf(new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}, 4); copiedInline[1] = "/bin/sh"; - new ProcessBuilder(copiedInline).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(copiedInline).start(); // $ Alert[java/command-line-injection] String[] base = new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}; String[] copiedStored = Arrays.copyOf(base, 4); copiedStored[1] = "/bin/sh"; - new ProcessBuilder(copiedStored).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(copiedStored).start(); // $ Alert[java/command-line-injection] } public static String[] returnedNonShellCommand(String arg) { @@ -197,7 +197,7 @@ public static void useReturnedNonShellCommand(String arg) throws java.io.IOExcep } public static void unknownDelegatedCommand(String arg, String command) throws java.io.IOException { - new ProcessBuilder(new String[]{"/usr/bin/env", command, "-c", arg}).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(new String[]{"/usr/bin/env", command, "-c", arg}).start(); // $ Alert[java/command-line-injection] } public static void unsupportedEnvAssignment(String arg) throws java.io.IOException { @@ -212,7 +212,7 @@ public static void changedDelegatedCommandThroughAlias(String arg) throws java.i String[] command = new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}; String[] alias = command; alias[1] = "/bin/sh"; - new ProcessBuilder(command).start(); // $ MISSING: Alert[java/command-line-injection] + new ProcessBuilder(command).start(); // $ Alert[java/command-line-injection] } public static void relativeCommand() throws java.io.IOException { From d353c531243822ac3209ee97bbe5cf069436a338 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 6 Nov 2026 10:48:31 +0000 Subject: [PATCH 3/5] Restrict inline delegated arguments to command consumers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../code/java/security/CommandArguments.qll | 13 +- .../security/CWE-078/ExecRelative.expected | 2 +- .../security/CWE-078/ExecTainted.expected | 144 +++++++++++------- .../query-tests/security/CWE-078/Test.java | 13 ++ 4 files changed, 107 insertions(+), 65 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/CommandArguments.qll b/java/ql/lib/semmle/code/java/security/CommandArguments.qll index eb96c964a8e9..976abc81e294 100644 --- a/java/ql/lib/semmle/code/java/security/CommandArguments.qll +++ b/java/ql/lib/semmle/code/java/security/CommandArguments.qll @@ -83,15 +83,16 @@ private predicate isNonDelegatingCommand(Expr ex) { not exists(delegatedCommandIndex(ex)) } -/** Holds if `init` initializes an array passed directly to a call. */ +/** Holds if `init` initializes an array passed directly to a command consumer. */ private predicate isInlineArgumentArray(ArrayInit init) { exists(ArrayCreationExpr array, Call call | array.getInit() = init and - array = call.getAnArgument() and - not exists(MethodCall methodCall | - call = methodCall and - methodCall.getMethod().getDeclaringType().hasQualifiedName("java.util", "Arrays") and - methodCall.getMethod().hasName("copyOf") + array = call.getArgument(0) and + ( + call.getCallee().hasQualifiedName("java.lang", "Runtime", "exec") + or + call.getCallee() + .hasQualifiedName("java.lang", "ProcessBuilder", ["ProcessBuilder", "command"]) ) ) } diff --git a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected index 4ff84d888b6c..c4e8bdc44381 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected @@ -1,2 +1,2 @@ | TaintedEnvironment.java:39:35:39:55 | new String[] | Command with a relative path 'ls' is executed. | -| Test.java:219:46:219:49 | "ls" | Command with a relative path 'ls' is executed. | +| Test.java:231:46:231:49 | "ls" | Command with a relative path 'ls' is executed. | diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected index b7a288cefdf2..9e046a7a1337 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected @@ -1,29 +1,31 @@ #select -| Test.java:8:44:8:69 | ... + ... | Test.java:226:27:226:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:11:29:11:74 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:19:29:19:31 | cmd | Test.java:226:27:226:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:25:29:25:32 | cmd1 | Test.java:226:27:226:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:30:44:30:64 | ... + ... | Test.java:226:27:226:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:68:24:68:56 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:69:24:69:57 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:70:24:70:58 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:71:24:71:73 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:72:24:72:57 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:73:24:73:58 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:74:24:74:59 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:75:24:75:66 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:76:24:76:66 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:77:24:77:73 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:94:24:94:36 | delegatedList | Test.java:226:27:226:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:102:24:102:40 | branchingPosition | Test.java:226:27:226:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:112:24:112:40 | branchingLauncher | Test.java:226:27:226:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:150:24:150:37 | delegatedArray | Test.java:226:27:226:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:158:24:158:56 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:170:24:170:30 | command | Test.java:226:27:226:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:175:24:175:35 | copiedInline | Test.java:226:27:226:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:180:24:180:35 | copiedStored | Test.java:226:27:226:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:200:24:200:71 | new String[] | Test.java:226:27:226:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | -| Test.java:215:24:215:30 | command | Test.java:226:27:226:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:226:27:226:39 | args | user-provided value | +| Test.java:8:44:8:69 | ... + ... | Test.java:238:27:238:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:11:29:11:74 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:19:29:19:31 | cmd | Test.java:238:27:238:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:25:29:25:32 | cmd1 | Test.java:238:27:238:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:30:44:30:64 | ... + ... | Test.java:238:27:238:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:68:24:68:56 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:69:24:69:57 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:70:24:70:58 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:71:24:71:73 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:72:24:72:57 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:73:24:73:58 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:74:24:74:59 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:75:24:75:66 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:76:24:76:66 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:77:24:77:73 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:94:24:94:36 | delegatedList | Test.java:238:27:238:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:102:24:102:40 | branchingPosition | Test.java:238:27:238:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:112:24:112:40 | branchingLauncher | Test.java:238:27:238:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:150:24:150:37 | delegatedArray | Test.java:238:27:238:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:158:24:158:56 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:170:24:170:30 | command | Test.java:238:27:238:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:175:24:175:35 | copiedInline | Test.java:238:27:238:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:180:24:180:35 | copiedStored | Test.java:238:27:238:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:200:24:200:71 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:215:24:215:30 | command | Test.java:238:27:238:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:222:24:222:30 | command | Test.java:238:27:238:39 | args : String[] | Test.java:222:24:222:30 | command | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:227:24:227:29 | copied | Test.java:238:27:238:39 | args : String[] | Test.java:227:24:227:29 | copied | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | edges | Test.java:7:35:7:44 | arg : String | Test.java:8:44:8:69 | ... + ... : String | provenance | | | Test.java:7:35:7:44 | arg : String | Test.java:11:61:11:73 | ... + ... : String | provenance | | @@ -33,7 +35,7 @@ edges | Test.java:11:29:11:74 | {...} : String[] [[]] : String | Test.java:11:29:11:74 | new String[] | provenance | Sink:MaD:2 | | Test.java:11:61:11:73 | ... + ... : String | Test.java:11:29:11:74 | {...} : String[] [[]] : String | provenance | | | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | Test.java:19:29:19:31 | cmd | provenance | Sink:MaD:1 | -| Test.java:17:13:17:25 | ... + ... : String | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:17:13:17:25 | ... + ... : String | Test.java:17:5:17:7 | cmd [post update] : ArrayList [] : String | provenance | MaD:6 | | Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | Test.java:25:29:25:32 | cmd1 | provenance | Sink:MaD:2 | | Test.java:23:15:23:27 | ... + ... : String | Test.java:23:5:23:8 | cmd1 [post update] : String[] [[]] : String | provenance | | | Test.java:29:38:29:47 | arg : String | Test.java:30:44:30:64 | ... + ... : String | provenance | | @@ -72,11 +74,11 @@ edges | Test.java:89:51:89:60 | arg : String | Test.java:101:27:101:29 | arg : String | provenance | | | Test.java:89:51:89:60 | arg : String | Test.java:111:27:111:29 | arg : String | provenance | | | Test.java:93:5:93:17 | delegatedList [post update] : ArrayList [] : String | Test.java:94:24:94:36 | delegatedList | provenance | Sink:MaD:1 | -| Test.java:93:23:93:25 | arg : String | Test.java:93:5:93:17 | delegatedList [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:93:23:93:25 | arg : String | Test.java:93:5:93:17 | delegatedList [post update] : ArrayList [] : String | provenance | MaD:6 | | Test.java:101:5:101:21 | branchingPosition [post update] : ArrayList [] : String | Test.java:102:24:102:40 | branchingPosition | provenance | Sink:MaD:1 | -| Test.java:101:27:101:29 | arg : String | Test.java:101:5:101:21 | branchingPosition [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:101:27:101:29 | arg : String | Test.java:101:5:101:21 | branchingPosition [post update] : ArrayList [] : String | provenance | MaD:6 | | Test.java:111:5:111:21 | branchingLauncher [post update] : ArrayList [] : String | Test.java:112:24:112:40 | branchingLauncher | provenance | Sink:MaD:1 | -| Test.java:111:27:111:29 | arg : String | Test.java:111:5:111:21 | branchingLauncher [post update] : ArrayList [] : String | provenance | MaD:4 | +| Test.java:111:27:111:29 | arg : String | Test.java:111:5:111:21 | branchingLauncher [post update] : ArrayList [] : String | provenance | MaD:6 | | Test.java:145:52:145:61 | arg : String | Test.java:149:25:149:27 | arg : String | provenance | | | Test.java:145:52:145:61 | arg : String | Test.java:158:53:158:55 | arg : String | provenance | | | Test.java:149:5:149:18 | delegatedArray [post update] : String[] [[]] : String | Test.java:150:24:150:37 | delegatedArray | provenance | Sink:MaD:2 | @@ -89,40 +91,54 @@ edges | Test.java:168:24:168:73 | {...} : String[] [[]] : String | Test.java:170:24:170:30 | command | provenance | Sink:MaD:2 | | Test.java:168:70:168:72 | arg : String | Test.java:168:24:168:73 | {...} : String[] [[]] : String | provenance | | | Test.java:173:9:173:76 | copyOf(...) : String[] [[]] : String | Test.java:175:24:175:35 | copiedInline | provenance | Sink:MaD:2 | -| Test.java:173:23:173:72 | new String[] : String[] [[]] : String | Test.java:173:9:173:76 | copyOf(...) : String[] [[]] : String | provenance | MaD:3 | +| Test.java:173:23:173:72 | new String[] : String[] [[]] : String | Test.java:173:9:173:76 | copyOf(...) : String[] [[]] : String | provenance | MaD:4 | | Test.java:173:23:173:72 | {...} : String[] [[]] : String | Test.java:173:23:173:72 | new String[] : String[] [[]] : String | provenance | | | Test.java:173:69:173:71 | arg : String | Test.java:173:23:173:72 | {...} : String[] [[]] : String | provenance | | | Test.java:177:21:177:70 | {...} : String[] [[]] : String | Test.java:178:43:178:46 | base : String[] [[]] : String | provenance | | | Test.java:177:67:177:69 | arg : String | Test.java:177:21:177:70 | {...} : String[] [[]] : String | provenance | | | Test.java:178:29:178:50 | copyOf(...) : String[] [[]] : String | Test.java:180:24:180:35 | copiedStored | provenance | Sink:MaD:2 | -| Test.java:178:43:178:46 | base : String[] [[]] : String | Test.java:178:29:178:50 | copyOf(...) : String[] [[]] : String | provenance | MaD:3 | +| Test.java:178:43:178:46 | base : String[] [[]] : String | Test.java:178:29:178:50 | copyOf(...) : String[] [[]] : String | provenance | MaD:4 | | Test.java:199:46:199:55 | arg : String | Test.java:200:68:200:70 | arg : String | provenance | | | Test.java:200:24:200:71 | {...} : String[] [[]] : String | Test.java:200:24:200:71 | new String[] | provenance | Sink:MaD:2 | | Test.java:200:68:200:70 | arg : String | Test.java:200:24:200:71 | {...} : String[] [[]] : String | provenance | | | Test.java:211:58:211:67 | arg : String | Test.java:212:70:212:72 | arg : String | provenance | | | Test.java:212:24:212:73 | {...} : String[] [[]] : String | Test.java:215:24:215:30 | command | provenance | Sink:MaD:2 | | Test.java:212:70:212:72 | arg : String | Test.java:212:24:212:73 | {...} : String[] [[]] : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:229:20:229:22 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:230:23:230:25 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:236:34:236:36 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:239:36:239:38 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:243:37:243:39 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:245:31:245:33 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:248:31:248:33 | arg : String | provenance | | -| Test.java:226:27:226:39 | args : String[] | Test.java:251:43:251:45 | arg : String | provenance | | -| Test.java:229:20:229:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | -| Test.java:230:23:230:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | -| Test.java:236:34:236:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | -| Test.java:239:36:239:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | -| Test.java:243:37:243:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | -| Test.java:245:31:245:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | -| Test.java:248:31:248:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | -| Test.java:251:43:251:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | +| Test.java:218:69:218:78 | arg : String | Test.java:220:88:220:90 | arg : String | provenance | | +| Test.java:218:69:218:78 | arg : String | Test.java:225:74:225:76 | arg : String | provenance | | +| Test.java:220:28:220:92 | asList(...) : List [] : String | Test.java:222:24:222:30 | command | provenance | Sink:MaD:1 | +| Test.java:220:42:220:91 | new String[] : String[] [[]] : String | Test.java:220:28:220:92 | asList(...) : List [] : String | provenance | MaD:3 | +| Test.java:220:42:220:91 | {...} : String[] [[]] : String | Test.java:220:42:220:91 | new String[] : String[] [[]] : String | provenance | | +| Test.java:220:88:220:90 | arg : String | Test.java:220:42:220:91 | {...} : String[] [[]] : String | provenance | | +| Test.java:225:9:225:84 | copyOfRange(...) : String[] [[]] : String | Test.java:227:24:227:29 | copied | provenance | Sink:MaD:2 | +| Test.java:225:28:225:77 | new String[] : String[] [[]] : String | Test.java:225:9:225:84 | copyOfRange(...) : String[] [[]] : String | provenance | MaD:5 | +| Test.java:225:28:225:77 | {...} : String[] [[]] : String | Test.java:225:28:225:77 | new String[] : String[] [[]] : String | provenance | | +| Test.java:225:74:225:76 | arg : String | Test.java:225:28:225:77 | {...} : String[] [[]] : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:241:20:241:22 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:242:23:242:25 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:248:34:248:36 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:251:36:251:38 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:255:37:255:39 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:257:31:257:33 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:260:31:260:33 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:263:43:263:45 | arg : String | provenance | | +| Test.java:238:27:238:39 | args : String[] | Test.java:264:54:264:56 | arg : String | provenance | | +| Test.java:241:20:241:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | +| Test.java:242:23:242:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | +| Test.java:248:34:248:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | +| Test.java:251:36:251:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | +| Test.java:255:37:255:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | +| Test.java:257:31:257:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | +| Test.java:260:31:260:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | +| Test.java:263:43:263:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | +| Test.java:264:54:264:56 | arg : String | Test.java:218:69:218:78 | arg : String | provenance | | models | 1 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (List); ; Argument[0]; command-injection; ai-manual | | 2 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (String[]); ; Argument[0]; command-injection; ai-manual | -| 3 | Summary: java.util; Arrays; false; copyOf; ; ; Argument[0].ArrayElement; ReturnValue.ArrayElement; value; manual | -| 4 | Summary: java.util; Collection; true; add; ; ; Argument[0]; Argument[this].Element; value; manual | +| 3 | Summary: java.util; Arrays; false; asList; ; ; Argument[0].ArrayElement; ReturnValue.Element; value; manual | +| 4 | Summary: java.util; Arrays; false; copyOf; ; ; Argument[0].ArrayElement; ReturnValue.ArrayElement; value; manual | +| 5 | Summary: java.util; Arrays; false; copyOfRange; ; ; Argument[0].ArrayElement; ReturnValue.ArrayElement; value; manual | +| 6 | Summary: java.util; Collection; true; add; ; ; Argument[0]; Argument[this].Element; value; manual | nodes | Test.java:7:35:7:44 | arg : String | semmle.label | arg : String | | Test.java:8:25:8:70 | new ..[] { .. } | semmle.label | new ..[] { .. } | @@ -209,13 +225,25 @@ nodes | Test.java:212:24:212:73 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | | Test.java:212:70:212:72 | arg : String | semmle.label | arg : String | | Test.java:215:24:215:30 | command | semmle.label | command | -| Test.java:226:27:226:39 | args : String[] | semmle.label | args : String[] | -| Test.java:229:20:229:22 | arg : String | semmle.label | arg : String | -| Test.java:230:23:230:25 | arg : String | semmle.label | arg : String | -| Test.java:236:34:236:36 | arg : String | semmle.label | arg : String | -| Test.java:239:36:239:38 | arg : String | semmle.label | arg : String | -| Test.java:243:37:243:39 | arg : String | semmle.label | arg : String | -| Test.java:245:31:245:33 | arg : String | semmle.label | arg : String | -| Test.java:248:31:248:33 | arg : String | semmle.label | arg : String | -| Test.java:251:43:251:45 | arg : String | semmle.label | arg : String | +| Test.java:218:69:218:78 | arg : String | semmle.label | arg : String | +| Test.java:220:28:220:92 | asList(...) : List [] : String | semmle.label | asList(...) : List [] : String | +| Test.java:220:42:220:91 | new String[] : String[] [[]] : String | semmle.label | new String[] : String[] [[]] : String | +| Test.java:220:42:220:91 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:220:88:220:90 | arg : String | semmle.label | arg : String | +| Test.java:222:24:222:30 | command | semmle.label | command | +| Test.java:225:9:225:84 | copyOfRange(...) : String[] [[]] : String | semmle.label | copyOfRange(...) : String[] [[]] : String | +| Test.java:225:28:225:77 | new String[] : String[] [[]] : String | semmle.label | new String[] : String[] [[]] : String | +| Test.java:225:28:225:77 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:225:74:225:76 | arg : String | semmle.label | arg : String | +| Test.java:227:24:227:29 | copied | semmle.label | copied | +| Test.java:238:27:238:39 | args : String[] | semmle.label | args : String[] | +| Test.java:241:20:241:22 | arg : String | semmle.label | arg : String | +| Test.java:242:23:242:25 | arg : String | semmle.label | arg : String | +| Test.java:248:34:248:36 | arg : String | semmle.label | arg : String | +| Test.java:251:36:251:38 | arg : String | semmle.label | arg : String | +| Test.java:255:37:255:39 | arg : String | semmle.label | arg : String | +| Test.java:257:31:257:33 | arg : String | semmle.label | arg : String | +| Test.java:260:31:260:33 | arg : String | semmle.label | arg : String | +| Test.java:263:43:263:45 | arg : String | semmle.label | arg : String | +| Test.java:264:54:264:56 | arg : String | semmle.label | arg : String | subpaths diff --git a/java/ql/test/query-tests/security/CWE-078/Test.java b/java/ql/test/query-tests/security/CWE-078/Test.java index fc4a409983ac..484a2ef76e31 100644 --- a/java/ql/test/query-tests/security/CWE-078/Test.java +++ b/java/ql/test/query-tests/security/CWE-078/Test.java @@ -215,6 +215,18 @@ public static void changedDelegatedCommandThroughAlias(String arg) throws java.i new ProcessBuilder(command).start(); // $ Alert[java/command-line-injection] } + public static void changedDelegatedCommandThroughDerivedContainer(String arg) + throws java.io.IOException { + List command = Arrays.asList(new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}); + command.set(1, "/bin/sh"); + new ProcessBuilder(command).start(); // $ Alert[java/command-line-injection] + + String[] copied = + Arrays.copyOfRange(new String[]{"/usr/bin/env", "/bin/cp", "-c", arg}, 0, 4); + copied[1] = "/bin/sh"; + new ProcessBuilder(copied).start(); // $ Alert[java/command-line-injection] + } + public static void relativeCommand() throws java.io.IOException { ProcessBuilder pb = new ProcessBuilder("ls"); // $ Alert[java/relative-path-command] pb.start(); @@ -249,6 +261,7 @@ public static void main(String[] args) throws java.io.IOException { // $ Source[ unsupportedEnvAssignment(arg); unsupportedSudoOption(arg); changedDelegatedCommandThroughAlias(arg); + changedDelegatedCommandThroughDerivedContainer(arg); relativeCommand(); } } From 6c2633d3c4cb53ddf3cf23e9f7ba6941e31715ed Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 6 Nov 2026 10:49:21 +0000 Subject: [PATCH 4/5] Reject shell alternatives when sanitizing delegated arguments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../code/java/security/CommandArguments.qll | 1 + .../security/CWE-078/ExecRelative.expected | 2 +- .../security/CWE-078/ExecTainted.expected | 135 +++++++++++------- .../query-tests/security/CWE-078/Test.java | 24 ++++ 4 files changed, 106 insertions(+), 56 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/CommandArguments.qll b/java/ql/lib/semmle/code/java/security/CommandArguments.qll index 976abc81e294..b46d7b5b3797 100644 --- a/java/ql/lib/semmle/code/java/security/CommandArguments.qll +++ b/java/ql/lib/semmle/code/java/security/CommandArguments.qll @@ -136,6 +136,7 @@ private class CommandArguments extends TCommandArguments { predicate hasSafeDelegatedArgument(int index) { exists(this.getArgument(index)) and forex(Expr launcher | launcher = this.getArgument(0) | + not isShell(launcher) and forex(int commandIndex | commandIndex = delegatedCommandIndex(launcher) | ( index < commandIndex diff --git a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected index c4e8bdc44381..5f39fd7c134a 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected @@ -1,2 +1,2 @@ | TaintedEnvironment.java:39:35:39:55 | new String[] | Command with a relative path 'ls' is executed. | -| Test.java:231:46:231:49 | "ls" | Command with a relative path 'ls' is executed. | +| Test.java:254:46:254:49 | "ls" | Command with a relative path 'ls' is executed. | diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected index 9e046a7a1337..65841612ce23 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected @@ -1,31 +1,34 @@ #select -| Test.java:8:44:8:69 | ... + ... | Test.java:238:27:238:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:11:29:11:74 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:19:29:19:31 | cmd | Test.java:238:27:238:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:25:29:25:32 | cmd1 | Test.java:238:27:238:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:30:44:30:64 | ... + ... | Test.java:238:27:238:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:68:24:68:56 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:69:24:69:57 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:70:24:70:58 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:71:24:71:73 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:72:24:72:57 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:73:24:73:58 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:74:24:74:59 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:75:24:75:66 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:76:24:76:66 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:77:24:77:73 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:94:24:94:36 | delegatedList | Test.java:238:27:238:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:102:24:102:40 | branchingPosition | Test.java:238:27:238:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:112:24:112:40 | branchingLauncher | Test.java:238:27:238:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:150:24:150:37 | delegatedArray | Test.java:238:27:238:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:158:24:158:56 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:170:24:170:30 | command | Test.java:238:27:238:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:175:24:175:35 | copiedInline | Test.java:238:27:238:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:180:24:180:35 | copiedStored | Test.java:238:27:238:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:200:24:200:71 | new String[] | Test.java:238:27:238:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:215:24:215:30 | command | Test.java:238:27:238:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:222:24:222:30 | command | Test.java:238:27:238:39 | args : String[] | Test.java:222:24:222:30 | command | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | -| Test.java:227:24:227:29 | copied | Test.java:238:27:238:39 | args : String[] | Test.java:227:24:227:29 | copied | This command line depends on a $@. | Test.java:238:27:238:39 | args | user-provided value | +| Test.java:8:44:8:69 | ... + ... | Test.java:261:27:261:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:11:29:11:74 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:19:29:19:31 | cmd | Test.java:261:27:261:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:25:29:25:32 | cmd1 | Test.java:261:27:261:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:30:44:30:64 | ... + ... | Test.java:261:27:261:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:68:24:68:56 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:69:24:69:57 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:70:24:70:58 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:71:24:71:73 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:72:24:72:57 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:73:24:73:58 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:74:24:74:59 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:75:24:75:66 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:76:24:76:66 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:77:24:77:73 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:94:24:94:36 | delegatedList | Test.java:261:27:261:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:102:24:102:40 | branchingPosition | Test.java:261:27:261:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:112:24:112:40 | branchingLauncher | Test.java:261:27:261:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:150:24:150:37 | delegatedArray | Test.java:261:27:261:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:158:24:158:56 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:170:24:170:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:175:24:175:35 | copiedInline | Test.java:261:27:261:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:180:24:180:35 | copiedStored | Test.java:261:27:261:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:200:24:200:71 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:215:24:215:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:222:24:222:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:222:24:222:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:227:24:227:29 | copied | Test.java:261:27:261:39 | args : String[] | Test.java:227:24:227:29 | copied | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:238:24:238:56 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:238:24:238:56 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:244:24:244:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:244:24:244:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:250:24:250:27 | list | Test.java:261:27:261:39 | args : String[] | Test.java:250:24:250:27 | list | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | edges | Test.java:7:35:7:44 | arg : String | Test.java:8:44:8:69 | ... + ... : String | provenance | | | Test.java:7:35:7:44 | arg : String | Test.java:11:61:11:73 | ... + ... : String | provenance | | @@ -114,24 +117,35 @@ edges | Test.java:225:28:225:77 | new String[] : String[] [[]] : String | Test.java:225:9:225:84 | copyOfRange(...) : String[] [[]] : String | provenance | MaD:5 | | Test.java:225:28:225:77 | {...} : String[] [[]] : String | Test.java:225:28:225:77 | new String[] : String[] [[]] : String | provenance | | | Test.java:225:74:225:76 | arg : String | Test.java:225:28:225:77 | {...} : String[] [[]] : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:241:20:241:22 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:242:23:242:25 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:248:34:248:36 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:251:36:251:38 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:255:37:255:39 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:257:31:257:33 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:260:31:260:33 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:263:43:263:45 | arg : String | provenance | | -| Test.java:238:27:238:39 | args : String[] | Test.java:264:54:264:56 | arg : String | provenance | | -| Test.java:241:20:241:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | -| Test.java:242:23:242:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | -| Test.java:248:34:248:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | -| Test.java:251:36:251:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | -| Test.java:255:37:255:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | -| Test.java:257:31:257:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | -| Test.java:260:31:260:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | -| Test.java:263:43:263:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | -| Test.java:264:54:264:56 | arg : String | Test.java:218:69:218:78 | arg : String | provenance | | +| Test.java:230:41:230:50 | arg : String | Test.java:238:53:238:55 | arg : String | provenance | | +| Test.java:230:41:230:50 | arg : String | Test.java:243:18:243:20 | arg : String | provenance | | +| Test.java:230:41:230:50 | arg : String | Test.java:249:14:249:16 | arg : String | provenance | | +| Test.java:238:24:238:56 | {...} : String[] [[]] : String | Test.java:238:24:238:56 | new String[] | provenance | Sink:MaD:2 | +| Test.java:238:53:238:55 | arg : String | Test.java:238:24:238:56 | {...} : String[] [[]] : String | provenance | | +| Test.java:243:5:243:11 | command [post update] : String[] [[]] : String | Test.java:244:24:244:30 | command | provenance | Sink:MaD:2 | +| Test.java:243:18:243:20 | arg : String | Test.java:243:5:243:11 | command [post update] : String[] [[]] : String | provenance | | +| Test.java:249:5:249:8 | list [post update] : ArrayList [] : String | Test.java:250:24:250:27 | list | provenance | Sink:MaD:1 | +| Test.java:249:14:249:16 | arg : String | Test.java:249:5:249:8 | list [post update] : ArrayList [] : String | provenance | MaD:6 | +| Test.java:261:27:261:39 | args : String[] | Test.java:264:20:264:22 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:265:23:265:25 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:271:34:271:36 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:274:36:274:38 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:278:37:278:39 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:280:31:280:33 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:283:31:283:33 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:286:43:286:45 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:287:54:287:56 | arg : String | provenance | | +| Test.java:261:27:261:39 | args : String[] | Test.java:288:26:288:28 | arg : String | provenance | | +| Test.java:264:20:264:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | +| Test.java:265:23:265:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | +| Test.java:271:34:271:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | +| Test.java:274:36:274:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | +| Test.java:278:37:278:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | +| Test.java:280:31:280:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | +| Test.java:283:31:283:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | +| Test.java:286:43:286:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | +| Test.java:287:54:287:56 | arg : String | Test.java:218:69:218:78 | arg : String | provenance | | +| Test.java:288:26:288:28 | arg : String | Test.java:230:41:230:50 | arg : String | provenance | | models | 1 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (List); ; Argument[0]; command-injection; ai-manual | | 2 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (String[]); ; Argument[0]; command-injection; ai-manual | @@ -236,14 +250,25 @@ nodes | Test.java:225:28:225:77 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | | Test.java:225:74:225:76 | arg : String | semmle.label | arg : String | | Test.java:227:24:227:29 | copied | semmle.label | copied | -| Test.java:238:27:238:39 | args : String[] | semmle.label | args : String[] | -| Test.java:241:20:241:22 | arg : String | semmle.label | arg : String | -| Test.java:242:23:242:25 | arg : String | semmle.label | arg : String | -| Test.java:248:34:248:36 | arg : String | semmle.label | arg : String | -| Test.java:251:36:251:38 | arg : String | semmle.label | arg : String | -| Test.java:255:37:255:39 | arg : String | semmle.label | arg : String | -| Test.java:257:31:257:33 | arg : String | semmle.label | arg : String | -| Test.java:260:31:260:33 | arg : String | semmle.label | arg : String | -| Test.java:263:43:263:45 | arg : String | semmle.label | arg : String | -| Test.java:264:54:264:56 | arg : String | semmle.label | arg : String | +| Test.java:230:41:230:50 | arg : String | semmle.label | arg : String | +| Test.java:238:24:238:56 | new String[] | semmle.label | new String[] | +| Test.java:238:24:238:56 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | +| Test.java:238:53:238:55 | arg : String | semmle.label | arg : String | +| Test.java:243:5:243:11 | command [post update] : String[] [[]] : String | semmle.label | command [post update] : String[] [[]] : String | +| Test.java:243:18:243:20 | arg : String | semmle.label | arg : String | +| Test.java:244:24:244:30 | command | semmle.label | command | +| Test.java:249:5:249:8 | list [post update] : ArrayList [] : String | semmle.label | list [post update] : ArrayList [] : String | +| Test.java:249:14:249:16 | arg : String | semmle.label | arg : String | +| Test.java:250:24:250:27 | list | semmle.label | list | +| Test.java:261:27:261:39 | args : String[] | semmle.label | args : String[] | +| Test.java:264:20:264:22 | arg : String | semmle.label | arg : String | +| Test.java:265:23:265:25 | arg : String | semmle.label | arg : String | +| Test.java:271:34:271:36 | arg : String | semmle.label | arg : String | +| Test.java:274:36:274:38 | arg : String | semmle.label | arg : String | +| Test.java:278:37:278:39 | arg : String | semmle.label | arg : String | +| Test.java:280:31:280:33 | arg : String | semmle.label | arg : String | +| Test.java:283:31:283:33 | arg : String | semmle.label | arg : String | +| Test.java:286:43:286:45 | arg : String | semmle.label | arg : String | +| Test.java:287:54:287:56 | arg : String | semmle.label | arg : String | +| Test.java:288:26:288:28 | arg : String | semmle.label | arg : String | subpaths diff --git a/java/ql/test/query-tests/security/CWE-078/Test.java b/java/ql/test/query-tests/security/CWE-078/Test.java index 484a2ef76e31..c75b6d85d4f2 100644 --- a/java/ql/test/query-tests/security/CWE-078/Test.java +++ b/java/ql/test/query-tests/security/CWE-078/Test.java @@ -227,6 +227,29 @@ public static void changedDelegatedCommandThroughDerivedContainer(String arg) new ProcessBuilder(copied).start(); // $ Alert[java/command-line-injection] } + public static void launcherMayBeShell(String arg, boolean condition) + throws java.io.IOException { + String launcher; + if (condition) { + launcher = "/usr/bin/env"; + } else { + launcher = "/bin/sh"; + } + new ProcessBuilder(new String[]{launcher, "-c", arg}).start(); // $ Alert[java/command-line-injection] + + String[] command = new String[3]; + command[0] = launcher; + command[1] = "-c"; + command[2] = arg; + new ProcessBuilder(command).start(); // $ Alert[java/command-line-injection] + + List list = new ArrayList(); + list.add(launcher); + list.add("-c"); + list.add(arg); + new ProcessBuilder(list).start(); // $ Alert[java/command-line-injection] + } + public static void relativeCommand() throws java.io.IOException { ProcessBuilder pb = new ProcessBuilder("ls"); // $ Alert[java/relative-path-command] pb.start(); @@ -262,6 +285,7 @@ public static void main(String[] args) throws java.io.IOException { // $ Source[ unsupportedSudoOption(arg); changedDelegatedCommandThroughAlias(arg); changedDelegatedCommandThroughDerivedContainer(arg); + launcherMayBeShell(arg, args.length > 2); relativeCommand(); } } From d6c859445f59798dad98dcb17db9a73329866028 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 6 Nov 2026 10:50:08 +0000 Subject: [PATCH 5/5] Preserve sanitization for ordinary command expressions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../code/java/security/CommandArguments.qll | 1 - .../security/CWE-078/ExecRelative.expected | 2 +- .../security/CWE-078/ExecTainted.expected | 122 +++++++++--------- .../query-tests/security/CWE-078/Test.java | 13 ++ 4 files changed, 75 insertions(+), 63 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/CommandArguments.qll b/java/ql/lib/semmle/code/java/security/CommandArguments.qll index b46d7b5b3797..4de6e9506665 100644 --- a/java/ql/lib/semmle/code/java/security/CommandArguments.qll +++ b/java/ql/lib/semmle/code/java/security/CommandArguments.qll @@ -78,7 +78,6 @@ private int delegatedCommandIndex(Expr ex) { } private predicate isNonDelegatingCommand(Expr ex) { - exists(commandName(ex)) and not isShell(ex) and not exists(delegatedCommandIndex(ex)) } diff --git a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected index 5f39fd7c134a..ec527035da15 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecRelative.expected @@ -1,2 +1,2 @@ | TaintedEnvironment.java:39:35:39:55 | new String[] | Command with a relative path 'ls' is executed. | -| Test.java:254:46:254:49 | "ls" | Command with a relative path 'ls' is executed. | +| Test.java:266:46:266:49 | "ls" | Command with a relative path 'ls' is executed. | diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected index 65841612ce23..f2653df3e6f9 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTainted.expected @@ -1,34 +1,34 @@ #select -| Test.java:8:44:8:69 | ... + ... | Test.java:261:27:261:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:11:29:11:74 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:19:29:19:31 | cmd | Test.java:261:27:261:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:25:29:25:32 | cmd1 | Test.java:261:27:261:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:30:44:30:64 | ... + ... | Test.java:261:27:261:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:68:24:68:56 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:69:24:69:57 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:70:24:70:58 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:71:24:71:73 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:72:24:72:57 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:73:24:73:58 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:74:24:74:59 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:75:24:75:66 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:76:24:76:66 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:77:24:77:73 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:94:24:94:36 | delegatedList | Test.java:261:27:261:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:102:24:102:40 | branchingPosition | Test.java:261:27:261:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:112:24:112:40 | branchingLauncher | Test.java:261:27:261:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:150:24:150:37 | delegatedArray | Test.java:261:27:261:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:158:24:158:56 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:170:24:170:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:175:24:175:35 | copiedInline | Test.java:261:27:261:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:180:24:180:35 | copiedStored | Test.java:261:27:261:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:200:24:200:71 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:215:24:215:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:222:24:222:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:222:24:222:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:227:24:227:29 | copied | Test.java:261:27:261:39 | args : String[] | Test.java:227:24:227:29 | copied | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:238:24:238:56 | new String[] | Test.java:261:27:261:39 | args : String[] | Test.java:238:24:238:56 | new String[] | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:244:24:244:30 | command | Test.java:261:27:261:39 | args : String[] | Test.java:244:24:244:30 | command | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | -| Test.java:250:24:250:27 | list | Test.java:261:27:261:39 | args : String[] | Test.java:250:24:250:27 | list | This command line depends on a $@. | Test.java:261:27:261:39 | args | user-provided value | +| Test.java:8:44:8:69 | ... + ... | Test.java:273:27:273:39 | args : String[] | Test.java:8:25:8:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:11:29:11:74 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:11:29:11:74 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:19:29:19:31 | cmd | Test.java:273:27:273:39 | args : String[] | Test.java:19:29:19:31 | cmd | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:25:29:25:32 | cmd1 | Test.java:273:27:273:39 | args : String[] | Test.java:25:29:25:32 | cmd1 | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:30:44:30:64 | ... + ... | Test.java:273:27:273:39 | args : String[] | Test.java:30:25:30:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:68:24:68:56 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:68:24:68:56 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:69:24:69:57 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:69:24:69:57 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:70:24:70:58 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:70:24:70:58 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:71:24:71:73 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:71:24:71:73 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:72:24:72:57 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:72:24:72:57 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:73:24:73:58 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:73:24:73:58 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:74:24:74:59 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:74:24:74:59 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:75:24:75:66 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:75:24:75:66 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:76:24:76:66 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:76:24:76:66 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:77:24:77:73 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:77:24:77:73 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:94:24:94:36 | delegatedList | Test.java:273:27:273:39 | args : String[] | Test.java:94:24:94:36 | delegatedList | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:102:24:102:40 | branchingPosition | Test.java:273:27:273:39 | args : String[] | Test.java:102:24:102:40 | branchingPosition | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:112:24:112:40 | branchingLauncher | Test.java:273:27:273:39 | args : String[] | Test.java:112:24:112:40 | branchingLauncher | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:150:24:150:37 | delegatedArray | Test.java:273:27:273:39 | args : String[] | Test.java:150:24:150:37 | delegatedArray | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:158:24:158:56 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:158:24:158:56 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:170:24:170:30 | command | Test.java:273:27:273:39 | args : String[] | Test.java:170:24:170:30 | command | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:175:24:175:35 | copiedInline | Test.java:273:27:273:39 | args : String[] | Test.java:175:24:175:35 | copiedInline | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:180:24:180:35 | copiedStored | Test.java:273:27:273:39 | args : String[] | Test.java:180:24:180:35 | copiedStored | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:200:24:200:71 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:200:24:200:71 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:215:24:215:30 | command | Test.java:273:27:273:39 | args : String[] | Test.java:215:24:215:30 | command | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:222:24:222:30 | command | Test.java:273:27:273:39 | args : String[] | Test.java:222:24:222:30 | command | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:227:24:227:29 | copied | Test.java:273:27:273:39 | args : String[] | Test.java:227:24:227:29 | copied | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:238:24:238:56 | new String[] | Test.java:273:27:273:39 | args : String[] | Test.java:238:24:238:56 | new String[] | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:244:24:244:30 | command | Test.java:273:27:273:39 | args : String[] | Test.java:244:24:244:30 | command | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | +| Test.java:250:24:250:27 | list | Test.java:273:27:273:39 | args : String[] | Test.java:250:24:250:27 | list | This command line depends on a $@. | Test.java:273:27:273:39 | args | user-provided value | edges | Test.java:7:35:7:44 | arg : String | Test.java:8:44:8:69 | ... + ... : String | provenance | | | Test.java:7:35:7:44 | arg : String | Test.java:11:61:11:73 | ... + ... : String | provenance | | @@ -126,26 +126,26 @@ edges | Test.java:243:18:243:20 | arg : String | Test.java:243:5:243:11 | command [post update] : String[] [[]] : String | provenance | | | Test.java:249:5:249:8 | list [post update] : ArrayList [] : String | Test.java:250:24:250:27 | list | provenance | Sink:MaD:1 | | Test.java:249:14:249:16 | arg : String | Test.java:249:5:249:8 | list [post update] : ArrayList [] : String | provenance | MaD:6 | -| Test.java:261:27:261:39 | args : String[] | Test.java:264:20:264:22 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:265:23:265:25 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:271:34:271:36 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:274:36:274:38 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:278:37:278:39 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:280:31:280:33 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:283:31:283:33 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:286:43:286:45 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:287:54:287:56 | arg : String | provenance | | -| Test.java:261:27:261:39 | args : String[] | Test.java:288:26:288:28 | arg : String | provenance | | -| Test.java:264:20:264:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | -| Test.java:265:23:265:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | -| Test.java:271:34:271:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | -| Test.java:274:36:274:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | -| Test.java:278:37:278:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | -| Test.java:280:31:280:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | -| Test.java:283:31:283:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | -| Test.java:286:43:286:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | -| Test.java:287:54:287:56 | arg : String | Test.java:218:69:218:78 | arg : String | provenance | | -| Test.java:288:26:288:28 | arg : String | Test.java:230:41:230:50 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:276:20:276:22 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:277:23:277:25 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:283:34:283:36 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:286:36:286:38 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:290:37:290:39 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:292:31:292:33 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:295:31:295:33 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:298:43:298:45 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:299:54:299:56 | arg : String | provenance | | +| Test.java:273:27:273:39 | args : String[] | Test.java:300:26:300:28 | arg : String | provenance | | +| Test.java:276:20:276:22 | arg : String | Test.java:7:35:7:44 | arg : String | provenance | | +| Test.java:277:23:277:25 | arg : String | Test.java:29:38:29:47 | arg : String | provenance | | +| Test.java:283:34:283:36 | arg : String | Test.java:67:49:67:58 | arg : String | provenance | | +| Test.java:286:36:286:38 | arg : String | Test.java:89:51:89:60 | arg : String | provenance | | +| Test.java:290:37:290:39 | arg : String | Test.java:145:52:145:61 | arg : String | provenance | | +| Test.java:292:31:292:33 | arg : String | Test.java:167:46:167:55 | arg : String | provenance | | +| Test.java:295:31:295:33 | arg : String | Test.java:199:46:199:55 | arg : String | provenance | | +| Test.java:298:43:298:45 | arg : String | Test.java:211:58:211:67 | arg : String | provenance | | +| Test.java:299:54:299:56 | arg : String | Test.java:218:69:218:78 | arg : String | provenance | | +| Test.java:300:26:300:28 | arg : String | Test.java:230:41:230:50 | arg : String | provenance | | models | 1 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (List); ; Argument[0]; command-injection; ai-manual | | 2 | Sink: java.lang; ProcessBuilder; false; ProcessBuilder; (String[]); ; Argument[0]; command-injection; ai-manual | @@ -260,15 +260,15 @@ nodes | Test.java:249:5:249:8 | list [post update] : ArrayList [] : String | semmle.label | list [post update] : ArrayList [] : String | | Test.java:249:14:249:16 | arg : String | semmle.label | arg : String | | Test.java:250:24:250:27 | list | semmle.label | list | -| Test.java:261:27:261:39 | args : String[] | semmle.label | args : String[] | -| Test.java:264:20:264:22 | arg : String | semmle.label | arg : String | -| Test.java:265:23:265:25 | arg : String | semmle.label | arg : String | -| Test.java:271:34:271:36 | arg : String | semmle.label | arg : String | -| Test.java:274:36:274:38 | arg : String | semmle.label | arg : String | -| Test.java:278:37:278:39 | arg : String | semmle.label | arg : String | -| Test.java:280:31:280:33 | arg : String | semmle.label | arg : String | -| Test.java:283:31:283:33 | arg : String | semmle.label | arg : String | -| Test.java:286:43:286:45 | arg : String | semmle.label | arg : String | -| Test.java:287:54:287:56 | arg : String | semmle.label | arg : String | -| Test.java:288:26:288:28 | arg : String | semmle.label | arg : String | +| Test.java:273:27:273:39 | args : String[] | semmle.label | args : String[] | +| Test.java:276:20:276:22 | arg : String | semmle.label | arg : String | +| Test.java:277:23:277:25 | arg : String | semmle.label | arg : String | +| Test.java:283:34:283:36 | arg : String | semmle.label | arg : String | +| Test.java:286:36:286:38 | arg : String | semmle.label | arg : String | +| Test.java:290:37:290:39 | arg : String | semmle.label | arg : String | +| Test.java:292:31:292:33 | arg : String | semmle.label | arg : String | +| Test.java:295:31:295:33 | arg : String | semmle.label | arg : String | +| Test.java:298:43:298:45 | arg : String | semmle.label | arg : String | +| Test.java:299:54:299:56 | arg : String | semmle.label | arg : String | +| Test.java:300:26:300:28 | arg : String | semmle.label | arg : String | subpaths diff --git a/java/ql/test/query-tests/security/CWE-078/Test.java b/java/ql/test/query-tests/security/CWE-078/Test.java index c75b6d85d4f2..24736d092d81 100644 --- a/java/ql/test/query-tests/security/CWE-078/Test.java +++ b/java/ql/test/query-tests/security/CWE-078/Test.java @@ -250,6 +250,18 @@ public static void launcherMayBeShell(String arg, boolean condition) new ProcessBuilder(list).start(); // $ Alert[java/command-line-injection] } + private static final String TOOL = "/bin/cp"; + + private static String getTool() { + return "/bin/cp"; + } + + public static void nonShellCommandExpressions(String arg) throws java.io.IOException { + new ProcessBuilder(new String[]{getTool(), arg}).start(); // GOOD + new ProcessBuilder(new String[]{TOOL, arg}).start(); // GOOD + new ProcessBuilder(new String[]{"/bin/" + "cp", arg}).start(); // GOOD + } + public static void relativeCommand() throws java.io.IOException { ProcessBuilder pb = new ProcessBuilder("ls"); // $ Alert[java/relative-path-command] pb.start(); @@ -286,6 +298,7 @@ public static void main(String[] args) throws java.io.IOException { // $ Source[ changedDelegatedCommandThroughAlias(arg); changedDelegatedCommandThroughDerivedContainer(arg); launcherMayBeShell(arg, args.length > 2); + nonShellCommandExpressions(arg); relativeCommand(); } }