From 8e3adb9633f59bf0710197cd5fb24376383bfe3b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:18:57 +0100 Subject: [PATCH 1/7] unified: copy command injection query and tests Copy the original Swift query, help, examples, and regression test files unchanged as a reviewable baseline for the unified port. Metadata, imports, and test references will be adapted in subsequent commits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../security/CWE-078/CommandInjection.qhelp | 45 +++ .../security/CWE-078/CommandInjection.ql | 23 ++ .../CWE-078/CommandInjectionBad.swift | 5 + .../CWE-078/CommandInjectionGood.swift | 15 + .../CommandInjection.expected | 300 ++++++++++++++++++ .../CommandInjection/CommandInjection.qlref | 1 + .../CommandInjection/CommandInjection.swift | 219 +++++++++++++ 7 files changed, 608 insertions(+) create mode 100644 unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp create mode 100644 unified/ql/src/queries/security/CWE-078/CommandInjection.ql create mode 100644 unified/ql/src/queries/security/CWE-078/CommandInjectionBad.swift create mode 100644 unified/ql/src/queries/security/CWE-078/CommandInjectionGood.swift create mode 100644 unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected create mode 100644 unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref create mode 100644 unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp b/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp new file mode 100644 index 000000000000..ed6a27a4222b --- /dev/null +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp @@ -0,0 +1,45 @@ + + + + +

+Constructing a system command with unsanitized user input is dangerous, +since a malicious user may be able to craft input that executes arbitrary code. +

+
+ + +

+If possible, use hard-coded string literals to specify the command to run. Instead of interpreting +user input directly as command names, examine the input and then choose among hard-coded string +literals. +

+

+If this is not possible, then add sanitization code to verify that the user input is safe before +using it. +

+
+ + +

+The following example executes code from user input without +sanitizing it first: +

+ +

+If user input is used to construct a command it should be checked +first. This ensures that the user cannot insert characters that have special +meanings: +

