diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/NsUrl.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/NsUrl.qll index 019bc30cdf35..fb4f8a8516ce 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/NsUrl.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/NsUrl.qll @@ -10,6 +10,6 @@ private import codeql.swift.dataflow.ExternalFlow */ private class NsUrlSummaries extends SummaryModelCsv { override predicate row(string row) { - row = ";NSURL;true;init(string:);(String);;Argument[0];ReturnValue;taint" + row = ";NSURL;true;init(string:);(String);;Argument[0];ReturnValue.OptionalSome;taint" } } diff --git a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll index 2f03fdd5327b..a24a394dd0f4 100644 --- a/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll +++ b/swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Url.qll @@ -85,29 +85,33 @@ private class UrlSummaries extends SummaryModelCsv { override predicate row(string row) { row = [ - ";URL;true;init(string:);(String);;Argument[0];ReturnValue;taint", - ";URL;true;init(string:relativeTo:);(String,URL?);;Argument[0..1];ReturnValue;taint", + ";URL;true;init(string:);(String);;Argument[0];ReturnValue.OptionalSome;taint", + ";URL;true;init(string:relativeTo:);(String,URL?);;Argument[0];ReturnValue.OptionalSome;taint", + ";URL;true;init(string:relativeTo:);(String,URL?);;Argument[1].OptionalSome;ReturnValue.OptionalSome;taint", ";URL;true;init(fileURLWithPath:);;;Argument[0];ReturnValue;taint", ";URL;true;init(fileURLWithPath:isDirectory:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(fileURLWithPath:relativeTo:);;;Argument[0..1];ReturnValue;taint", + ";URL;true;init(fileURLWithPath:relativeTo:);;;Argument[0];ReturnValue;taint", + ";URL;true;init(fileURLWithPath:relativeTo:);;;Argument[1].OptionalSome;ReturnValue;taint", ";URL;true;init(fileURLWithPath:isDirectory:relativeTo:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(fileURLWithPath:isDirectory:relativeTo:);;;Argument[2];ReturnValue;taint", + ";URL;true;init(fileURLWithPath:isDirectory:relativeTo:);;;Argument[2].OptionalSome;ReturnValue;taint", ";URL;true;init(fileURLWithFileSystemRepresentation:isDirectory:relativeTo:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(fileURLWithFileSystemRepresentation:isDirectory:relativeTo:);;;Argument[2];ReturnValue;taint", + ";URL;true;init(fileURLWithFileSystemRepresentation:isDirectory:relativeTo:);;;Argument[2].OptionalSome;ReturnValue;taint", ";URL;true;init(fileReferenceLiteralResourceName:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(_:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(_:isDirectory:);;;Argument[0];ReturnValue;taint", + ";URL;true;init(_:);;;Argument[0];ReturnValue.OptionalSome;taint", + ";URL;true;init(_:isDirectory:);;;Argument[0];ReturnValue.OptionalSome;taint", ";URL;true;init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:);;;Argument[2];ReturnValue;taint", + ";URL;true;init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:);;;Argument[2].OptionalSome;ReturnValue;taint", ";URL;true;init(resolvingAliasFileAt:options:);;;Argument[0];ReturnValue;taint", ";URL;true;init(resource:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(dataRepresentation:relativeTo:isAbsolute:);;;Argument[0..1];ReturnValue;taint", + ";URL;true;init(dataRepresentation:relativeTo:isAbsolute:);;;Argument[0];ReturnValue;taint", + ";URL;true;init(dataRepresentation:relativeTo:isAbsolute:);;;Argument[1].OptionalSome;ReturnValue;taint", ";URL;true;init(_:strategy:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(filePath:directoryHint:);;;Argument[0];ReturnValue;taint", + ";URL;true;init(filePath:directoryHint:);;;Argument[0];ReturnValue.OptionalSome;taint", ";URL;true;init(filePath:directoryHint:relativeTo:);;;Argument[0];ReturnValue;taint", - ";URL;true;init(filePath:directoryHint:relativeTo:);;;Argument[2];ReturnValue;taint", - ";URL;true;init(for:in:appropriateFor:create:);;;Argument[0..2];ReturnValue;taint", - ";URL;true;init(string:encodingInvalidCharacters:);;;Argument[0];ReturnValue;taint", + ";URL;true;init(filePath:directoryHint:relativeTo:);;;Argument[2].OptionalSome;ReturnValue;taint", + ";URL;true;init(for:in:appropriateFor:create:);;;Argument[0..1];ReturnValue;taint", + ";URL;true;init(for:in:appropriateFor:create:);;;Argument[2].OptionalSome;ReturnValue;taint", + ";URL;true;init(string:encodingInvalidCharacters:);;;Argument[0];ReturnValue.OptionalSome;taint", ";URL;true;resourceValues(forKeys:);;;Argument[-1];ReturnValue;taint", ";URL;true;setResourceValues(_:);;;Argument[0];Argument[-1];taint", ";URL;true;setTemporaryResourceValue(_:forKey:);;;Argument[-1..0];Argument[-1];taint", @@ -125,7 +129,8 @@ private class UrlSummaries extends SummaryModelCsv { ";URL;true;deletingLastPathComponent();;;Argument[-1];ReturnValue;taint", ";URL;true;deletingPathExtension();;;Argument[-1];ReturnValue;taint", ";URL;true;bookmarkData(options:includingResourceValuesForKeys:relativeTo:);;;Argument[-1];ReturnValue;taint", - ";URL;true;bookmarkData(options:includingResourceValuesForKeys:relativeTo:);;;Argument[1..2];ReturnValue;taint", + ";URL;true;bookmarkData(options:includingResourceValuesForKeys:relativeTo:);;;Argument[1].OptionalSome.CollectionElement;ReturnValue;taint", + ";URL;true;bookmarkData(options:includingResourceValuesForKeys:relativeTo:);;;Argument[2].OptionalSome;ReturnValue;taint", ";URL;true;bookmarkData(withContentsOf:);;;Argument[0];ReturnValue;taint", ";URL;true;resourceValues(forKeys:fromBookmarkData:);;;Argument[1];ReturnValue;taint", ";URL;true;promisedItemResourceValues(forKeys:);;;Argument[-1];ReturnValue;taint", diff --git a/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll b/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll index 26e8782a7994..5b1cc065ec28 100644 --- a/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll +++ b/swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll @@ -29,50 +29,15 @@ class CommandInjectionAdditionalFlowStep extends Unit { abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); } -private class ProcessSink2 extends CommandInjectionSink instanceof DataFlow::Node { - ProcessSink2() { - exists(AssignExpr assign, ProcessHost s | - assign.getDest() = s and - this.asExpr() = assign.getSource() - ) - or - exists(AssignExpr assign, ProcessHost s, ArrayExpr a | - assign.getDest() = s and - a = assign.getSource() and - this.asExpr() = a.getAnElement() - ) - } -} - -private class ProcessHost extends MemberRefExpr { - ProcessHost() { this.getBase() instanceof ProcessRef } -} - -/** An expression of type `Process`. */ -private class ProcessRef extends Expr { - ProcessRef() { - this.getType() instanceof ProcessType or - this.getType() = any(OptionalType t | t.getBaseType() instanceof ProcessType) - } -} - -/** The type `Process`. */ -private class ProcessType extends NominalType { - ProcessType() { this.getFullName() = "Process" } -} - /** - * A `DataFlow::Node` that is written into a `Process` object. + * An additional taint step for command injection vulnerabilities. */ -private class ProcessSink extends CommandInjectionSink instanceof DataFlow::Node { - ProcessSink() { - // any write into a class derived from `Process` is a sink. For - // example in `Process.launchPath = sensitive` the post-update node corresponding - // with `Process.launchPath` is a sink. - exists(NominalType t, Expr e | - t.getABaseType*().getUnderlyingType().getName() = "Process" and - e.getFullyConverted() = this.asExpr() and - e.getFullyConverted().getType() = t +private class CommandInjectionArrayAdditionalFlowStep extends CommandInjectionAdditionalFlowStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // needed until we have proper content flow through arrays. + exists(ArrayExpr arr | + nodeFrom.asExpr() = arr.getAnElement() and + nodeTo.asExpr() = arr ) } } @@ -83,3 +48,24 @@ private class ProcessSink extends CommandInjectionSink instanceof DataFlow::Node 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", + ] + } +} diff --git a/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll b/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll index 4b67932209dc..10dbc1377260 100644 --- a/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll +++ b/swift/ql/lib/codeql/swift/security/CommandInjectionQuery.qll @@ -1,6 +1,6 @@ /** * Provides a taint-tracking configuration for reasoning about system - * commands built from user-controlled sources (that is, Command injection + * commands built from user-controlled sources (that is, command injection * vulnerabilities). */ @@ -11,7 +11,7 @@ import codeql.swift.dataflow.FlowSources import codeql.swift.security.CommandInjectionExtensions /** - * A taint configuration for tainted data that reaches a Command Injection sink. + * 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 } @@ -26,6 +26,6 @@ module CommandInjectionConfig implements DataFlow::ConfigSig { } /** - * Detect taint flow of tainted data that reaches a Command Injection sink. + * Detect taint flow of tainted data that reaches a command injection sink. */ module CommandInjectionFlow = TaintTracking::Global; diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp index ab9562af12c2..ed6a27a4222b 100644 --- a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.qhelp @@ -24,14 +24,14 @@ using it.

-The following examples execute code from user input without +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. +meanings:

@@ -42,4 +42,4 @@ OWASP: Command Injection. - \ No newline at end of file + diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql index 148676e1ac38..defa939cf68b 100644 --- a/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjection.ql @@ -1,6 +1,7 @@ /** * @name System command built from user-controlled sources - * @description Building a system command from user-controlled sources is vulnerable to insertion of malicious code by the user. + * @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 diff --git a/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift index 3482718eeac5..1773d5b187ba 100644 --- a/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift +++ b/swift/ql/src/experimental/Security/CWE-078/CommandInjectionGood.swift @@ -6,8 +6,10 @@ func validateCommand(_ command: String) -> String? { return nil } -var task = Process() -task.launchPath = "/bin/bash" -task.arguments = ["-c", validateCommand(userControlledString)] // GOOD +if let validatedString = validateCommand(userControlledString) { + var task = Process() + task.launchPath = "/bin/bash" + task.arguments = ["-c", validatedString] // GOOD -task.launch() \ No newline at end of file + task.launch() +} diff --git a/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.expected new file mode 100644 index 000000000000..a015cb69b644 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -0,0 +1,272 @@ +edges +| CommandInjection.swift:58:22:58:33 | command | CommandInjection.swift:62:16:62:16 | command | +| CommandInjection.swift:58:22:58:33 | command [some:0] | CommandInjection.swift:62:16:62:16 | command [some:0] | +| CommandInjection.swift:62:16:62:16 | command | CommandInjection.swift:62:16:62:16 | command [some:0] | +| CommandInjection.swift:69:8:69:12 | let ...? [some:0, some:0] | CommandInjection.swift:69:12:69:12 | userControlledString [some:0] | +| CommandInjection.swift:69:8:69:12 | let ...? [some:0] | CommandInjection.swift:69:12:69:12 | userControlledString | +| CommandInjection.swift:69:12:69:12 | userControlledString | CommandInjection.swift:75:27:75:27 | userControlledString | +| CommandInjection.swift:69:12:69:12 | userControlledString [some:0] | CommandInjection.swift:75:27:75:27 | userControlledString [some:0] | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) | CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0, some:0] | CommandInjection.swift:69:8:69:12 | let ...? [some:0, some:0] | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:69:8:69:12 | let ...? [some:0] | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0, some:0] | +| CommandInjection.swift:75:2:75:2 | [post] task1 [arguments] | CommandInjection.swift:75:2:75:2 | [post] task1 | +| CommandInjection.swift:75:20:75:47 | [...] | CommandInjection.swift:75:2:75:2 | [post] task1 [arguments] | +| CommandInjection.swift:75:27:75:27 | userControlledString | CommandInjection.swift:75:20:75:47 | [...] | +| CommandInjection.swift:75:27:75:27 | userControlledString | CommandInjection.swift:78:43:78:43 | userControlledString | +| CommandInjection.swift:75:27:75:27 | userControlledString [some:0] | CommandInjection.swift:78:43:78:43 | userControlledString [some:0] | +| CommandInjection.swift:78:5:78:9 | let ...? [some:0] | CommandInjection.swift:78:9:78:9 | validatedString | +| CommandInjection.swift:78:9:78:9 | validatedString | CommandInjection.swift:81:31:81:31 | validatedString | +| CommandInjection.swift:78:27:78:63 | call to validateCommand(_:) [some:0] | CommandInjection.swift:78:5:78:9 | let ...? [some:0] | +| CommandInjection.swift:78:43:78:43 | userControlledString | CommandInjection.swift:58:22:58:33 | command | +| CommandInjection.swift:78:43:78:43 | userControlledString | CommandInjection.swift:78:27:78:63 | call to validateCommand(_:) [some:0] | +| CommandInjection.swift:78:43:78:43 | userControlledString [some:0] | CommandInjection.swift:58:22:58:33 | command [some:0] | +| CommandInjection.swift:78:43:78:43 | userControlledString [some:0] | CommandInjection.swift:78:27:78:63 | call to validateCommand(_:) [some:0] | +| CommandInjection.swift:81:6:81:6 | [post] task2 [arguments] | CommandInjection.swift:81:6:81:6 | [post] task2 | +| CommandInjection.swift:81:24:81:46 | [...] | CommandInjection.swift:81:6:81:6 | [post] task2 [arguments] | +| CommandInjection.swift:81:31:81:31 | validatedString | CommandInjection.swift:81:24:81:46 | [...] | +| CommandInjection.swift:99:8:99:12 | let ...? [some:0] | CommandInjection.swift:99:12:99:12 | userControlledString | +| CommandInjection.swift:99:12:99:12 | userControlledString | CommandInjection.swift:114:36:114:36 | userControlledString | +| CommandInjection.swift:99:12:99:12 | userControlledString | CommandInjection.swift:115:28:115:28 | userControlledString | +| CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:99:8:99:12 | let ...? [some:0] | +| CommandInjection.swift:114:2:114:2 | [post] task3 [executableURL] | CommandInjection.swift:114:2:114:2 | [post] task3 | +| CommandInjection.swift:114:24:114:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:114:24:114:57 | ...! | +| CommandInjection.swift:114:24:114:57 | ...! | CommandInjection.swift:114:2:114:2 | [post] task3 [executableURL] | +| CommandInjection.swift:114:36:114:36 | userControlledString | CommandInjection.swift:114:24:114:56 | call to URL.init(string:) [some:0] | +| CommandInjection.swift:115:2:115:2 | [post] task3 [arguments] | CommandInjection.swift:115:2:115:2 | [post] task3 | +| CommandInjection.swift:115:20:115:48 | [...] | CommandInjection.swift:115:2:115:2 | [post] task3 [arguments] | +| CommandInjection.swift:115:28:115:28 | userControlledString | CommandInjection.swift:115:20:115:48 | [...] | +| CommandInjection.swift:115:28:115:28 | userControlledString | CommandInjection.swift:119:45:119:45 | userControlledString | +| CommandInjection.swift:115:28:115:28 | userControlledString | CommandInjection.swift:120:28:120:36 | ... .+(_:_:) ... | +| CommandInjection.swift:115:28:115:28 | userControlledString | CommandInjection.swift:124:46:124:46 | userControlledString | +| CommandInjection.swift:115:28:115:28 | userControlledString | CommandInjection.swift:125:22:125:22 | userControlledString | +| CommandInjection.swift:119:2:119:2 | [post] task4 [executableURL] | CommandInjection.swift:119:2:119:2 | [post] task4 | +| CommandInjection.swift:119:24:119:65 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:119:2:119:2 | [post] task4 [executableURL] | +| CommandInjection.swift:119:45:119:45 | userControlledString | CommandInjection.swift:119:24:119:65 | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:120:2:120:2 | [post] task4 [arguments] | CommandInjection.swift:120:2:120:2 | [post] task4 | +| CommandInjection.swift:120:20:120:56 | [...] | CommandInjection.swift:120:2:120:2 | [post] task4 [arguments] | +| CommandInjection.swift:120:28:120:36 | ... .+(_:_:) ... | CommandInjection.swift:120:20:120:56 | [...] | +| CommandInjection.swift:124:2:124:7 | [post] ...? [executableURL] | CommandInjection.swift:124:2:124:7 | [post] ...? | +| CommandInjection.swift:124:25:124:66 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:124:2:124:7 | [post] ...? [executableURL] | +| CommandInjection.swift:124:46:124:46 | userControlledString | CommandInjection.swift:124:25:124:66 | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:125:2:125:7 | [post] ...? [arguments] | CommandInjection.swift:125:2:125:7 | [post] ...? | +| CommandInjection.swift:125:21:125:42 | [...] | CommandInjection.swift:125:2:125:7 | [post] ...? [arguments] | +| CommandInjection.swift:125:22:125:22 | userControlledString | CommandInjection.swift:125:21:125:42 | [...] | +| CommandInjection.swift:125:22:125:22 | userControlledString | CommandInjection.swift:130:21:130:21 | userControlledString | +| CommandInjection.swift:130:21:130:21 | userControlledString | CommandInjection.swift:131:22:131:22 | userControlledString | +| CommandInjection.swift:131:22:131:22 | userControlledString | CommandInjection.swift:142:42:142:42 | userControlledString | +| CommandInjection.swift:131:22:131:22 | userControlledString | CommandInjection.swift:143:75:143:75 | userControlledString | +| CommandInjection.swift:143:75:143:75 | userControlledString | CommandInjection.swift:143:67:143:95 | [...] | +| CommandInjection.swift:143:75:143:75 | userControlledString | CommandInjection.swift:146:35:146:35 | userControlledString | +| CommandInjection.swift:143:75:143:75 | userControlledString | CommandInjection.swift:147:70:147:70 | userControlledString | +| CommandInjection.swift:146:23:146:55 | call to URL.init(string:) [some:0] | CommandInjection.swift:146:23:146:56 | ...! | +| CommandInjection.swift:146:35:146:35 | userControlledString | CommandInjection.swift:146:23:146:55 | call to URL.init(string:) [some:0] | +| CommandInjection.swift:147:70:147:70 | userControlledString | CommandInjection.swift:147:62:147:90 | [...] | +| CommandInjection.swift:147:70:147:70 | userControlledString | CommandInjection.swift:152:53:152:53 | userControlledString | +| CommandInjection.swift:147:70:147:70 | userControlledString | CommandInjection.swift:155:52:155:52 | userControlledString | +| CommandInjection.swift:147:70:147:70 | userControlledString | CommandInjection.swift:156:33:156:33 | userControlledString | +| CommandInjection.swift:152:41:152:73 | call to URL.init(string:) [some:0] | CommandInjection.swift:152:41:152:74 | ...! | +| CommandInjection.swift:152:53:152:53 | userControlledString | CommandInjection.swift:152:41:152:73 | call to URL.init(string:) [some:0] | +| CommandInjection.swift:155:40:155:72 | call to URL.init(string:) [some:0] | CommandInjection.swift:155:40:155:73 | ...! | +| CommandInjection.swift:155:40:155:72 | call to URL.init(string:) [some:0] | CommandInjection.swift:155:40:155:73 | ...! | +| CommandInjection.swift:155:40:155:73 | ...! | file://:0:0:0:0 | url | +| CommandInjection.swift:155:52:155:52 | userControlledString | CommandInjection.swift:155:40:155:72 | call to URL.init(string:) [some:0] | +| CommandInjection.swift:156:33:156:33 | userControlledString | CommandInjection.swift:156:32:156:53 | [...] | +| CommandInjection.swift:156:33:156:33 | userControlledString | CommandInjection.swift:158:57:158:57 | userControlledString | +| CommandInjection.swift:158:45:158:77 | call to URL.init(string:) [some:0] | CommandInjection.swift:158:45:158:78 | ...! | +| CommandInjection.swift:158:45:158:77 | call to URL.init(string:) [some:0] | CommandInjection.swift:158:45:158:78 | ...! | +| CommandInjection.swift:158:45:158:78 | ...! | file://:0:0:0:0 | url | +| CommandInjection.swift:158:57:158:57 | userControlledString | CommandInjection.swift:158:45:158:77 | call to URL.init(string:) [some:0] | +| CommandInjection.swift:172:3:172:3 | newValue | CommandInjection.swift:173:19:173:19 | newValue | +| CommandInjection.swift:172:3:172:3 | newValue | CommandInjection.swift:174:20:174:20 | newValue | +| CommandInjection.swift:172:3:172:3 | newValue | CommandInjection.swift:175:19:175:19 | newValue | +| CommandInjection.swift:173:4:173:4 | [post] getter for .p1 [arguments] | CommandInjection.swift:173:4:173:4 | [post] getter for .p1 | +| CommandInjection.swift:173:19:173:19 | newValue | CommandInjection.swift:173:4:173:4 | [post] getter for .p1 [arguments] | +| CommandInjection.swift:174:4:174:6 | [post] ...! [arguments] | CommandInjection.swift:174:4:174:6 | [post] ...! | +| CommandInjection.swift:174:20:174:20 | newValue | CommandInjection.swift:174:4:174:6 | [post] ...! [arguments] | +| CommandInjection.swift:175:4:175:4 | [post] ...! [arguments] | CommandInjection.swift:175:4:175:4 | [post] ...! | +| CommandInjection.swift:175:19:175:19 | newValue | CommandInjection.swift:175:4:175:4 | [post] ...! [arguments] | +| CommandInjection.swift:180:9:180:13 | let ...? [some:0] | CommandInjection.swift:180:13:180:13 | userControlledString | +| CommandInjection.swift:180:13:180:13 | userControlledString | CommandInjection.swift:184:19:184:19 | userControlledString | +| CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:180:9:180:13 | let ...? [some:0] | +| CommandInjection.swift:184:18:184:39 | [...] | CommandInjection.swift:186:18:186:18 | tainted1 | +| CommandInjection.swift:184:18:184:39 | [...] | CommandInjection.swift:187:19:187:19 | tainted1 | +| CommandInjection.swift:184:18:184:39 | [...] | CommandInjection.swift:188:18:188:18 | tainted1 | +| CommandInjection.swift:184:19:184:19 | userControlledString | CommandInjection.swift:184:18:184:39 | [...] | +| CommandInjection.swift:184:19:184:19 | userControlledString | CommandInjection.swift:190:31:190:31 | userControlledString | +| CommandInjection.swift:186:3:186:3 | [post] getter for .p1 [arguments] | CommandInjection.swift:186:3:186:3 | [post] getter for .p1 | +| CommandInjection.swift:186:18:186:18 | tainted1 | CommandInjection.swift:186:3:186:3 | [post] getter for .p1 [arguments] | +| CommandInjection.swift:186:18:186:18 | tainted1 | CommandInjection.swift:187:19:187:19 | tainted1 | +| CommandInjection.swift:186:18:186:18 | tainted1 | CommandInjection.swift:188:18:188:18 | tainted1 | +| CommandInjection.swift:187:3:187:5 | [post] ...! [arguments] | CommandInjection.swift:187:3:187:5 | [post] ...! | +| CommandInjection.swift:187:19:187:19 | tainted1 | CommandInjection.swift:187:3:187:5 | [post] ...! [arguments] | +| CommandInjection.swift:187:19:187:19 | tainted1 | CommandInjection.swift:188:18:188:18 | tainted1 | +| CommandInjection.swift:188:3:188:3 | [post] ...! [arguments] | CommandInjection.swift:188:3:188:3 | [post] ...! | +| CommandInjection.swift:188:18:188:18 | tainted1 | CommandInjection.swift:188:3:188:3 | [post] ...! [arguments] | +| CommandInjection.swift:190:30:190:51 | [...] | CommandInjection.swift:192:18:192:18 | tainted2 | +| CommandInjection.swift:190:30:190:51 | [...] | CommandInjection.swift:193:19:193:19 | tainted2 | +| CommandInjection.swift:190:30:190:51 | [...] | CommandInjection.swift:194:18:194:18 | tainted2 | +| CommandInjection.swift:190:30:190:51 | [...] | CommandInjection.swift:196:13:196:13 | tainted2 | +| CommandInjection.swift:190:31:190:31 | userControlledString | CommandInjection.swift:190:30:190:51 | [...] | +| CommandInjection.swift:192:3:192:3 | [post] getter for .p1 [arguments] | CommandInjection.swift:192:3:192:3 | [post] getter for .p1 | +| CommandInjection.swift:192:18:192:18 | tainted2 | CommandInjection.swift:192:3:192:3 | [post] getter for .p1 [arguments] | +| CommandInjection.swift:193:3:193:5 | [post] ...! [arguments] | CommandInjection.swift:193:3:193:5 | [post] ...! | +| CommandInjection.swift:193:19:193:19 | tainted2 | CommandInjection.swift:193:3:193:5 | [post] ...! [arguments] | +| CommandInjection.swift:194:3:194:3 | [post] ...! [arguments] | CommandInjection.swift:194:3:194:3 | [post] ...! | +| CommandInjection.swift:194:18:194:18 | tainted2 | CommandInjection.swift:194:3:194:3 | [post] ...! [arguments] | +| CommandInjection.swift:196:13:196:13 | tainted2 | CommandInjection.swift:172:3:172:3 | newValue | +| file://:0:0:0:0 | url | file://:0:0:0:0 | url | +| file://:0:0:0:0 | url | file://:0:0:0:0 | url | +nodes +| CommandInjection.swift:58:22:58:33 | command | semmle.label | command | +| CommandInjection.swift:58:22:58:33 | command [some:0] | semmle.label | command [some:0] | +| CommandInjection.swift:62:16:62:16 | command | semmle.label | command | +| CommandInjection.swift:62:16:62:16 | command [some:0] | semmle.label | command [some:0] | +| CommandInjection.swift:62:16:62:16 | command [some:0] | semmle.label | command [some:0] | +| CommandInjection.swift:69:8:69:12 | let ...? [some:0, some:0] | semmle.label | let ...? [some:0, some:0] | +| CommandInjection.swift:69:8:69:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:69:12:69:12 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:69:12:69:12 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0, some:0] | semmle.label | call to String.init(contentsOf:) [some:0, some:0] | +| CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:75:2:75:2 | [post] task1 | semmle.label | [post] task1 | +| CommandInjection.swift:75:2:75:2 | [post] task1 [arguments] | semmle.label | [post] task1 [arguments] | +| CommandInjection.swift:75:20:75:47 | [...] | semmle.label | [...] | +| CommandInjection.swift:75:27:75:27 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:75:27:75:27 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | +| CommandInjection.swift:78:5:78:9 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:78:9:78:9 | validatedString | semmle.label | validatedString | +| CommandInjection.swift:78:27:78:63 | call to validateCommand(_:) [some:0] | semmle.label | call to validateCommand(_:) [some:0] | +| CommandInjection.swift:78:43:78:43 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:78:43:78:43 | userControlledString [some:0] | semmle.label | userControlledString [some:0] | +| CommandInjection.swift:81:6:81:6 | [post] task2 | semmle.label | [post] task2 | +| CommandInjection.swift:81:6:81:6 | [post] task2 [arguments] | semmle.label | [post] task2 [arguments] | +| CommandInjection.swift:81:24:81:46 | [...] | semmle.label | [...] | +| CommandInjection.swift:81:31:81:31 | validatedString | semmle.label | validatedString | +| CommandInjection.swift:99:8:99:12 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:99:12:99:12 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:114:2:114:2 | [post] task3 | semmle.label | [post] task3 | +| CommandInjection.swift:114:2:114:2 | [post] task3 [executableURL] | semmle.label | [post] task3 [executableURL] | +| CommandInjection.swift:114:24:114:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:114:24:114:57 | ...! | semmle.label | ...! | +| CommandInjection.swift:114:36:114:36 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:115:2:115:2 | [post] task3 | semmle.label | [post] task3 | +| CommandInjection.swift:115:2:115:2 | [post] task3 [arguments] | semmle.label | [post] task3 [arguments] | +| CommandInjection.swift:115:20:115:48 | [...] | semmle.label | [...] | +| CommandInjection.swift:115:28:115:28 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:119:2:119:2 | [post] task4 | semmle.label | [post] task4 | +| CommandInjection.swift:119:2:119:2 | [post] task4 [executableURL] | semmle.label | [post] task4 [executableURL] | +| CommandInjection.swift:119:24:119:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:119:45:119:45 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:120:2:120:2 | [post] task4 | semmle.label | [post] task4 | +| CommandInjection.swift:120:2:120:2 | [post] task4 [arguments] | semmle.label | [post] task4 [arguments] | +| CommandInjection.swift:120:20:120:56 | [...] | semmle.label | [...] | +| CommandInjection.swift:120:28:120:36 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | +| CommandInjection.swift:124:2:124:7 | [post] ...? | semmle.label | [post] ...? | +| CommandInjection.swift:124:2:124:7 | [post] ...? [executableURL] | semmle.label | [post] ...? [executableURL] | +| CommandInjection.swift:124:25:124:66 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) | +| CommandInjection.swift:124:46:124:46 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:125:2:125:7 | [post] ...? | semmle.label | [post] ...? | +| CommandInjection.swift:125:2:125:7 | [post] ...? [arguments] | semmle.label | [post] ...? [arguments] | +| CommandInjection.swift:125:21:125:42 | [...] | semmle.label | [...] | +| CommandInjection.swift:125:22:125:22 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:130:21:130:21 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:131:22:131:22 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:142:42:142:42 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:143:67:143:95 | [...] | semmle.label | [...] | +| CommandInjection.swift:143:75:143:75 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:146:23:146:55 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:146:23:146:56 | ...! | semmle.label | ...! | +| CommandInjection.swift:146:35:146:35 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:147:62:147:90 | [...] | semmle.label | [...] | +| CommandInjection.swift:147:70:147:70 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:152:41:152:73 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:152:41:152:74 | ...! | semmle.label | ...! | +| CommandInjection.swift:152:53:152:53 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:155:40:155:72 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:155:40:155:73 | ...! | semmle.label | ...! | +| CommandInjection.swift:155:40:155:73 | ...! | semmle.label | ...! | +| CommandInjection.swift:155:52:155:52 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:156:32:156:53 | [...] | semmle.label | [...] | +| CommandInjection.swift:156:33:156:33 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:158:45:158:77 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | +| CommandInjection.swift:158:45:158:78 | ...! | semmle.label | ...! | +| CommandInjection.swift:158:45:158:78 | ...! | semmle.label | ...! | +| CommandInjection.swift:158:57:158:57 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:172:3:172:3 | newValue | semmle.label | newValue | +| CommandInjection.swift:173:4:173:4 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | +| CommandInjection.swift:173:4:173:4 | [post] getter for .p1 [arguments] | semmle.label | [post] getter for .p1 [arguments] | +| CommandInjection.swift:173:19:173:19 | newValue | semmle.label | newValue | +| CommandInjection.swift:174:4:174:6 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:174:4:174:6 | [post] ...! [arguments] | semmle.label | [post] ...! [arguments] | +| CommandInjection.swift:174:20:174:20 | newValue | semmle.label | newValue | +| CommandInjection.swift:175:4:175:4 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:175:4:175:4 | [post] ...! [arguments] | semmle.label | [post] ...! [arguments] | +| CommandInjection.swift:175:19:175:19 | newValue | semmle.label | newValue | +| CommandInjection.swift:180:9:180:13 | let ...? [some:0] | semmle.label | let ...? [some:0] | +| CommandInjection.swift:180:13:180:13 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] | +| CommandInjection.swift:184:18:184:39 | [...] | semmle.label | [...] | +| CommandInjection.swift:184:19:184:19 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:186:3:186:3 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | +| CommandInjection.swift:186:3:186:3 | [post] getter for .p1 [arguments] | semmle.label | [post] getter for .p1 [arguments] | +| CommandInjection.swift:186:18:186:18 | tainted1 | semmle.label | tainted1 | +| CommandInjection.swift:187:3:187:5 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:187:3:187:5 | [post] ...! [arguments] | semmle.label | [post] ...! [arguments] | +| CommandInjection.swift:187:19:187:19 | tainted1 | semmle.label | tainted1 | +| CommandInjection.swift:188:3:188:3 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:188:3:188:3 | [post] ...! [arguments] | semmle.label | [post] ...! [arguments] | +| CommandInjection.swift:188:18:188:18 | tainted1 | semmle.label | tainted1 | +| CommandInjection.swift:190:30:190:51 | [...] | semmle.label | [...] | +| CommandInjection.swift:190:31:190:31 | userControlledString | semmle.label | userControlledString | +| CommandInjection.swift:192:3:192:3 | [post] getter for .p1 | semmle.label | [post] getter for .p1 | +| CommandInjection.swift:192:3:192:3 | [post] getter for .p1 [arguments] | semmle.label | [post] getter for .p1 [arguments] | +| CommandInjection.swift:192:18:192:18 | tainted2 | semmle.label | tainted2 | +| CommandInjection.swift:193:3:193:5 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:193:3:193:5 | [post] ...! [arguments] | semmle.label | [post] ...! [arguments] | +| CommandInjection.swift:193:19:193:19 | tainted2 | semmle.label | tainted2 | +| CommandInjection.swift:194:3:194:3 | [post] ...! | semmle.label | [post] ...! | +| CommandInjection.swift:194:3:194:3 | [post] ...! [arguments] | semmle.label | [post] ...! [arguments] | +| CommandInjection.swift:194:18:194:18 | tainted2 | semmle.label | tainted2 | +| CommandInjection.swift:196:13:196:13 | tainted2 | semmle.label | tainted2 | +| 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:78:43:78:43 | userControlledString | CommandInjection.swift:58:22:58:33 | command | CommandInjection.swift:62:16:62:16 | command [some:0] | CommandInjection.swift:78:27:78:63 | call to validateCommand(_:) [some:0] | +| CommandInjection.swift:78:43:78:43 | userControlledString [some:0] | CommandInjection.swift:58:22:58:33 | command [some:0] | CommandInjection.swift:62:16:62:16 | command [some:0] | CommandInjection.swift:78:27:78:63 | call to validateCommand(_:) [some:0] | +#select +| CommandInjection.swift:75:2:75:2 | [post] task1 | CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) | CommandInjection.swift:75:2:75:2 | [post] task1 | This command depends on a $@. | CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:81:6:81:6 | [post] task2 | CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) | CommandInjection.swift:81:6:81:6 | [post] task2 | This command depends on a $@. | CommandInjection.swift:69:40:69:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:114:2:114:2 | [post] task3 | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:114:2:114:2 | [post] task3 | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:115:2:115:2 | [post] task3 | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:115:2:115:2 | [post] task3 | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:119:2:119:2 | [post] task4 | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:119:2:119:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:120:2:120:2 | [post] task4 | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:120:2:120:2 | [post] task4 | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:124:2:124:7 | [post] ...? | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:124:2:124:7 | [post] ...? | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:125:2:125:7 | [post] ...? | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:125:2:125:7 | [post] ...? | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:142:42:142:42 | userControlledString | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:142:42:142:42 | userControlledString | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:143:67:143:95 | [...] | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:143:67:143:95 | [...] | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:146:23:146:56 | ...! | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:146:23:146:56 | ...! | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:147:62:147:90 | [...] | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:147:62:147:90 | [...] | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:152:41:152:74 | ...! | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:152:41:152:74 | ...! | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:155:40:155:73 | ...! | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:155:40:155:73 | ...! | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:156:32:156:53 | [...] | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:156:32:156:53 | [...] | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:158:45:158:78 | ...! | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | CommandInjection.swift:158:45:158:78 | ...! | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:173:4:173:4 | [post] getter for .p1 | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:173:4:173:4 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:174:4:174:6 | [post] ...! | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:174:4:174:6 | [post] ...! | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:175:4:175:4 | [post] ...! | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:175:4:175:4 | [post] ...! | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:186:3:186:3 | [post] getter for .p1 | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:186:3:186:3 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:187:3:187:5 | [post] ...! | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:187:3:187:5 | [post] ...! | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:188:3:188:3 | [post] ...! | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:188:3:188:3 | [post] ...! | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:192:3:192:3 | [post] getter for .p1 | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:192:3:192:3 | [post] getter for .p1 | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:193:3:193:5 | [post] ...! | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:193:3:193:5 | [post] ...! | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| CommandInjection.swift:194:3:194:3 | [post] ...! | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | CommandInjection.swift:194:3:194:3 | [post] ...! | This command depends on a $@. | CommandInjection.swift:180:41:180:95 | call to String.init(contentsOf:) | user-provided value | +| file://:0:0:0:0 | url | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | file://:0:0:0:0 | url | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | +| file://:0:0:0:0 | url | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | file://:0:0:0:0 | url | This command depends on a $@. | CommandInjection.swift:99:40:99:94 | call to String.init(contentsOf:) | user-provided value | diff --git a/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.qlref b/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.qlref new file mode 100644 index 000000000000..3556b36b1857 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-078/CommandInjection.ql \ No newline at end of file diff --git a/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.swift b/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.swift new file mode 100644 index 000000000000..c1facad1305b --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.swift @@ -0,0 +1,198 @@ + +// --- 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 { } } +} + +// --- 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 + } +} + +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.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(string: userControlledString)! // BAD [NOT DETECTED] + task6.arguments = [userControlledString] // BAD [NOT DETECTED] + task6.setArguments([userControlledString]) // BAD [NOT DETECTED] + 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) +} + +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 + } +} diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected index a4fc0d173128..66356d313493 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -17,23 +17,15 @@ edges | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | +| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:138:47:138:47 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:139:48:139:48 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:153:85:153:85 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:154:86:154:86 | remoteURL [some:0] | -| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | -| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | -| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | -| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | +| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:140:47:140:47 | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:141:48:141:48 | remoteURL2 [some:0] | -| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | +| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL [some:0] | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | | UnsafeWebViewFetch.swift:138:47:138:47 | remoteURL [some:0] | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | | UnsafeWebViewFetch.swift:139:48:139:48 | remoteURL [some:0] | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | | UnsafeWebViewFetch.swift:140:47:140:47 | remoteURL2 [some:0] | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | @@ -52,23 +44,15 @@ edges | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | +| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:185:47:185:47 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:186:48:186:48 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:200:90:200:90 | remoteURL [some:0] | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | UnsafeWebViewFetch.swift:201:91:201:91 | remoteURL [some:0] | -| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | -| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | -| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | -| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | +| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:187:47:187:47 | remoteURL2 [some:0] | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | UnsafeWebViewFetch.swift:188:48:188:48 | remoteURL2 [some:0] | -| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | +| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL [some:0] | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | | UnsafeWebViewFetch.swift:185:47:185:47 | remoteURL [some:0] | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | | UnsafeWebViewFetch.swift:186:48:186:48 | remoteURL [some:0] | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | | UnsafeWebViewFetch.swift:187:47:187:47 | remoteURL2 [some:0] | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | @@ -94,12 +78,10 @@ nodes | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:124:25:124:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | semmle.label | "..." | -| UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | semmle.label | call to URL.init(string:) | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | semmle.label | remoteString | -| UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | semmle.label | call to URL.init(string:relativeTo:) | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) [some:0] | semmle.label | call to URL.init(string:relativeTo:) [some:0] | -| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | semmle.label | remoteURL | +| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:138:47:138:47 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | @@ -125,12 +107,10 @@ nodes | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | semmle.label | "..." | -| UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | semmle.label | call to URL.init(string:) | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] | | UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | semmle.label | remoteString | -| UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | semmle.label | call to URL.init(string:relativeTo:) | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) [some:0] | semmle.label | call to URL.init(string:relativeTo:) [some:0] | -| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | semmle.label | remoteURL | +| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL [some:0] | semmle.label | remoteURL [some:0] | | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:185:47:185:47 | remoteURL [some:0] | semmle.label | remoteURL [some:0] |