Swift: Add tests.

This commit is contained in:
Geoffrey White
2023-07-31 15:31:30 +01:00
parent 2664c306d3
commit 8c2140b28d
3 changed files with 211 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
edges
| CommandInjection.swift:38:22:38:33 | command | CommandInjection.swift:42:16:42:16 | command |
| CommandInjection.swift:38:22:38:33 | command [some:0] | CommandInjection.swift:42:16:42:16 | command [some:0] |
| CommandInjection.swift:42:16:42:16 | command | CommandInjection.swift:42:16:42:16 | command [some:0] |
| CommandInjection.swift:49:8:49:12 | let ...? [some:0, some:0] | CommandInjection.swift:49:12:49:12 | userControlledString [some:0] |
| CommandInjection.swift:49:8:49:12 | let ...? [some:0] | CommandInjection.swift:49:12:49:12 | userControlledString |
| CommandInjection.swift:49:12:49:12 | userControlledString | CommandInjection.swift:55:27:55:27 | userControlledString |
| CommandInjection.swift:49:12:49:12 | userControlledString | CommandInjection.swift:58:43:58:43 | userControlledString |
| CommandInjection.swift:49:12:49:12 | userControlledString [some:0] | CommandInjection.swift:58:43:58:43 | userControlledString [some:0] |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0, some:0] | CommandInjection.swift:49:8:49:12 | let ...? [some:0, some:0] |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:49:8:49:12 | let ...? [some:0] |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0, some:0] |
| CommandInjection.swift:58:5:58:9 | let ...? [some:0] | CommandInjection.swift:58:9:58:9 | validatedString |
| CommandInjection.swift:58:9:58:9 | validatedString | CommandInjection.swift:61:31:61:31 | validatedString |
| CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] | CommandInjection.swift:58:5:58:9 | let ...? [some:0] |
| CommandInjection.swift:58:43:58:43 | userControlledString | CommandInjection.swift:38:22:38:33 | command |
| CommandInjection.swift:58:43:58:43 | userControlledString | CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] |
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | CommandInjection.swift:38:22:38:33 | command [some:0] |
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] |
| CommandInjection.swift:79:8:79:12 | let ...? [some:0] | CommandInjection.swift:79:12:79:12 | userControlledString |
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:94:36:94:36 | userControlledString |
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:95:28:95:28 | userControlledString |
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:99:45:99:45 | userControlledString |
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:100:28:100:36 | ... .+(_:_:) ... |
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:104:46:104:46 | userControlledString |
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:105:22:105:22 | userControlledString |
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) [some:0] |
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:79:8:79:12 | let ...? [some:0] |
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) | CommandInjection.swift:94:24:94:56 | call to URL.init(string:) [some:0] |
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) | CommandInjection.swift:94:24:94:57 | ...! |
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:94:24:94:57 | ...! |
| CommandInjection.swift:94:36:94:36 | userControlledString | CommandInjection.swift:94:24:94:56 | call to URL.init(string:) |
| CommandInjection.swift:99:45:99:45 | userControlledString | CommandInjection.swift:99:24:99:65 | call to URL.init(fileURLWithPath:) |
| CommandInjection.swift:104:46:104:46 | userControlledString | CommandInjection.swift:104:25:104:66 | call to URL.init(fileURLWithPath:) |
nodes
| CommandInjection.swift:38:22:38:33 | command | semmle.label | command |
| CommandInjection.swift:38:22:38:33 | command [some:0] | semmle.label | command [some:0] |
| CommandInjection.swift:42:16:42:16 | command | semmle.label | command |
| CommandInjection.swift:42:16:42:16 | command [some:0] | semmle.label | command [some:0] |
| CommandInjection.swift:42:16:42:16 | command [some:0] | semmle.label | command [some:0] |
| CommandInjection.swift:49:8:49:12 | let ...? [some:0, some:0] | semmle.label | let ...? [some:0, some:0] |
| CommandInjection.swift:49:8:49:12 | let ...? [some:0] | semmle.label | let ...? [some:0] |
| CommandInjection.swift:49:12:49:12 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:49:12:49:12 | userControlledString [some:0] | semmle.label | userControlledString [some:0] |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0, some:0] | semmle.label | call to String.init(contentsOf:) [some:0, some:0] |
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] |
| CommandInjection.swift:55:27:55:27 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:58:5:58:9 | let ...? [some:0] | semmle.label | let ...? [some:0] |
| CommandInjection.swift:58:9:58:9 | validatedString | semmle.label | validatedString |
| CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] | semmle.label | call to validateCommand(_:) [some:0] |
| CommandInjection.swift:58:43:58:43 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | semmle.label | userControlledString [some:0] |
| CommandInjection.swift:61:31:61:31 | validatedString | semmle.label | validatedString |
| CommandInjection.swift:79:8:79:12 | let ...? [some:0] | semmle.label | let ...? [some:0] |
| CommandInjection.swift:79:12:79:12 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) |
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] |
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) | semmle.label | call to URL.init(string:) |
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] |
| CommandInjection.swift:94:24:94:57 | ...! | semmle.label | ...! |
| CommandInjection.swift:94:36:94:36 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:95:28:95:28 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:99:24:99:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) |
| CommandInjection.swift:99:45:99:45 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:100:28:100:36 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| CommandInjection.swift:104:25:104:66 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) |
| CommandInjection.swift:104:46:104:46 | userControlledString | semmle.label | userControlledString |
| CommandInjection.swift:105:22:105:22 | userControlledString | semmle.label | userControlledString |
subpaths
| CommandInjection.swift:58:43:58:43 | userControlledString | CommandInjection.swift:38:22:38:33 | command | CommandInjection.swift:42:16:42:16 | command [some:0] | CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] |
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | CommandInjection.swift:38:22:38:33 | command [some:0] | CommandInjection.swift:42:16:42:16 | command [some:0] | CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] |
#select
| CommandInjection.swift:55:27:55:27 | userControlledString | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | CommandInjection.swift:55:27:55:27 | userControlledString | This command depends on a $@. | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:61:31:61:31 | validatedString | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | CommandInjection.swift:61:31:61:31 | validatedString | This command depends on a $@. | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:94:24:94:57 | ...! | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:94:24:94:57 | ...! | This command depends on a $@. | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:95:28:95:28 | userControlledString | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:95:28:95:28 | userControlledString | This command depends on a $@. | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:99:24:99:65 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:99:24:99:65 | call to URL.init(fileURLWithPath:) | This command depends on a $@. | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:100:28:100:36 | ... .+(_:_:) ... | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:100:28:100:36 | ... .+(_:_:) ... | This command depends on a $@. | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:104:25:104:66 | call to URL.init(fileURLWithPath:) | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:104:25:104:66 | call to URL.init(fileURLWithPath:) | This command depends on a $@. | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | user-provided value |
| CommandInjection.swift:105:22:105:22 | userControlledString | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:105:22:105:22 | userControlledString | This command depends on a $@. | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | user-provided value |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-078/CommandInjection.ql

View File

@@ -0,0 +1,128 @@
// --- 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("")
}
}
// --- 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 [NOT DETECTED]
_ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // BAD [NOT DETECTED]
_ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", mySafeString]) // GOOD
_ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // BAD [NOT DETECTED]
_ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // BAD [NOT DETECTED]
}