+ +
+ + +
  • +OWASP: +Command Injection. +
  • +
    +
    diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql new file mode 100644 index 000000000000..defa939cf68b --- /dev/null +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql @@ -0,0 +1,23 @@ +/** + * @name System command built from user-controlled sources + * @description Building a system command from user-controlled sources may allow a malicious + * user to change the meaning of the command. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id swift/command-line-injection + * @tags security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.security.CommandInjectionQuery +import CommandInjectionFlow::PathGraph + +from CommandInjectionFlow::PathNode sourceNode, CommandInjectionFlow::PathNode sinkNode +where CommandInjectionFlow::flowPath(sourceNode, sinkNode) +select sinkNode.getNode(), sourceNode, sinkNode, "This command depends on a $@.", + sourceNode.getNode(), "user-provided value" diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjectionBad.swift b/unified/ql/src/queries/security/CWE-078/CommandInjectionBad.swift new file mode 100644 index 000000000000..ffdaaf907caa --- /dev/null +++ b/unified/ql/src/queries/security/CWE-078/CommandInjectionBad.swift @@ -0,0 +1,5 @@ +var task = Process() +task.launchPath = "/bin/bash" +task.arguments = ["-c", userControlledString] // BAD + +task.launch() \ No newline at end of file diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjectionGood.swift b/unified/ql/src/queries/security/CWE-078/CommandInjectionGood.swift new file mode 100644 index 000000000000..1773d5b187ba --- /dev/null +++ b/unified/ql/src/queries/security/CWE-078/CommandInjectionGood.swift @@ -0,0 +1,15 @@ +func validateCommand(_ command: String) -> String? { + let allowedCommands = ["ls -l", "pwd", "echo"] + if allowedCommands.contains(command) { + return command + } + return nil +} + +if let validatedString = validateCommand(userControlledString) { + var task = Process() + task.launchPath = "/bin/bash" + task.arguments = ["-c", validatedString] // GOOD + + task.launch() +} diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected new file mode 100644 index 000000000000..6e6b86231495 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected @@ -0,0 +1,300 @@ +edges +| CommandInjection.swift:64:22:64:33 | command | CommandInjection.swift:68:16:68:16 | command | provenance | | +| CommandInjection.swift:64:22:64:33 | command [some:0] | CommandInjection.swift:68:16:68:16 | command [some:0] | provenance | | +| CommandInjection.swift:68:16:68:16 | command | CommandInjection.swift:68:16:68:16 | command [some:0] | provenance | | +| CommandInjection.swift:75:8:75:12 | let ...? [some:0, some:0] | CommandInjection.swift:75:12:75:12 | userControlledString [some:0] | provenance | | +| CommandInjection.swift:75:8:75:12 | let ...? [some:0] | CommandInjection.swift:75:12:75:12 | userControlledString | provenance | | +| CommandInjection.swift:75:12:75:12 | userControlledString | CommandInjection.swift:81:27:81:27 | userControlledString | provenance | | +| CommandInjection.swift:75:12:75:12 | userControlledString | CommandInjection.swift:84:43:84:43 | userControlledString | provenance | | +| CommandInjection.swift:75:12:75:12 | userControlledString [some:0] | CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:81:27:81:27 | userControlledString | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:84:43:84:43 | userControlledString | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0, some:0] | CommandInjection.swift:75:8:75:12 | let ...? [some:0, some:0] | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:75:8:75:12 | let ...? [some:0] | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0, some:0] | provenance | | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | provenance | | +| CommandInjection.swift:81:20:81:47 | [...] [Collection element] | CommandInjection.swift:81:2:81:2 | [post] task1 | provenance | | +| CommandInjection.swift:81:27:81:27 | userControlledString | CommandInjection.swift:81:20:81:47 | [...] [Collection element] | provenance | | +| CommandInjection.swift:84:5:84:9 | let ...? [some:0] | CommandInjection.swift:84:9:84:9 | validatedString | provenance | | +| CommandInjection.swift:84:9:84:9 | validatedString | CommandInjection.swift:87:31:87:31 | validatedString | provenance | | +| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | CommandInjection.swift:87:31:87:31 | validatedString | provenance | | +| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | CommandInjection.swift:84:5:84:9 | let ...? [some:0] | provenance | | +| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:64:22:64:33 | command | provenance | | +| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | provenance | | +| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | provenance | | +| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | CommandInjection.swift:64:22:64:33 | command [some:0] | provenance | | +| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | provenance | | +| CommandInjection.swift:87:24:87:46 | [...] [Collection element] | CommandInjection.swift:87:6:87:6 | [post] task2 | provenance | | +| CommandInjection.swift:87:31:87:31 | validatedString | CommandInjection.swift:87:24:87:46 | [...] [Collection element] | provenance | | +| CommandInjection.swift:99:20:99:40 | arguments [Collection element] | CommandInjection.swift:100:20:100:20 | arguments [Collection element] | provenance | | +| CommandInjection.swift:100:20:100:20 | arguments [Collection element] | CommandInjection.swift:100:3:100:3 | [post] self | provenance | | +| CommandInjection.swift:105:8:105:12 | let ...? [some:0] | CommandInjection.swift:105:12:105:12 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:120:36:120:36 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:121:28:121:28 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:125:45:125:45 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:126:36:126:36 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:131:46:131:46 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:132:22:132:22 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:136:45:136:45 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:137:36:137:36 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:138:21:138:21 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:139:22:139:22 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:150:42:150:42 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:151:75:151:75 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:154:35:154:35 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:155:70:155:70 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:160:53:160:53 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:163:52:163:52 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:164:33:164:33 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:166:57:166:57 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) [some:0] | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:120:36:120:36 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:121:28:121:28 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:125:45:125:45 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:126:36:126:36 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:131:46:131:46 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:132:22:132:22 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:136:45:136:45 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:137:36:137:36 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:138:21:138:21 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:139:22:139:22 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:150:42:150:42 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:151:75:151:75 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:154:35:154:35 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:155:70:155:70 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:160:53:160:53 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:163:52:163:52 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:164:33:164:33 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:166:57:166:57 | userControlledString | provenance | | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:105:8:105:12 | let ...? [some:0] | provenance | | +| CommandInjection.swift:120:24:120:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:120:24:120:57 | ...! | provenance | | +| CommandInjection.swift:120:24:120:57 | ...! | CommandInjection.swift:120:2:120:2 | [post] task3 | provenance | | +| CommandInjection.swift:120:36:120:36 | userControlledString | CommandInjection.swift:120:24:120:56 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:121:20:121:48 | [...] [Collection element] | CommandInjection.swift:121:2:121:2 | [post] task3 | provenance | | +| CommandInjection.swift:121:28:121:28 | userControlledString | CommandInjection.swift:121:20:121:48 | [...] [Collection element] | provenance | | +| CommandInjection.swift:125:24:125:65 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:125:2:125:2 | [post] task4 | provenance | | +| CommandInjection.swift:125:45:125:45 | userControlledString | CommandInjection.swift:125:24:125:65 | call to URL.init(fileURLWithPath:) | provenance | | +| CommandInjection.swift:126:24:126:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:126:24:126:57 | ...! | provenance | | +| CommandInjection.swift:126:24:126:57 | ...! | CommandInjection.swift:126:2:126:2 | [post] task4 | provenance | | +| CommandInjection.swift:126:36:126:36 | userControlledString | CommandInjection.swift:126:24:126:56 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:127:20:127:56 | [...] [Collection element] | CommandInjection.swift:127:2:127:2 | [post] task4 | provenance | | +| CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | CommandInjection.swift:127:20:127:56 | [...] [Collection element] | provenance | | +| CommandInjection.swift:131:25:131:66 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:131:2:131:7 | [post] ...? | provenance | | +| CommandInjection.swift:131:46:131:46 | userControlledString | CommandInjection.swift:131:25:131:66 | call to URL.init(fileURLWithPath:) | provenance | | +| CommandInjection.swift:132:21:132:42 | [...] [Collection element] | CommandInjection.swift:132:2:132:7 | [post] ...? | provenance | | +| CommandInjection.swift:132:22:132:22 | userControlledString | CommandInjection.swift:132:21:132:42 | [...] [Collection element] | provenance | | +| CommandInjection.swift:136:24:136:65 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:136:2:136:2 | [post] task6 | provenance | | +| CommandInjection.swift:136:45:136:45 | userControlledString | CommandInjection.swift:136:24:136:65 | call to URL.init(fileURLWithPath:) | provenance | | +| CommandInjection.swift:137:24:137:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:137:24:137:57 | ...! | provenance | | +| CommandInjection.swift:137:24:137:57 | ...! | CommandInjection.swift:137:2:137:2 | [post] task6 | provenance | | +| CommandInjection.swift:137:36:137:36 | userControlledString | CommandInjection.swift:137:24:137:56 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:138:20:138:41 | [...] [Collection element] | CommandInjection.swift:138:2:138:2 | [post] task6 | provenance | | +| CommandInjection.swift:138:21:138:21 | userControlledString | CommandInjection.swift:138:20:138:41 | [...] [Collection element] | provenance | | +| CommandInjection.swift:139:21:139:42 | [...] [Collection element] | CommandInjection.swift:99:20:99:40 | arguments [Collection element] | provenance | | +| CommandInjection.swift:139:22:139:22 | userControlledString | CommandInjection.swift:139:21:139:42 | [...] [Collection element] | provenance | | +| CommandInjection.swift:151:75:151:75 | userControlledString | CommandInjection.swift:151:67:151:95 | [...] | provenance | | +| CommandInjection.swift:154:23:154:55 | call to URL.init(string:) [some:0] | CommandInjection.swift:154:23:154:56 | ...! | provenance | | +| CommandInjection.swift:154:35:154:35 | userControlledString | CommandInjection.swift:154:23:154:55 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:155:70:155:70 | userControlledString | CommandInjection.swift:155:62:155:90 | [...] | provenance | | +| CommandInjection.swift:160:41:160:73 | call to URL.init(string:) [some:0] | CommandInjection.swift:160:41:160:74 | ...! | provenance | | +| CommandInjection.swift:160:53:160:53 | userControlledString | CommandInjection.swift:160:41:160:73 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | CommandInjection.swift:163:40:163:73 | ...! | provenance | | +| CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | CommandInjection.swift:163:40:163:73 | ...! | provenance | | +| CommandInjection.swift:163:40:163:73 | ...! | file://:0:0:0:0 | url | provenance | | +| CommandInjection.swift:163:52:163:52 | userControlledString | CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:164:33:164:33 | userControlledString | CommandInjection.swift:164:32:164:53 | [...] | provenance | | +| CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | CommandInjection.swift:166:45:166:78 | ...! | provenance | | +| CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | CommandInjection.swift:166:45:166:78 | ...! | provenance | | +| CommandInjection.swift:166:45:166:78 | ...! | file://:0:0:0:0 | url | provenance | | +| CommandInjection.swift:166:57:166:57 | userControlledString | CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | provenance | | +| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | CommandInjection.swift:194:19:194:19 | newValue [Collection element] | provenance | | +| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | CommandInjection.swift:195:20:195:20 | newValue [Collection element] | provenance | | +| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | CommandInjection.swift:196:19:196:19 | newValue [Collection element] | provenance | | +| CommandInjection.swift:194:19:194:19 | newValue [Collection element] | CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | provenance | | +| CommandInjection.swift:195:20:195:20 | newValue [Collection element] | CommandInjection.swift:195:4:195:6 | [post] ...! | provenance | | +| CommandInjection.swift:196:19:196:19 | newValue [Collection element] | CommandInjection.swift:196:4:196:4 | [post] ...! | provenance | | +| CommandInjection.swift:201:9:201:13 | let ...? [some:0] | CommandInjection.swift:201:13:201:13 | userControlledString | provenance | | +| CommandInjection.swift:201:13:201:13 | userControlledString | CommandInjection.swift:205:19:205:19 | userControlledString | provenance | | +| CommandInjection.swift:201:13:201:13 | userControlledString | CommandInjection.swift:211:31:211:31 | userControlledString | provenance | | +| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) [some:0] | provenance | | +| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:205:19:205:19 | userControlledString | provenance | | +| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:211:31:211:31 | userControlledString | provenance | | +| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:201:9:201:13 | let ...? [some:0] | provenance | | +| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | CommandInjection.swift:207:18:207:18 | tainted1 [Collection element] | provenance | | +| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | CommandInjection.swift:208:19:208:19 | tainted1 [Collection element] | provenance | | +| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | CommandInjection.swift:209:18:209:18 | tainted1 [Collection element] | provenance | | +| CommandInjection.swift:205:19:205:19 | userControlledString | CommandInjection.swift:205:18:205:39 | [...] [Collection element] | provenance | | +| CommandInjection.swift:207:18:207:18 | tainted1 [Collection element] | CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | provenance | | +| CommandInjection.swift:208:19:208:19 | tainted1 [Collection element] | CommandInjection.swift:208:3:208:5 | [post] ...! | provenance | | +| CommandInjection.swift:209:18:209:18 | tainted1 [Collection element] | CommandInjection.swift:209:3:209:3 | [post] ...! | provenance | | +| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:213:18:213:18 | tainted2 [Collection element] | provenance | | +| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:214:19:214:19 | tainted2 [Collection element] | provenance | | +| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:215:18:215:18 | tainted2 [Collection element] | provenance | | +| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:217:13:217:13 | tainted2 [Collection element] | provenance | | +| CommandInjection.swift:211:31:211:31 | userControlledString | CommandInjection.swift:211:30:211:51 | [...] [Collection element] | provenance | | +| CommandInjection.swift:213:18:213:18 | tainted2 [Collection element] | CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | provenance | | +| CommandInjection.swift:214:19:214:19 | tainted2 [Collection element] | CommandInjection.swift:214:3:214:5 | [post] ...! | provenance | | +| CommandInjection.swift:215:18:215:18 | tainted2 [Collection element] | CommandInjection.swift:215:3:215:3 | [post] ...! | provenance | | +| CommandInjection.swift:217:13:217:13 | tainted2 [Collection element] | CommandInjection.swift:193:3:193:3 | newValue [Collection element] | provenance | | +| file://:0:0:0:0 | url | file://:0:0:0:0 | url | provenance | | +| file://:0:0:0:0 | url | file://:0:0:0:0 | url | provenance | | +nodes +| CommandInjection.swift:64:22:64:33 | command | semmle.label | command | +| CommandInjection.swift:64:22:64:33 | command [some:0] | semmle.label | command [some:0] | +| CommandInjection.swift:68:16:68:16 | command | semmle.label | command | +| CommandInjection.swift:68:16:68:16 | command [some:0] | semmle.label | command [some:0] | +| CommandInjection.swift:68:16:68:16 | command [some:0] | semmle.label | command [some:0] | +| CommandInjection.swift:75:8:75:12 | let ...? [some:0, some:0] | semmle.label | let ...? [some:0, some:0] | +| CommandInjection.swift:75:8:75:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:75:12:75:12 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:75:12:75:12 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0, some:0] | semmle.label | call to String.init(contentsOf:) [some:0, some:0] | +| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:81:2:81:2 | [post] task1 | semmle.label | [post] task1 | +| CommandInjection.swift:81:20:81:47 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:81:27:81:27 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:84:5:84:9 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:84:9:84:9 | validatedString | semmle.label | validatedString | +| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | semmle.label | call to validateCommand(_:) | +| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | semmle.label | call to validateCommand(_:) [some:0] | +| CommandInjection.swift:84:43:84:43 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | +| CommandInjection.swift:87:6:87:6 | [post] task2 | semmle.label | [post] task2 | +| CommandInjection.swift:87:24:87:46 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:87:31:87:31 | validatedString | semmle.label | validatedString | +| CommandInjection.swift:99:20:99:40 | arguments [Collection element] | semmle.label | arguments [Collection element] | +| CommandInjection.swift:100:3:100:3 | [post] self | semmle.label | [post] self | +| CommandInjection.swift:100:20:100:20 | arguments [Collection element] | semmle.label | arguments [Collection element] | +| CommandInjection.swift:105:8:105:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:105:12:105:12 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:120:2:120:2 | [post] task3 | semmle.label | [post] task3 | +| CommandInjection.swift:120:24:120:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:120:24:120:57 | ...! | semmle.label | ...! | +| CommandInjection.swift:120:36:120:36 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:121:2:121:2 | [post] task3 | semmle.label | [post] task3 | +| CommandInjection.swift:121:20:121:48 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:121:28:121:28 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:125:2:125:2 | [post] task4 | semmle.label | [post] task4 | +| CommandInjection.swift:125:24:125:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:125:45:125:45 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:126:2:126:2 | [post] task4 | semmle.label | [post] task4 | +| CommandInjection.swift:126:24:126:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:126:24:126:57 | ...! | semmle.label | ...! | +| CommandInjection.swift:126:36:126:36 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:127:2:127:2 | [post] task4 | semmle.label | [post] task4 | +| CommandInjection.swift:127:20:127:56 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | +| CommandInjection.swift:131:2:131:7 | [post] ...? | semmle.label | [post] ...? | +| CommandInjection.swift:131:25:131:66 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:131:46:131:46 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:132:2:132:7 | [post] ...? | semmle.label | [post] ...? | +| CommandInjection.swift:132:21:132:42 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:132:22:132:22 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:136:2:136:2 | [post] task6 | semmle.label | [post] task6 | +| CommandInjection.swift:136:24:136:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:136:45:136:45 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:137:2:137:2 | [post] task6 | semmle.label | [post] task6 | +| CommandInjection.swift:137:24:137:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:137:24:137:57 | ...! | semmle.label | ...! | +| CommandInjection.swift:137:36:137:36 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:138:2:138:2 | [post] task6 | semmle.label | [post] task6 | +| CommandInjection.swift:138:20:138:41 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:138:21:138:21 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:139:21:139:42 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:139:22:139:22 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:150:42:150:42 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:151:67:151:95 | [...] | semmle.label | [...] | +| CommandInjection.swift:151:75:151:75 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:154:23:154:55 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:154:23:154:56 | ...! | semmle.label | ...! | +| CommandInjection.swift:154:35:154:35 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:155:62:155:90 | [...] | semmle.label | [...] | +| CommandInjection.swift:155:70:155:70 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:160:41:160:73 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:160:41:160:74 | ...! | semmle.label | ...! | +| CommandInjection.swift:160:53:160:53 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:163:40:163:73 | ...! | semmle.label | ...! | +| CommandInjection.swift:163:40:163:73 | ...! | semmle.label | ...! | +| CommandInjection.swift:163:52:163:52 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:164:32:164:53 | [...] | semmle.label | [...] | +| CommandInjection.swift:164:33:164:33 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:166:45:166:78 | ...! | semmle.label | ...! | +| CommandInjection.swift:166:45:166:78 | ...! | semmle.label | ...! | +| CommandInjection.swift:166:57:166:57 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | semmle.label | newValue [Collection element] | +| CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | +| CommandInjection.swift:194:19:194:19 | newValue [Collection element] | semmle.label | newValue [Collection element] | +| CommandInjection.swift:195:4:195:6 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:195:20:195:20 | newValue [Collection element] | semmle.label | newValue [Collection element] | +| CommandInjection.swift:196:4:196:4 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:196:19:196:19 | newValue [Collection element] | semmle.label | newValue [Collection element] | +| CommandInjection.swift:201:9:201:13 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:201:13:201:13 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:205:19:205:19 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | +| CommandInjection.swift:207:18:207:18 | tainted1 [Collection element] | semmle.label | tainted1 [Collection element] | +| CommandInjection.swift:208:3:208:5 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:208:19:208:19 | tainted1 [Collection element] | semmle.label | tainted1 [Collection element] | +| CommandInjection.swift:209:3:209:3 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:209:18:209:18 | tainted1 [Collection element] | semmle.label | tainted1 [Collection element] | +| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | semmle.label | [...] [Collection element] | +| CommandInjection.swift:211:31:211:31 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | +| CommandInjection.swift:213:18:213:18 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | +| CommandInjection.swift:214:3:214:5 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:214:19:214:19 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | +| CommandInjection.swift:215:3:215:3 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:215:18:215:18 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | +| CommandInjection.swift:217:13:217:13 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | +| file://:0:0:0:0 | url | semmle.label | url | +| file://:0:0:0:0 | url | semmle.label | url | +| file://:0:0:0:0 | url | semmle.label | url | +| file://:0:0:0:0 | url | semmle.label | url | +subpaths +| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:64:22:64:33 | command | CommandInjection.swift:68:16:68:16 | command | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | +| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:64:22:64:33 | command | CommandInjection.swift:68:16:68:16 | command [some:0] | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | +| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | CommandInjection.swift:64:22:64:33 | command [some:0] | CommandInjection.swift:68:16:68:16 | command [some:0] | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | +#select +| CommandInjection.swift:81:2:81:2 | [post] task1 | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:81:2:81:2 | [post] task1 | This command depends on a $@. | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:87:6:87:6 | [post] task2 | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:87:6:87:6 | [post] task2 | This command depends on a $@. | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:100:3:100:3 | [post] self | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:100:3:100:3 | [post] self | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:120:2:120:2 | [post] task3 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:120:2:120:2 | [post] task3 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:121:2:121:2 | [post] task3 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:121:2:121:2 | [post] task3 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:125:2:125:2 | [post] task4 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:125:2:125:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:126:2:126:2 | [post] task4 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:126:2:126:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:127:2:127:2 | [post] task4 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:127:2:127:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:131:2:131:7 | [post] ...? | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:131:2:131:7 | [post] ...? | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:132:2:132:7 | [post] ...? | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:132:2:132:7 | [post] ...? | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:136:2:136:2 | [post] task6 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:136:2:136:2 | [post] task6 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:137:2:137:2 | [post] task6 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:137:2:137:2 | [post] task6 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:138:2:138:2 | [post] task6 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:138:2:138:2 | [post] task6 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:150:42:150:42 | userControlledString | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:150:42:150:42 | userControlledString | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:151:67:151:95 | [...] | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:151:67:151:95 | [...] | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:154:23:154:56 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:154:23:154:56 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:155:62:155:90 | [...] | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:155:62:155:90 | [...] | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:160:41:160:74 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:160:41:160:74 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:163:40:163:73 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:163:40:163:73 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:164:32:164:53 | [...] | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:164:32:164:53 | [...] | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:166:45:166:78 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:166:45:166:78 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:195:4:195:6 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:195:4:195:6 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:196:4:196:4 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:196:4:196:4 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:208:3:208:5 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:208:3:208:5 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:209:3:209:3 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:209:3:209:3 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:214:3:214:5 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:214:3:214:5 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:215:3:215:3 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:215:3:215:3 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | +| file://:0:0:0:0 | url | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | file://:0:0:0:0 | url | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | +| file://:0:0:0:0 | url | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | file://:0:0:0:0 | url | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref new file mode 100644 index 000000000000..87be59af70b3 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref @@ -0,0 +1 @@ +queries/Security/CWE-078/CommandInjection.ql \ No newline at end of file diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift new file mode 100644 index 000000000000..4ef0ac58aed7 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift @@ -0,0 +1,219 @@ + +// --- stubs --- + +struct URL +{ + init?(string: String) {} + init(fileURLWithPath: String) {} +} + +class NSObject { +} + +class Process : NSObject { + var launchPath: String? { get { nil } set {} } + var arguments: [String]? { get { nil } set {} } + func launch() {} + + var executableURL: URL? { get { nil } set {} } + func run() throws {} + + class func launchedProcess(launchPath path: String, arguments: [String]) -> Process { + return Process() + } + + class func run(_ url: URL, arguments: [String], terminationHandler: (@Sendable (Process) -> Void)? = nil) throws -> Process { + return Process() + } +} + +extension String { + init(contentsOf url: URL) throws { + self.init("") + } +} + +class NSUserScriptTask : NSObject { + typealias CompletionHandler = @Sendable (Error?) -> Void + + init(url: URL) throws {} + func execute(completionHandler handler: NSUserScriptTask.CompletionHandler? = nil) { } +} + +class NSUserUnixTask : NSUserScriptTask { + func execute(withArguments arguments: [String]?, completionHandler handler: NSUserScriptTask.CompletionHandler? = nil) { } +} + +protocol NSSecureCoding { +} + +class NSUserAutomatorTask : NSUserScriptTask { + func execute(withInput input: NSSecureCoding?, completionHandler handler: NSUserScriptTask.CompletionHandler? = nil) { } + + var variables: [String: Any]? { get { return nil } set { } } +} + +class FileManager : NSObject { + class var `default`: FileManager { get { return 0 as! FileManager } } + + func contentsOfDirectory(atPath path: String) throws -> [String] { [] } +} + +// --- tests --- + +func validateCommand(_ command: String) -> String? { + let allowedCommands = ["ls -l", "pwd", "echo"] + + if allowedCommands.contains(command) { + return command + } + + return nil +} + +func testCommandInjectionQhelpExamples() { + guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { + return + } + + let task1 = Process() + task1.launchPath = "/bin/bash" // GOOD + task1.arguments = ["-c", userControlledString] // BAD + task1.launch() + + if let validatedString = validateCommand(userControlledString) { + let task2 = Process() + task2.launchPath = "/bin/bash" // GOOD + task2.arguments = ["-c", validatedString] // GOOD [FALSE POSITIVE] + task2.launch() + } +} + +func mkProcess() -> Process? { + return Process() +} + +class MyProcess : Process { + var harmlessField: String? + + func setArguments(_ arguments: [String]) { + self.arguments = arguments // BAD + } +} + +func testCommandInjectionMore(mySafeString: String) { + guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { + return + } + + let task1 = Process() + task1.executableURL = URL(string: mySafeString)! // GOOD + task1.arguments = ["abc"] // GOOD + try! task1.run() + + let task2 = Process() + task2.executableURL = URL(fileURLWithPath: mySafeString) // GOOD + task2.arguments = ["abc", "def"] // GOOD + try! task2.run() + + let task3 = Process() + task3.executableURL = URL(string: userControlledString)! // BAD + task3.arguments = ["abc", userControlledString] // BAD + try! task3.run() + + let task4 = Process() + task4.executableURL = URL(fileURLWithPath: userControlledString) // BAD + task4.executableURL = URL(string: userControlledString)! // BAD + task4.arguments = ["abc", "def" + userControlledString] // BAD + try! task4.run() + + let task5 = mkProcess() + task5?.executableURL = URL(fileURLWithPath: userControlledString) // BAD + task5?.arguments = [userControlledString] // BAD + try! task5?.run() + + let task6 = MyProcess() + task6.executableURL = URL(fileURLWithPath: userControlledString) // BAD + task6.executableURL = URL(string: userControlledString)! // BAD + task6.arguments = [userControlledString] // BAD + task6.setArguments([userControlledString]) // BAD (flagged inside `setArguments`) + task6.harmlessField = userControlledString // GOOD + try! task6.run() + + let task7 = Process() + task7.executableURL = URL(fileURLWithPath: mySafeString) // GOOD + task7.arguments = ["abc", "def"] + task7.arguments?.append(userControlledString) // BAD [NOT DETECTED] + try! task7.run() + + _ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", mySafeString]) // GOOD + _ = Process.launchedProcess(launchPath: userControlledString, arguments: ["abc", mySafeString]) // BAD + _ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // BAD + + _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", mySafeString]) // GOOD + _ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // BAD + _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // BAD + + let task8 = try! NSUserScriptTask(url: URL(string: mySafeString)!) // GOOD + task8.execute() + + let task9 = try! NSUserScriptTask(url: URL(string: userControlledString)!) // BAD + task9.execute() + + let task10 = try! NSUserUnixTask(url: URL(string: userControlledString)!) // BAD + task10.execute(withArguments: [userControlledString]) // BAD + + let task11 = try! NSUserAutomatorTask(url: URL(string: userControlledString)!) // BAD + task11.variables = ["abc": userControlledString] // BAD [NOT DETECTED] + task11.execute(withInput: nil) + + let files = try! FileManager.default.contentsOfDirectory(atPath: "some/directory") + for file in files { + let task12 = Process() + task12.launchPath = "/bin/rm" // GOOD + task12.arguments = [file] // GOOD (cases like this vary, but our analysis doesn't work well on them) + task12.launch() + task12.arguments = files // GOOD (similar to previous) + task12.launch() + task12.arguments = [files[0]] // GOOD (similar to previous) + task12.launch() + } + +} + +struct MyClass { + let p1 : Process + let p2 : Process? + let p3 : Process! + + var myValue: [String]? { + get { + return nil + } + set { + p1.arguments = newValue // BAD + p2!.arguments = newValue // BAD + p3.arguments = newValue // BAD + } + } + + mutating func myFunc() { + guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { + return + } + + let tainted1 = [userControlledString] + + p1.arguments = tainted1 // BAD + p2!.arguments = tainted1 // BAD + p3.arguments = tainted1 // BAD + + let tainted2 : [String]? = [userControlledString] + + p1.arguments = tainted2 // BAD + p2!.arguments = tainted2 // BAD + p3.arguments = tainted2 // BAD + + myValue = tainted2 + } +} From 4a3124e0d19f24d4b13fb4e14fcc7f3ab1fd06f1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:58:13 +0100 Subject: [PATCH 2/7] unified: copy original Swift command injection configuration and models Inline CommandInjectionQuery.qll and CommandInjectionExtensions.qll in the query, preserving their declarations and required shared imports. Keep this source-collection step separate from the unified API port; the query is not expected to compile yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../security/CWE-078/CommandInjection.ql | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql index defa939cf68b..5d37c2a6f057 100644 --- a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql @@ -14,9 +14,107 @@ import swift import codeql.swift.dataflow.DataFlow -import codeql.swift.security.CommandInjectionQuery +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import codeql.swift.dataflow.ExternalFlow import CommandInjectionFlow::PathGraph +// Copied from codeql/swift/security/CommandInjectionExtensions.qll. +/** + * Provides classes and predicates for reasoning about system + * commands built from user-controlled sources (that is, command injection + * vulnerabilities). + */ + +/** + * A dataflow sink for command injection vulnerabilities. + */ +abstract class CommandInjectionSink extends DataFlow::Node { } + +/** + * A barrier for command injection vulnerabilities. + */ +abstract class CommandInjectionBarrier extends DataFlow::Node { } + +/** + * A unit class for adding additional flow steps. + */ +class CommandInjectionAdditionalFlowStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a flow + * step for paths related to command injection vulnerabilities. + */ + abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); +} + +/** + * A sink defined in a CSV model. + */ +private class DefaultCommandInjectionSink extends CommandInjectionSink { + DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } +} + +private class CommandInjectionSinks extends SinkModelCsv { + override predicate row(string row) { + row = + [ + ";Process;true;run(_:arguments:terminationHandler:);;;Argument[0..1];command-injection", + ";Process;true;launchedProcess(launchPath:arguments:);;;Argument[0..1];command-injection", + ";Process;true;arguments;;;PostUpdate;command-injection", + ";Process;true;currentDirectory;;;PostUpdate;command-injection", + ";Process;true;environment;;;PostUpdate;command-injection", + ";Process;true;executableURL;;;PostUpdate;command-injection", + ";Process;true;standardError;;;PostUpdate;command-injection", + ";Process;true;standardInput;;;PostUpdate;command-injection", + ";Process;true;standardOutput;;;PostUpdate;command-injection", + ";Process;true;currentDirectoryPath;;;PostUpdate;command-injection", + ";Process;true;launchPath;;;PostUpdate;command-injection", + ";NSUserScriptTask;true;init(url:);;;Argument[0];command-injection", + ";NSUserUnixTask;true;execute(withArguments:completionHandler:);;;Argument[0];command-injection", + ] + } +} + +/** + * A barrier for command injection vulnerabilities. + */ +private class CommandInjectionDefaultBarrier extends CommandInjectionBarrier { + CommandInjectionDefaultBarrier() { + // any numeric type + this.asExpr().getType().getUnderlyingType().getABaseType*().getName() = + ["Numeric", "SignedInteger", "UnsignedInteger"] + } +} + +// Copied from codeql/swift/security/CommandInjectionQuery.qll. +/** + * Provides a taint-tracking configuration for reasoning about system + * commands built from user-controlled sources (that is, command injection + * vulnerabilities). + */ + +/** + * A taint configuration for tainted data that reaches a command injection sink. + */ +module CommandInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + + predicate isSink(DataFlow::Node node) { node instanceof CommandInjectionSink } + + predicate isBarrier(DataFlow::Node barrier) { barrier instanceof CommandInjectionBarrier } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + any(CommandInjectionAdditionalFlowStep s).step(nodeFrom, nodeTo) + } + + predicate observeDiffInformedIncrementalMode() { any() } +} + +/** + * Detect taint flow of tainted data that reaches a command injection sink. + */ +module CommandInjectionFlow = TaintTracking::Global; + from CommandInjectionFlow::PathNode sourceNode, CommandInjectionFlow::PathNode sinkNode where CommandInjectionFlow::flowPath(sourceNode, sinkNode) select sinkNode.getNode(), sourceNode, sinkNode, "This command depends on a $@.", From d9cdc4726df5fe4e7948ab47ce5048f9a4036678 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:05:41 +0100 Subject: [PATCH 3/7] unified: port command injection query to a compiling baseline Use unified taint tracking and the existing command-injection models, update the query ID and test reference, and enable inline expectation checks. Leave TODOs for Process property sinks and the original numeric barrier. Learn the focused regression test and annotate missing behavior explicitly. The query currently reports two alerts with no inline expectation failures; further modeling and flow improvements are left for follow-up commits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../security/CWE-078/CommandInjection.ql | 107 +----- .../CommandInjection.expected | 313 +----------------- .../CommandInjection/CommandInjection.qlref | 4 +- .../CommandInjection/CommandInjection.swift | 68 ++-- 4 files changed, 69 insertions(+), 423 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql index 5d37c2a6f057..54a4394dff4f 100644 --- a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql @@ -6,105 +6,28 @@ * @problem.severity error * @security-severity 9.8 * @precision high - * @id swift/command-line-injection + * @id unified/swift/command-line-injection * @tags security * external/cwe/cwe-078 * external/cwe/cwe-088 */ -import swift -import codeql.swift.dataflow.DataFlow -import codeql.swift.dataflow.TaintTracking -import codeql.swift.dataflow.FlowSources -import codeql.swift.dataflow.ExternalFlow -import CommandInjectionFlow::PathGraph - -// Copied from codeql/swift/security/CommandInjectionExtensions.qll. -/** - * Provides classes and predicates for reasoning about system - * commands built from user-controlled sources (that is, command injection - * vulnerabilities). - */ - -/** - * A dataflow sink for command injection vulnerabilities. - */ -abstract class CommandInjectionSink extends DataFlow::Node { } - -/** - * A barrier for command injection vulnerabilities. - */ -abstract class CommandInjectionBarrier extends DataFlow::Node { } - -/** - * A unit class for adding additional flow steps. - */ -class CommandInjectionAdditionalFlowStep extends Unit { - /** - * Holds if the step from `node1` to `node2` should be considered a flow - * step for paths related to command injection vulnerabilities. - */ - abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); -} - -/** - * A sink defined in a CSV model. - */ -private class DefaultCommandInjectionSink extends CommandInjectionSink { - DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } -} - -private class CommandInjectionSinks extends SinkModelCsv { - override predicate row(string row) { - row = - [ - ";Process;true;run(_:arguments:terminationHandler:);;;Argument[0..1];command-injection", - ";Process;true;launchedProcess(launchPath:arguments:);;;Argument[0..1];command-injection", - ";Process;true;arguments;;;PostUpdate;command-injection", - ";Process;true;currentDirectory;;;PostUpdate;command-injection", - ";Process;true;environment;;;PostUpdate;command-injection", - ";Process;true;executableURL;;;PostUpdate;command-injection", - ";Process;true;standardError;;;PostUpdate;command-injection", - ";Process;true;standardInput;;;PostUpdate;command-injection", - ";Process;true;standardOutput;;;PostUpdate;command-injection", - ";Process;true;currentDirectoryPath;;;PostUpdate;command-injection", - ";Process;true;launchPath;;;PostUpdate;command-injection", - ";NSUserScriptTask;true;init(url:);;;Argument[0];command-injection", - ";NSUserUnixTask;true;execute(withArguments:completionHandler:);;;Argument[0];command-injection", - ] - } -} - -/** - * A barrier for command injection vulnerabilities. - */ -private class CommandInjectionDefaultBarrier extends CommandInjectionBarrier { - CommandInjectionDefaultBarrier() { - // any numeric type - this.asExpr().getType().getUnderlyingType().getABaseType*().getName() = - ["Numeric", "SignedInteger", "UnsignedInteger"] - } -} - -// Copied from codeql/swift/security/CommandInjectionQuery.qll. -/** - * Provides a taint-tracking configuration for reasoning about system - * commands built from user-controlled sources (that is, command injection - * vulnerabilities). - */ +import unified /** * A taint configuration for tainted data that reaches a command injection sink. */ module CommandInjectionConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + predicate isSource(DataFlow::Node node) { Models::isSource(node, _) } - predicate isSink(DataFlow::Node node) { node instanceof CommandInjectionSink } - - predicate isBarrier(DataFlow::Node barrier) { barrier instanceof CommandInjectionBarrier } + predicate isSink(DataFlow::Node node) { + // TODO: Support Process property-write sinks from original Swift's CommandInjectionExtensions.qll. + Models::isSink(node, "command-injection") + } - predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - any(CommandInjectionAdditionalFlowStep s).step(nodeFrom, nodeTo) + predicate isBarrier(DataFlow::Node node) { + // TODO: Port the numeric-type barrier from original Swift's CommandInjectionExtensions.qll. + none() } predicate observeDiffInformedIncrementalMode() { any() } @@ -115,7 +38,9 @@ module CommandInjectionConfig implements DataFlow::ConfigSig { */ module CommandInjectionFlow = TaintTracking::Global; -from CommandInjectionFlow::PathNode sourceNode, CommandInjectionFlow::PathNode sinkNode -where CommandInjectionFlow::flowPath(sourceNode, sinkNode) -select sinkNode.getNode(), sourceNode, sinkNode, "This command depends on a $@.", - sourceNode.getNode(), "user-provided value" +import CommandInjectionFlow::PathGraph + +from CommandInjectionFlow::PathNode source, CommandInjectionFlow::PathNode sink +where CommandInjectionFlow::flowPath(source, sink) +select sink.getNode(), source, sink, "This command depends on a $@.", source.getNode(), + "user-provided value" diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected index 6e6b86231495..2a0d50ee5f7d 100644 --- a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected @@ -1,300 +1,19 @@ +#select +| CommandInjection.swift:150:42:150:61 | userControlledString | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:150:42:150:61 | userControlledString | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | +| CommandInjection.swift:160:41:160:74 | ... ! | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:160:41:160:74 | ... ! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | edges -| CommandInjection.swift:64:22:64:33 | command | CommandInjection.swift:68:16:68:16 | command | provenance | | -| CommandInjection.swift:64:22:64:33 | command [some:0] | CommandInjection.swift:68:16:68:16 | command [some:0] | provenance | | -| CommandInjection.swift:68:16:68:16 | command | CommandInjection.swift:68:16:68:16 | command [some:0] | provenance | | -| CommandInjection.swift:75:8:75:12 | let ...? [some:0, some:0] | CommandInjection.swift:75:12:75:12 | userControlledString [some:0] | provenance | | -| CommandInjection.swift:75:8:75:12 | let ...? [some:0] | CommandInjection.swift:75:12:75:12 | userControlledString | provenance | | -| CommandInjection.swift:75:12:75:12 | userControlledString | CommandInjection.swift:81:27:81:27 | userControlledString | provenance | | -| CommandInjection.swift:75:12:75:12 | userControlledString | CommandInjection.swift:84:43:84:43 | userControlledString | provenance | | -| CommandInjection.swift:75:12:75:12 | userControlledString [some:0] | CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:81:27:81:27 | userControlledString | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:84:43:84:43 | userControlledString | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0, some:0] | CommandInjection.swift:75:8:75:12 | let ...? [some:0, some:0] | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:75:8:75:12 | let ...? [some:0] | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0, some:0] | provenance | | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | provenance | | -| CommandInjection.swift:81:20:81:47 | [...] [Collection element] | CommandInjection.swift:81:2:81:2 | [post] task1 | provenance | | -| CommandInjection.swift:81:27:81:27 | userControlledString | CommandInjection.swift:81:20:81:47 | [...] [Collection element] | provenance | | -| CommandInjection.swift:84:5:84:9 | let ...? [some:0] | CommandInjection.swift:84:9:84:9 | validatedString | provenance | | -| CommandInjection.swift:84:9:84:9 | validatedString | CommandInjection.swift:87:31:87:31 | validatedString | provenance | | -| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | CommandInjection.swift:87:31:87:31 | validatedString | provenance | | -| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | CommandInjection.swift:84:5:84:9 | let ...? [some:0] | provenance | | -| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:64:22:64:33 | command | provenance | | -| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | provenance | | -| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | provenance | | -| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | CommandInjection.swift:64:22:64:33 | command [some:0] | provenance | | -| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | provenance | | -| CommandInjection.swift:87:24:87:46 | [...] [Collection element] | CommandInjection.swift:87:6:87:6 | [post] task2 | provenance | | -| CommandInjection.swift:87:31:87:31 | validatedString | CommandInjection.swift:87:24:87:46 | [...] [Collection element] | provenance | | -| CommandInjection.swift:99:20:99:40 | arguments [Collection element] | CommandInjection.swift:100:20:100:20 | arguments [Collection element] | provenance | | -| CommandInjection.swift:100:20:100:20 | arguments [Collection element] | CommandInjection.swift:100:3:100:3 | [post] self | provenance | | -| CommandInjection.swift:105:8:105:12 | let ...? [some:0] | CommandInjection.swift:105:12:105:12 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:120:36:120:36 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:121:28:121:28 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:125:45:125:45 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:126:36:126:36 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:131:46:131:46 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:132:22:132:22 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:136:45:136:45 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:137:36:137:36 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:138:21:138:21 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:139:22:139:22 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:150:42:150:42 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:151:75:151:75 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:154:35:154:35 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:155:70:155:70 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:160:53:160:53 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:163:52:163:52 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:164:33:164:33 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:12 | userControlledString | CommandInjection.swift:166:57:166:57 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) [some:0] | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:120:36:120:36 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:121:28:121:28 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:125:45:125:45 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:126:36:126:36 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:131:46:131:46 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:132:22:132:22 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:136:45:136:45 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:137:36:137:36 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:138:21:138:21 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:139:22:139:22 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:150:42:150:42 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:151:75:151:75 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:154:35:154:35 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:155:70:155:70 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:160:53:160:53 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:163:52:163:52 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:164:33:164:33 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:166:57:166:57 | userControlledString | provenance | | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:105:8:105:12 | let ...? [some:0] | provenance | | -| CommandInjection.swift:120:24:120:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:120:24:120:57 | ...! | provenance | | -| CommandInjection.swift:120:24:120:57 | ...! | CommandInjection.swift:120:2:120:2 | [post] task3 | provenance | | -| CommandInjection.swift:120:36:120:36 | userControlledString | CommandInjection.swift:120:24:120:56 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:121:20:121:48 | [...] [Collection element] | CommandInjection.swift:121:2:121:2 | [post] task3 | provenance | | -| CommandInjection.swift:121:28:121:28 | userControlledString | CommandInjection.swift:121:20:121:48 | [...] [Collection element] | provenance | | -| CommandInjection.swift:125:24:125:65 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:125:2:125:2 | [post] task4 | provenance | | -| CommandInjection.swift:125:45:125:45 | userControlledString | CommandInjection.swift:125:24:125:65 | call to URL.init(fileURLWithPath:) | provenance | | -| CommandInjection.swift:126:24:126:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:126:24:126:57 | ...! | provenance | | -| CommandInjection.swift:126:24:126:57 | ...! | CommandInjection.swift:126:2:126:2 | [post] task4 | provenance | | -| CommandInjection.swift:126:36:126:36 | userControlledString | CommandInjection.swift:126:24:126:56 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:127:20:127:56 | [...] [Collection element] | CommandInjection.swift:127:2:127:2 | [post] task4 | provenance | | -| CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | CommandInjection.swift:127:20:127:56 | [...] [Collection element] | provenance | | -| CommandInjection.swift:131:25:131:66 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:131:2:131:7 | [post] ...? | provenance | | -| CommandInjection.swift:131:46:131:46 | userControlledString | CommandInjection.swift:131:25:131:66 | call to URL.init(fileURLWithPath:) | provenance | | -| CommandInjection.swift:132:21:132:42 | [...] [Collection element] | CommandInjection.swift:132:2:132:7 | [post] ...? | provenance | | -| CommandInjection.swift:132:22:132:22 | userControlledString | CommandInjection.swift:132:21:132:42 | [...] [Collection element] | provenance | | -| CommandInjection.swift:136:24:136:65 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:136:2:136:2 | [post] task6 | provenance | | -| CommandInjection.swift:136:45:136:45 | userControlledString | CommandInjection.swift:136:24:136:65 | call to URL.init(fileURLWithPath:) | provenance | | -| CommandInjection.swift:137:24:137:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:137:24:137:57 | ...! | provenance | | -| CommandInjection.swift:137:24:137:57 | ...! | CommandInjection.swift:137:2:137:2 | [post] task6 | provenance | | -| CommandInjection.swift:137:36:137:36 | userControlledString | CommandInjection.swift:137:24:137:56 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:138:20:138:41 | [...] [Collection element] | CommandInjection.swift:138:2:138:2 | [post] task6 | provenance | | -| CommandInjection.swift:138:21:138:21 | userControlledString | CommandInjection.swift:138:20:138:41 | [...] [Collection element] | provenance | | -| CommandInjection.swift:139:21:139:42 | [...] [Collection element] | CommandInjection.swift:99:20:99:40 | arguments [Collection element] | provenance | | -| CommandInjection.swift:139:22:139:22 | userControlledString | CommandInjection.swift:139:21:139:42 | [...] [Collection element] | provenance | | -| CommandInjection.swift:151:75:151:75 | userControlledString | CommandInjection.swift:151:67:151:95 | [...] | provenance | | -| CommandInjection.swift:154:23:154:55 | call to URL.init(string:) [some:0] | CommandInjection.swift:154:23:154:56 | ...! | provenance | | -| CommandInjection.swift:154:35:154:35 | userControlledString | CommandInjection.swift:154:23:154:55 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:155:70:155:70 | userControlledString | CommandInjection.swift:155:62:155:90 | [...] | provenance | | -| CommandInjection.swift:160:41:160:73 | call to URL.init(string:) [some:0] | CommandInjection.swift:160:41:160:74 | ...! | provenance | | -| CommandInjection.swift:160:53:160:53 | userControlledString | CommandInjection.swift:160:41:160:73 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | CommandInjection.swift:163:40:163:73 | ...! | provenance | | -| CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | CommandInjection.swift:163:40:163:73 | ...! | provenance | | -| CommandInjection.swift:163:40:163:73 | ...! | file://:0:0:0:0 | url | provenance | | -| CommandInjection.swift:163:52:163:52 | userControlledString | CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:164:33:164:33 | userControlledString | CommandInjection.swift:164:32:164:53 | [...] | provenance | | -| CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | CommandInjection.swift:166:45:166:78 | ...! | provenance | | -| CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | CommandInjection.swift:166:45:166:78 | ...! | provenance | | -| CommandInjection.swift:166:45:166:78 | ...! | file://:0:0:0:0 | url | provenance | | -| CommandInjection.swift:166:57:166:57 | userControlledString | CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | provenance | | -| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | CommandInjection.swift:194:19:194:19 | newValue [Collection element] | provenance | | -| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | CommandInjection.swift:195:20:195:20 | newValue [Collection element] | provenance | | -| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | CommandInjection.swift:196:19:196:19 | newValue [Collection element] | provenance | | -| CommandInjection.swift:194:19:194:19 | newValue [Collection element] | CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | provenance | | -| CommandInjection.swift:195:20:195:20 | newValue [Collection element] | CommandInjection.swift:195:4:195:6 | [post] ...! | provenance | | -| CommandInjection.swift:196:19:196:19 | newValue [Collection element] | CommandInjection.swift:196:4:196:4 | [post] ...! | provenance | | -| CommandInjection.swift:201:9:201:13 | let ...? [some:0] | CommandInjection.swift:201:13:201:13 | userControlledString | provenance | | -| CommandInjection.swift:201:13:201:13 | userControlledString | CommandInjection.swift:205:19:205:19 | userControlledString | provenance | | -| CommandInjection.swift:201:13:201:13 | userControlledString | CommandInjection.swift:211:31:211:31 | userControlledString | provenance | | -| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) [some:0] | provenance | | -| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:205:19:205:19 | userControlledString | provenance | | -| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:211:31:211:31 | userControlledString | provenance | | -| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:201:9:201:13 | let ...? [some:0] | provenance | | -| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | CommandInjection.swift:207:18:207:18 | tainted1 [Collection element] | provenance | | -| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | CommandInjection.swift:208:19:208:19 | tainted1 [Collection element] | provenance | | -| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | CommandInjection.swift:209:18:209:18 | tainted1 [Collection element] | provenance | | -| CommandInjection.swift:205:19:205:19 | userControlledString | CommandInjection.swift:205:18:205:39 | [...] [Collection element] | provenance | | -| CommandInjection.swift:207:18:207:18 | tainted1 [Collection element] | CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | provenance | | -| CommandInjection.swift:208:19:208:19 | tainted1 [Collection element] | CommandInjection.swift:208:3:208:5 | [post] ...! | provenance | | -| CommandInjection.swift:209:18:209:18 | tainted1 [Collection element] | CommandInjection.swift:209:3:209:3 | [post] ...! | provenance | | -| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:213:18:213:18 | tainted2 [Collection element] | provenance | | -| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:214:19:214:19 | tainted2 [Collection element] | provenance | | -| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:215:18:215:18 | tainted2 [Collection element] | provenance | | -| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | CommandInjection.swift:217:13:217:13 | tainted2 [Collection element] | provenance | | -| CommandInjection.swift:211:31:211:31 | userControlledString | CommandInjection.swift:211:30:211:51 | [...] [Collection element] | provenance | | -| CommandInjection.swift:213:18:213:18 | tainted2 [Collection element] | CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | provenance | | -| CommandInjection.swift:214:19:214:19 | tainted2 [Collection element] | CommandInjection.swift:214:3:214:5 | [post] ...! | provenance | | -| CommandInjection.swift:215:18:215:18 | tainted2 [Collection element] | CommandInjection.swift:215:3:215:3 | [post] ...! | provenance | | -| CommandInjection.swift:217:13:217:13 | tainted2 [Collection element] | CommandInjection.swift:193:3:193:3 | newValue [Collection element] | provenance | | -| file://:0:0:0:0 | url | file://:0:0:0:0 | url | provenance | | -| file://:0:0:0:0 | url | file://:0:0:0:0 | url | provenance | | +| CommandInjection.swift:105:8:105:31 | ... .some(...) [some.0] | CommandInjection.swift:105:12:105:31 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:150:42:150:61 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:160:53:160:72 | userControlledString | provenance | | +| CommandInjection.swift:105:35:105:94 | try? ... [some.0] | CommandInjection.swift:105:8:105:31 | ... .some(...) [some.0] | provenance | | +| CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:105:35:105:94 | try? ... [some.0] | provenance | | +| CommandInjection.swift:160:53:160:72 | userControlledString | CommandInjection.swift:160:41:160:74 | ... ! | provenance | | nodes -| CommandInjection.swift:64:22:64:33 | command | semmle.label | command | -| CommandInjection.swift:64:22:64:33 | command [some:0] | semmle.label | command [some:0] | -| CommandInjection.swift:68:16:68:16 | command | semmle.label | command | -| CommandInjection.swift:68:16:68:16 | command [some:0] | semmle.label | command [some:0] | -| CommandInjection.swift:68:16:68:16 | command [some:0] | semmle.label | command [some:0] | -| CommandInjection.swift:75:8:75:12 | let ...? [some:0, some:0] | semmle.label | let ...? [some:0, some:0] | -| CommandInjection.swift:75:8:75:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | -| CommandInjection.swift:75:12:75:12 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:75:12:75:12 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0, some:0] | semmle.label | call to String.init(contentsOf:) [some:0, some:0] | -| CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | -| CommandInjection.swift:81:2:81:2 | [post] task1 | semmle.label | [post] task1 | -| CommandInjection.swift:81:20:81:47 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:81:27:81:27 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:84:5:84:9 | let ...? [some:0] | semmle.label | let ...? [some:0] | -| CommandInjection.swift:84:9:84:9 | validatedString | semmle.label | validatedString | -| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | semmle.label | call to validateCommand(_:) | -| CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | semmle.label | call to validateCommand(_:) [some:0] | -| CommandInjection.swift:84:43:84:43 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | -| CommandInjection.swift:87:6:87:6 | [post] task2 | semmle.label | [post] task2 | -| CommandInjection.swift:87:24:87:46 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:87:31:87:31 | validatedString | semmle.label | validatedString | -| CommandInjection.swift:99:20:99:40 | arguments [Collection element] | semmle.label | arguments [Collection element] | -| CommandInjection.swift:100:3:100:3 | [post] self | semmle.label | [post] self | -| CommandInjection.swift:100:20:100:20 | arguments [Collection element] | semmle.label | arguments [Collection element] | -| CommandInjection.swift:105:8:105:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | -| CommandInjection.swift:105:12:105:12 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | -| CommandInjection.swift:120:2:120:2 | [post] task3 | semmle.label | [post] task3 | -| CommandInjection.swift:120:24:120:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:120:24:120:57 | ...! | semmle.label | ...! | -| CommandInjection.swift:120:36:120:36 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:121:2:121:2 | [post] task3 | semmle.label | [post] task3 | -| CommandInjection.swift:121:20:121:48 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:121:28:121:28 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:125:2:125:2 | [post] task4 | semmle.label | [post] task4 | -| CommandInjection.swift:125:24:125:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | -| CommandInjection.swift:125:45:125:45 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:126:2:126:2 | [post] task4 | semmle.label | [post] task4 | -| CommandInjection.swift:126:24:126:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:126:24:126:57 | ...! | semmle.label | ...! | -| CommandInjection.swift:126:36:126:36 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:127:2:127:2 | [post] task4 | semmle.label | [post] task4 | -| CommandInjection.swift:127:20:127:56 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:127:28:127:36 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | -| CommandInjection.swift:131:2:131:7 | [post] ...? | semmle.label | [post] ...? | -| CommandInjection.swift:131:25:131:66 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | -| CommandInjection.swift:131:46:131:46 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:132:2:132:7 | [post] ...? | semmle.label | [post] ...? | -| CommandInjection.swift:132:21:132:42 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:132:22:132:22 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:136:2:136:2 | [post] task6 | semmle.label | [post] task6 | -| CommandInjection.swift:136:24:136:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | -| CommandInjection.swift:136:45:136:45 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:137:2:137:2 | [post] task6 | semmle.label | [post] task6 | -| CommandInjection.swift:137:24:137:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:137:24:137:57 | ...! | semmle.label | ...! | -| CommandInjection.swift:137:36:137:36 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:138:2:138:2 | [post] task6 | semmle.label | [post] task6 | -| CommandInjection.swift:138:20:138:41 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:138:21:138:21 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:139:21:139:42 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:139:22:139:22 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:150:42:150:42 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:151:67:151:95 | [...] | semmle.label | [...] | -| CommandInjection.swift:151:75:151:75 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:154:23:154:55 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:154:23:154:56 | ...! | semmle.label | ...! | -| CommandInjection.swift:154:35:154:35 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:155:62:155:90 | [...] | semmle.label | [...] | -| CommandInjection.swift:155:70:155:70 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:160:41:160:73 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:160:41:160:74 | ...! | semmle.label | ...! | -| CommandInjection.swift:160:53:160:53 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:163:40:163:72 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:163:40:163:73 | ...! | semmle.label | ...! | -| CommandInjection.swift:163:40:163:73 | ...! | semmle.label | ...! | -| CommandInjection.swift:163:52:163:52 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:164:32:164:53 | [...] | semmle.label | [...] | -| CommandInjection.swift:164:33:164:33 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:166:45:166:77 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | -| CommandInjection.swift:166:45:166:78 | ...! | semmle.label | ...! | -| CommandInjection.swift:166:45:166:78 | ...! | semmle.label | ...! | -| CommandInjection.swift:166:57:166:57 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:193:3:193:3 | newValue [Collection element] | semmle.label | newValue [Collection element] | -| CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | -| CommandInjection.swift:194:19:194:19 | newValue [Collection element] | semmle.label | newValue [Collection element] | -| CommandInjection.swift:195:4:195:6 | [post] ...! | semmle.label | [post] ...! | -| CommandInjection.swift:195:20:195:20 | newValue [Collection element] | semmle.label | newValue [Collection element] | -| CommandInjection.swift:196:4:196:4 | [post] ...! | semmle.label | [post] ...! | -| CommandInjection.swift:196:19:196:19 | newValue [Collection element] | semmle.label | newValue [Collection element] | -| CommandInjection.swift:201:9:201:13 | let ...? [some:0] | semmle.label | let ...? [some:0] | -| CommandInjection.swift:201:13:201:13 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | -| CommandInjection.swift:205:18:205:39 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:205:19:205:19 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | -| CommandInjection.swift:207:18:207:18 | tainted1 [Collection element] | semmle.label | tainted1 [Collection element] | -| CommandInjection.swift:208:3:208:5 | [post] ...! | semmle.label | [post] ...! | -| CommandInjection.swift:208:19:208:19 | tainted1 [Collection element] | semmle.label | tainted1 [Collection element] | -| CommandInjection.swift:209:3:209:3 | [post] ...! | semmle.label | [post] ...! | -| CommandInjection.swift:209:18:209:18 | tainted1 [Collection element] | semmle.label | tainted1 [Collection element] | -| CommandInjection.swift:211:30:211:51 | [...] [Collection element] | semmle.label | [...] [Collection element] | -| CommandInjection.swift:211:31:211:31 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | -| CommandInjection.swift:213:18:213:18 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | -| CommandInjection.swift:214:3:214:5 | [post] ...! | semmle.label | [post] ...! | -| CommandInjection.swift:214:19:214:19 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | -| CommandInjection.swift:215:3:215:3 | [post] ...! | semmle.label | [post] ...! | -| CommandInjection.swift:215:18:215:18 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | -| CommandInjection.swift:217:13:217:13 | tainted2 [Collection element] | semmle.label | tainted2 [Collection element] | -| file://:0:0:0:0 | url | semmle.label | url | -| file://:0:0:0:0 | url | semmle.label | url | -| file://:0:0:0:0 | url | semmle.label | url | -| file://:0:0:0:0 | url | semmle.label | url | +| CommandInjection.swift:105:8:105:31 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| CommandInjection.swift:105:12:105:31 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:105:35:105:94 | try? ... [some.0] | semmle.label | try? ... [some.0] | +| CommandInjection.swift:105:40:105:94 | String(...) | semmle.label | String(...) | +| CommandInjection.swift:150:42:150:61 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:160:41:160:74 | ... ! | semmle.label | ... ! | +| CommandInjection.swift:160:53:160:72 | userControlledString | semmle.label | userControlledString | subpaths -| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:64:22:64:33 | command | CommandInjection.swift:68:16:68:16 | command | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) | -| CommandInjection.swift:84:43:84:43 | userControlledString | CommandInjection.swift:64:22:64:33 | command | CommandInjection.swift:68:16:68:16 | command [some:0] | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | -| CommandInjection.swift:84:43:84:43 | userControlledString [some:0] | CommandInjection.swift:64:22:64:33 | command [some:0] | CommandInjection.swift:68:16:68:16 | command [some:0] | CommandInjection.swift:84:27:84:63 | call to validateCommand(_:) [some:0] | -#select -| CommandInjection.swift:81:2:81:2 | [post] task1 | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:81:2:81:2 | [post] task1 | This command depends on a $@. | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:87:6:87:6 | [post] task2 | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | CommandInjection.swift:87:6:87:6 | [post] task2 | This command depends on a $@. | CommandInjection.swift:75:40:75:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:100:3:100:3 | [post] self | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:100:3:100:3 | [post] self | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:120:2:120:2 | [post] task3 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:120:2:120:2 | [post] task3 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:121:2:121:2 | [post] task3 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:121:2:121:2 | [post] task3 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:125:2:125:2 | [post] task4 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:125:2:125:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:126:2:126:2 | [post] task4 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:126:2:126:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:127:2:127:2 | [post] task4 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:127:2:127:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:131:2:131:7 | [post] ...? | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:131:2:131:7 | [post] ...? | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:132:2:132:7 | [post] ...? | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:132:2:132:7 | [post] ...? | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:136:2:136:2 | [post] task6 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:136:2:136:2 | [post] task6 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:137:2:137:2 | [post] task6 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:137:2:137:2 | [post] task6 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:138:2:138:2 | [post] task6 | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:138:2:138:2 | [post] task6 | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:150:42:150:42 | userControlledString | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:150:42:150:42 | userControlledString | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:151:67:151:95 | [...] | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:151:67:151:95 | [...] | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:154:23:154:56 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:154:23:154:56 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:155:62:155:90 | [...] | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:155:62:155:90 | [...] | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:160:41:160:74 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:160:41:160:74 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:163:40:163:73 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:163:40:163:73 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:164:32:164:53 | [...] | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:164:32:164:53 | [...] | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:166:45:166:78 | ...! | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | CommandInjection.swift:166:45:166:78 | ...! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:194:4:194:4 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:195:4:195:6 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:195:4:195:6 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:196:4:196:4 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:196:4:196:4 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:207:3:207:3 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:208:3:208:5 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:208:3:208:5 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:209:3:209:3 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:209:3:209:3 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:213:3:213:3 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:214:3:214:5 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:214:3:214:5 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| CommandInjection.swift:215:3:215:3 | [post] ...! | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | CommandInjection.swift:215:3:215:3 | [post] ...! | This command depends on a $@. | CommandInjection.swift:201:41:201:95 | call to String.init(contentsOf:) | user-provided value | -| file://:0:0:0:0 | url | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | file://:0:0:0:0 | url | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | -| file://:0:0:0:0 | url | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | file://:0:0:0:0 | url | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | call to String.init(contentsOf:) | user-provided value | diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref index 87be59af70b3..330ea2d5bede 100644 --- a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.qlref @@ -1 +1,3 @@ -queries/Security/CWE-078/CommandInjection.ql \ No newline at end of file +query: queries/security/CWE-078/CommandInjection.ql +postprocess: + - utils/test/InlineExpectationsTestQuery.ql \ No newline at end of file diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift index 4ef0ac58aed7..e70d80a9dc3b 100644 --- a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift @@ -72,13 +72,13 @@ func validateCommand(_ command: String) -> String? { } func testCommandInjectionQhelpExamples() { - guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { + guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { // $ MISSING: Source return } let task1 = Process() task1.launchPath = "/bin/bash" // GOOD - task1.arguments = ["-c", userControlledString] // BAD + task1.arguments = ["-c", userControlledString] // $ MISSING: Alert task1.launch() if let validatedString = validateCommand(userControlledString) { @@ -97,12 +97,12 @@ class MyProcess : Process { var harmlessField: String? func setArguments(_ arguments: [String]) { - self.arguments = arguments // BAD + self.arguments = arguments // $ MISSING: Alert } } func testCommandInjectionMore(mySafeString: String) { - guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { + guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { // $ Source return } @@ -117,25 +117,25 @@ func testCommandInjectionMore(mySafeString: String) { try! task2.run() let task3 = Process() - task3.executableURL = URL(string: userControlledString)! // BAD - task3.arguments = ["abc", userControlledString] // BAD + task3.executableURL = URL(string: userControlledString)! // $ MISSING: Alert + task3.arguments = ["abc", userControlledString] // $ MISSING: Alert try! task3.run() let task4 = Process() - task4.executableURL = URL(fileURLWithPath: userControlledString) // BAD - task4.executableURL = URL(string: userControlledString)! // BAD - task4.arguments = ["abc", "def" + userControlledString] // BAD + task4.executableURL = URL(fileURLWithPath: userControlledString) // $ MISSING: Alert + task4.executableURL = URL(string: userControlledString)! // $ MISSING: Alert + task4.arguments = ["abc", "def" + userControlledString] // $ MISSING: Alert try! task4.run() let task5 = mkProcess() - task5?.executableURL = URL(fileURLWithPath: userControlledString) // BAD - task5?.arguments = [userControlledString] // BAD + task5?.executableURL = URL(fileURLWithPath: userControlledString) // $ MISSING: Alert + task5?.arguments = [userControlledString] // $ MISSING: Alert try! task5?.run() let task6 = MyProcess() - task6.executableURL = URL(fileURLWithPath: userControlledString) // BAD - task6.executableURL = URL(string: userControlledString)! // BAD - task6.arguments = [userControlledString] // BAD + task6.executableURL = URL(fileURLWithPath: userControlledString) // $ MISSING: Alert + task6.executableURL = URL(string: userControlledString)! // $ MISSING: Alert + task6.arguments = [userControlledString] // $ MISSING: Alert task6.setArguments([userControlledString]) // BAD (flagged inside `setArguments`) task6.harmlessField = userControlledString // GOOD try! task6.run() @@ -143,28 +143,28 @@ func testCommandInjectionMore(mySafeString: String) { let task7 = Process() task7.executableURL = URL(fileURLWithPath: mySafeString) // GOOD task7.arguments = ["abc", "def"] - task7.arguments?.append(userControlledString) // BAD [NOT DETECTED] + task7.arguments?.append(userControlledString) // $ MISSING: Alert // Originally missing try! task7.run() _ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", mySafeString]) // GOOD - _ = Process.launchedProcess(launchPath: userControlledString, arguments: ["abc", mySafeString]) // BAD - _ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // BAD + _ = Process.launchedProcess(launchPath: userControlledString, arguments: ["abc", mySafeString]) // $ Alert + _ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // $ MISSING: Alert _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", mySafeString]) // GOOD - _ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // BAD - _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // BAD + _ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // $ MISSING: Alert + _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // $ MISSING: Alert let task8 = try! NSUserScriptTask(url: URL(string: mySafeString)!) // GOOD task8.execute() - let task9 = try! NSUserScriptTask(url: URL(string: userControlledString)!) // BAD + let task9 = try! NSUserScriptTask(url: URL(string: userControlledString)!) // $ Alert task9.execute() - let task10 = try! NSUserUnixTask(url: URL(string: userControlledString)!) // BAD - task10.execute(withArguments: [userControlledString]) // BAD + let task10 = try! NSUserUnixTask(url: URL(string: userControlledString)!) // $ MISSING: Alert + task10.execute(withArguments: [userControlledString]) // $ MISSING: Alert - let task11 = try! NSUserAutomatorTask(url: URL(string: userControlledString)!) // BAD - task11.variables = ["abc": userControlledString] // BAD [NOT DETECTED] + let task11 = try! NSUserAutomatorTask(url: URL(string: userControlledString)!) // $ MISSING: Alert + task11.variables = ["abc": userControlledString] // $ MISSING: Alert // Originally missing task11.execute(withInput: nil) let files = try! FileManager.default.contentsOfDirectory(atPath: "some/directory") @@ -191,28 +191,28 @@ struct MyClass { return nil } set { - p1.arguments = newValue // BAD - p2!.arguments = newValue // BAD - p3.arguments = newValue // BAD + p1.arguments = newValue // $ MISSING: Alert + p2!.arguments = newValue // $ MISSING: Alert + p3.arguments = newValue // $ MISSING: Alert } } mutating func myFunc() { - guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { + guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else { // $ MISSING: Source return } let tainted1 = [userControlledString] - p1.arguments = tainted1 // BAD - p2!.arguments = tainted1 // BAD - p3.arguments = tainted1 // BAD + p1.arguments = tainted1 // $ MISSING: Alert + p2!.arguments = tainted1 // $ MISSING: Alert + p3.arguments = tainted1 // $ MISSING: Alert let tainted2 : [String]? = [userControlledString] - p1.arguments = tainted2 // BAD - p2!.arguments = tainted2 // BAD - p3.arguments = tainted2 // BAD + p1.arguments = tainted2 // $ MISSING: Alert + p2!.arguments = tainted2 // $ MISSING: Alert + p3.arguments = tainted2 // $ MISSING: Alert myValue = tainted2 } From 618b888aa3d897df67c19bcc5326e3b368be78a3 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:35:31 +0100 Subject: [PATCH 4/7] unified: model Process.run with its default termination handler Add the model-as-data selector for calls that omit terminationHandler, retaining the existing full-argument model. This detects the previously missing executable-URL case without query-local model code. Cover explicit and omitted termination handlers with positive and safe cases. The command-injection test now reports four alerts with no expectation failures; both it and the path-injection test pass without --learn. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- unified/ql/lib/ext/legacy-swift.model.yml | 2 ++ .../CommandInjection.expected | 20 ++++++++++++++----- .../CommandInjection/CommandInjection.swift | 5 ++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/unified/ql/lib/ext/legacy-swift.model.yml b/unified/ql/lib/ext/legacy-swift.model.yml index 30d5be429efc..d262fadcec1d 100644 --- a/unified/ql/lib/ext/legacy-swift.model.yml +++ b/unified/ql/lib/ext/legacy-swift.model.yml @@ -220,6 +220,8 @@ extensions: - ["", "Process", true, "executableURL", "", "", "PostUpdate", "command-injection", "manual"] - ["", "Process", true, "launchPath", "", "", "PostUpdate", "command-injection", "manual"] - ["", "Process", true, "launchedProcess(arguments:launchPath:)", "", "", "Argument[launchPath:,arguments:]", "command-injection", "manual"] + # Calls may omit the defaulted terminationHandler argument. + - ["", "Process", true, "run(_:arguments:)", "", "", "Argument[0,arguments:]", "command-injection", "manual"] - ["", "Process", true, "run(_:arguments:terminationHandler:)", "", "", "Argument[0,arguments:]", "command-injection", "manual"] - ["", "Process", true, "standardError", "", "", "PostUpdate", "command-injection", "manual"] - ["", "Process", true, "standardInput", "", "", "PostUpdate", "command-injection", "manual"] diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected index 2a0d50ee5f7d..3d8ffa476377 100644 --- a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.expected @@ -1,19 +1,29 @@ #select | CommandInjection.swift:150:42:150:61 | userControlledString | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:150:42:150:61 | userControlledString | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | -| CommandInjection.swift:160:41:160:74 | ... ! | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:160:41:160:74 | ... ! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | +| CommandInjection.swift:154:23:154:56 | ... ! | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:154:23:154:56 | ... ! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | +| CommandInjection.swift:158:23:158:56 | ... ! | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:158:23:158:56 | ... ! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | +| CommandInjection.swift:163:41:163:74 | ... ! | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:163:41:163:74 | ... ! | This command depends on a $@. | CommandInjection.swift:105:40:105:94 | String(...) | user-provided value | edges | CommandInjection.swift:105:8:105:31 | ... .some(...) [some.0] | CommandInjection.swift:105:12:105:31 | userControlledString | provenance | | | CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:150:42:150:61 | userControlledString | provenance | | -| CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:160:53:160:72 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:154:35:154:54 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:158:35:158:54 | userControlledString | provenance | | +| CommandInjection.swift:105:12:105:31 | userControlledString | CommandInjection.swift:163:53:163:72 | userControlledString | provenance | | | CommandInjection.swift:105:35:105:94 | try? ... [some.0] | CommandInjection.swift:105:8:105:31 | ... .some(...) [some.0] | provenance | | | CommandInjection.swift:105:40:105:94 | String(...) | CommandInjection.swift:105:35:105:94 | try? ... [some.0] | provenance | | -| CommandInjection.swift:160:53:160:72 | userControlledString | CommandInjection.swift:160:41:160:74 | ... ! | provenance | | +| CommandInjection.swift:154:35:154:54 | userControlledString | CommandInjection.swift:154:23:154:56 | ... ! | provenance | | +| CommandInjection.swift:158:35:158:54 | userControlledString | CommandInjection.swift:158:23:158:56 | ... ! | provenance | | +| CommandInjection.swift:163:53:163:72 | userControlledString | CommandInjection.swift:163:41:163:74 | ... ! | provenance | | nodes | CommandInjection.swift:105:8:105:31 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | | CommandInjection.swift:105:12:105:31 | userControlledString | semmle.label | userControlledString | | CommandInjection.swift:105:35:105:94 | try? ... [some.0] | semmle.label | try? ... [some.0] | | CommandInjection.swift:105:40:105:94 | String(...) | semmle.label | String(...) | | CommandInjection.swift:150:42:150:61 | userControlledString | semmle.label | userControlledString | -| CommandInjection.swift:160:41:160:74 | ... ! | semmle.label | ... ! | -| CommandInjection.swift:160:53:160:72 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:154:23:154:56 | ... ! | semmle.label | ... ! | +| CommandInjection.swift:154:35:154:54 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:158:23:158:56 | ... ! | semmle.label | ... ! | +| CommandInjection.swift:158:35:158:54 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:163:41:163:74 | ... ! | semmle.label | ... ! | +| CommandInjection.swift:163:53:163:72 | userControlledString | semmle.label | userControlledString | subpaths diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift index e70d80a9dc3b..0b9307d10bf5 100644 --- a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift @@ -151,9 +151,12 @@ func testCommandInjectionMore(mySafeString: String) { _ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // $ MISSING: Alert _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", mySafeString]) // GOOD - _ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // $ MISSING: Alert + _ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // $ Alert _ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // $ MISSING: Alert + _ = try? Process.run(URL(string: mySafeString)!, arguments: [], terminationHandler: nil) // GOOD + _ = try? Process.run(URL(string: userControlledString)!, arguments: [], terminationHandler: nil) // $ Alert + let task8 = try! NSUserScriptTask(url: URL(string: mySafeString)!) // GOOD task8.execute() From d93f510fb99d71909bc7d517ff7d2dda17ad79fa Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:36:59 +0100 Subject: [PATCH 5/7] unified: refresh command injection guidance and references Replace the OWASP link returning 404 with its current canonical page (verified HTTP 200). Explain fixed executables, separate arguments, and allowlisting complete shell commands so the guidance matches the examples. Clarify which test comments describe original Swift false positives or currently missing alerts. Help XML and example paths validate, and the focused test passes with unchanged results. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../security/CWE-078/CommandInjection.qhelp | 27 ++++++++++--------- .../CommandInjection/CommandInjection.swift | 4 +-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp b/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp index ed6a27a4222b..33071a352efb 100644 --- a/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp @@ -5,33 +5,34 @@

    -Constructing a system command with unsanitized user input is dangerous, -since a malicious user may be able to craft input that executes arbitrary code. +Constructing a system command with unvalidated user input may allow an attacker +to choose an executable or change the meaning of its arguments. If a command +string is passed to a shell, malicious input may also execute additional commands.

    -If possible, use hard-coded string literals to specify the command to run. Instead of interpreting -user input directly as command names, examine the input and then choose among hard-coded string -literals. +If possible, avoid invoking a shell. Use a fixed executable and pass arguments +separately using Process. Validate untrusted values against the +allowed values for the command and its arguments.

    -If this is not possible, then add sanitization code to verify that the user input is safe before -using it. +If a shell command is required, select from an allowlist of complete, fixed +commands rather than concatenating untrusted input. Do not rely solely on +removing shell metacharacters.

    -The following example executes code from user input without -sanitizing it first: +The following example passes user input to bash -c without +validating it first:

    -If user input is used to construct a command it should be checked -first. This ensures that the user cannot insert characters that have special -meanings: +The following example allows only a fixed set of command strings before +passing one to the shell:

    @@ -39,7 +40,7 @@ meanings:
  • OWASP: -Command Injection. +Command Injection.
  • diff --git a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift index 0b9307d10bf5..b822e8a258a8 100644 --- a/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift +++ b/unified/ql/test/query-tests/security/CWE-078/CommandInjection/CommandInjection.swift @@ -84,7 +84,7 @@ func testCommandInjectionQhelpExamples() { if let validatedString = validateCommand(userControlledString) { let task2 = Process() task2.launchPath = "/bin/bash" // GOOD - task2.arguments = ["-c", validatedString] // GOOD [FALSE POSITIVE] + task2.arguments = ["-c", validatedString] // GOOD (original Swift false positive) task2.launch() } } @@ -136,7 +136,7 @@ func testCommandInjectionMore(mySafeString: String) { task6.executableURL = URL(fileURLWithPath: userControlledString) // $ MISSING: Alert task6.executableURL = URL(string: userControlledString)! // $ MISSING: Alert task6.arguments = [userControlledString] // $ MISSING: Alert - task6.setArguments([userControlledString]) // BAD (flagged inside `setArguments`) + task6.setArguments([userControlledString]) // BAD (expected inside setArguments; currently missing) task6.harmlessField = userControlledString // GOOD try! task6.run() From 21a3e09bf362fd13ecdce08007f3182ad7b725d3 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:13:52 +0100 Subject: [PATCH 6/7] unified: Revert most of the changes to the .qhelp (as far as I can tell they don't really fix anything just rephrase?). --- .../security/CWE-078/CommandInjection.qhelp | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp b/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp index 33071a352efb..9fb39fd64a32 100644 --- a/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.qhelp @@ -5,34 +5,33 @@

    -Constructing a system command with unvalidated user input may allow an attacker -to choose an executable or change the meaning of its arguments. If a command -string is passed to a shell, malicious input may also execute additional commands. +Constructing a system command with unsanitized user input is dangerous, +since a malicious user may be able to craft input that executes arbitrary code.

    -If possible, avoid invoking a shell. Use a fixed executable and pass arguments -separately using Process. Validate untrusted values against the -allowed values for the command and its arguments. +If possible, use hard-coded string literals to specify the command to run. Instead of interpreting +user input directly as command names, examine the input and then choose among hard-coded string +literals.

    -If a shell command is required, select from an allowlist of complete, fixed -commands rather than concatenating untrusted input. Do not rely solely on -removing shell metacharacters. +If this is not possible, then add sanitization code to verify that the user input is safe before +using it.

    -The following example passes user input to bash -c without -validating it first: +The following example executes code from user input without +sanitizing it first:

    -The following example allows only a fixed set of command strings before -passing one to the shell: +If user input is used to construct a command it should be checked +first. This ensures that the user cannot insert characters that have special +meanings:

    From 2012a65ef1425940099d466857ed4243928fc63c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:30:54 +0100 Subject: [PATCH 7/7] unified: Delete excessive comment. --- unified/ql/src/queries/security/CWE-078/CommandInjection.ql | 3 --- 1 file changed, 3 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql index 54a4394dff4f..c1268d2bde21 100644 --- a/unified/ql/src/queries/security/CWE-078/CommandInjection.ql +++ b/unified/ql/src/queries/security/CWE-078/CommandInjection.ql @@ -33,9 +33,6 @@ module CommandInjectionConfig implements DataFlow::ConfigSig { predicate observeDiffInformedIncrementalMode() { any() } } -/** - * Detect taint flow of tainted data that reaches a command injection sink. - */ module CommandInjectionFlow = TaintTracking::Global; import CommandInjectionFlow::PathGraph