Skip to content

Commit 8c2140b

Browse files
committed
Swift: Add tests.
1 parent 2664c30 commit 8c2140b

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
edges
2+
| CommandInjection.swift:38:22:38:33 | command | CommandInjection.swift:42:16:42:16 | command |
3+
| CommandInjection.swift:38:22:38:33 | command [some:0] | CommandInjection.swift:42:16:42:16 | command [some:0] |
4+
| CommandInjection.swift:42:16:42:16 | command | CommandInjection.swift:42:16:42:16 | command [some:0] |
5+
| CommandInjection.swift:49:8:49:12 | let ...? [some:0, some:0] | CommandInjection.swift:49:12:49:12 | userControlledString [some:0] |
6+
| CommandInjection.swift:49:8:49:12 | let ...? [some:0] | CommandInjection.swift:49:12:49:12 | userControlledString |
7+
| CommandInjection.swift:49:12:49:12 | userControlledString | CommandInjection.swift:55:27:55:27 | userControlledString |
8+
| CommandInjection.swift:49:12:49:12 | userControlledString | CommandInjection.swift:58:43:58:43 | userControlledString |
9+
| CommandInjection.swift:49:12:49:12 | userControlledString [some:0] | CommandInjection.swift:58:43:58:43 | userControlledString [some:0] |
10+
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] |
11+
| 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] |
12+
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:49:8:49:12 | let ...? [some:0] |
13+
| 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] |
14+
| CommandInjection.swift:58:5:58:9 | let ...? [some:0] | CommandInjection.swift:58:9:58:9 | validatedString |
15+
| CommandInjection.swift:58:9:58:9 | validatedString | CommandInjection.swift:61:31:61:31 | validatedString |
16+
| CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] | CommandInjection.swift:58:5:58:9 | let ...? [some:0] |
17+
| CommandInjection.swift:58:43:58:43 | userControlledString | CommandInjection.swift:38:22:38:33 | command |
18+
| CommandInjection.swift:58:43:58:43 | userControlledString | CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] |
19+
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | CommandInjection.swift:38:22:38:33 | command [some:0] |
20+
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] |
21+
| CommandInjection.swift:79:8:79:12 | let ...? [some:0] | CommandInjection.swift:79:12:79:12 | userControlledString |
22+
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:94:36:94:36 | userControlledString |
23+
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:95:28:95:28 | userControlledString |
24+
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:99:45:99:45 | userControlledString |
25+
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:100:28:100:36 | ... .+(_:_:) ... |
26+
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:104:46:104:46 | userControlledString |
27+
| CommandInjection.swift:79:12:79:12 | userControlledString | CommandInjection.swift:105:22:105:22 | userControlledString |
28+
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) [some:0] |
29+
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) [some:0] | CommandInjection.swift:79:8:79:12 | let ...? [some:0] |
30+
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) | CommandInjection.swift:94:24:94:56 | call to URL.init(string:) [some:0] |
31+
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) | CommandInjection.swift:94:24:94:57 | ...! |
32+
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) [some:0] | CommandInjection.swift:94:24:94:57 | ...! |
33+
| CommandInjection.swift:94:36:94:36 | userControlledString | CommandInjection.swift:94:24:94:56 | call to URL.init(string:) |
34+
| CommandInjection.swift:99:45:99:45 | userControlledString | CommandInjection.swift:99:24:99:65 | call to URL.init(fileURLWithPath:) |
35+
| CommandInjection.swift:104:46:104:46 | userControlledString | CommandInjection.swift:104:25:104:66 | call to URL.init(fileURLWithPath:) |
36+
nodes
37+
| CommandInjection.swift:38:22:38:33 | command | semmle.label | command |
38+
| CommandInjection.swift:38:22:38:33 | command [some:0] | semmle.label | command [some:0] |
39+
| CommandInjection.swift:42:16:42:16 | command | semmle.label | command |
40+
| CommandInjection.swift:42:16:42:16 | command [some:0] | semmle.label | command [some:0] |
41+
| CommandInjection.swift:42:16:42:16 | command [some:0] | semmle.label | command [some:0] |
42+
| CommandInjection.swift:49:8:49:12 | let ...? [some:0, some:0] | semmle.label | let ...? [some:0, some:0] |
43+
| CommandInjection.swift:49:8:49:12 | let ...? [some:0] | semmle.label | let ...? [some:0] |
44+
| CommandInjection.swift:49:12:49:12 | userControlledString | semmle.label | userControlledString |
45+
| CommandInjection.swift:49:12:49:12 | userControlledString [some:0] | semmle.label | userControlledString [some:0] |
46+
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) |
47+
| 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] |
48+
| CommandInjection.swift:49:40:49:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] |
49+
| CommandInjection.swift:55:27:55:27 | userControlledString | semmle.label | userControlledString |
50+
| CommandInjection.swift:58:5:58:9 | let ...? [some:0] | semmle.label | let ...? [some:0] |
51+
| CommandInjection.swift:58:9:58:9 | validatedString | semmle.label | validatedString |
52+
| CommandInjection.swift:58:27:58:63 | call to validateCommand(_:) [some:0] | semmle.label | call to validateCommand(_:) [some:0] |
53+
| CommandInjection.swift:58:43:58:43 | userControlledString | semmle.label | userControlledString |
54+
| CommandInjection.swift:58:43:58:43 | userControlledString [some:0] | semmle.label | userControlledString [some:0] |
55+
| CommandInjection.swift:61:31:61:31 | validatedString | semmle.label | validatedString |
56+
| CommandInjection.swift:79:8:79:12 | let ...? [some:0] | semmle.label | let ...? [some:0] |
57+
| CommandInjection.swift:79:12:79:12 | userControlledString | semmle.label | userControlledString |
58+
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) |
59+
| CommandInjection.swift:79:40:79:94 | call to String.init(contentsOf:) [some:0] | semmle.label | call to String.init(contentsOf:) [some:0] |
60+
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) | semmle.label | call to URL.init(string:) |
61+
| CommandInjection.swift:94:24:94:56 | call to URL.init(string:) [some:0] | semmle.label | call to URL.init(string:) [some:0] |
62+
| CommandInjection.swift:94:24:94:57 | ...! | semmle.label | ...! |
63+
| CommandInjection.swift:94:36:94:36 | userControlledString | semmle.label | userControlledString |
64+
| CommandInjection.swift:95:28:95:28 | userControlledString | semmle.label | userControlledString |
65+
| CommandInjection.swift:99:24:99:65 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) |
66+
| CommandInjection.swift:99:45:99:45 | userControlledString | semmle.label | userControlledString |
67+
| CommandInjection.swift:100:28:100:36 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
68+
| CommandInjection.swift:104:25:104:66 | call to URL.init(fileURLWithPath:) | semmle.label | call to URL.init(fileURLWithPath:) |
69+
| CommandInjection.swift:104:46:104:46 | userControlledString | semmle.label | userControlledString |
70+
| CommandInjection.swift:105:22:105:22 | userControlledString | semmle.label | userControlledString |
71+
subpaths
72+
| 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] |
73+
| 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] |
74+
#select
75+
| 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 |
76+
| 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 |
77+
| 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 |
78+
| 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 |
79+
| 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 |
80+
| 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 |
81+
| 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 |
82+
| 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 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE-078/CommandInjection.ql
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
2+
// --- stubs ---
3+
4+
struct URL
5+
{
6+
init?(string: String) {}
7+
init(fileURLWithPath: String) {}
8+
}
9+
10+
class NSObject {
11+
}
12+
13+
class Process : NSObject {
14+
var launchPath: String? { get { nil } set {} }
15+
var arguments: [String]? { get { nil } set {} }
16+
func launch() {}
17+
18+
var executableURL: URL? { get { nil } set {} }
19+
func run() throws {}
20+
21+
class func launchedProcess(launchPath path: String, arguments: [String]) -> Process {
22+
return Process()
23+
}
24+
25+
class func run(_ url: URL, arguments: [String], terminationHandler: (@Sendable (Process) -> Void)? = nil) throws -> Process {
26+
return Process()
27+
}
28+
}
29+
30+
extension String {
31+
init(contentsOf url: URL) throws {
32+
self.init("")
33+
}
34+
}
35+
36+
// --- tests ---
37+
38+
func validateCommand(_ command: String) -> String? {
39+
let allowedCommands = ["ls -l", "pwd", "echo"]
40+
41+
if allowedCommands.contains(command) {
42+
return command
43+
}
44+
45+
return nil
46+
}
47+
48+
func testCommandInjectionQhelpExamples() {
49+
guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else {
50+
return
51+
}
52+
53+
let task1 = Process()
54+
task1.launchPath = "/bin/bash" // GOOD
55+
task1.arguments = ["-c", userControlledString] // BAD
56+
task1.launch()
57+
58+
if let validatedString = validateCommand(userControlledString) {
59+
let task2 = Process()
60+
task2.launchPath = "/bin/bash" // GOOD
61+
task2.arguments = ["-c", validatedString] // GOOD [FALSE POSITIVE]
62+
task2.launch()
63+
}
64+
}
65+
66+
func mkProcess() -> Process? {
67+
return Process()
68+
}
69+
70+
class MyProcess : Process {
71+
var harmlessField: String?
72+
73+
func setArguments(_ arguments: [String]) {
74+
self.arguments = arguments
75+
}
76+
}
77+
78+
func testCommandInjectionMore(mySafeString: String) {
79+
guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else {
80+
return
81+
}
82+
83+
let task1 = Process()
84+
task1.executableURL = URL(string: mySafeString)! // GOOD
85+
task1.arguments = ["abc"] // GOOD
86+
try! task1.run()
87+
88+
let task2 = Process()
89+
task2.executableURL = URL(fileURLWithPath: mySafeString) // GOOD
90+
task2.arguments = ["abc", "def"] // GOOD
91+
try! task2.run()
92+
93+
let task3 = Process()
94+
task3.executableURL = URL(string: userControlledString)! // BAD
95+
task3.arguments = ["abc", userControlledString] // BAD
96+
try! task3.run()
97+
98+
let task4 = Process()
99+
task4.executableURL = URL(fileURLWithPath: userControlledString) // BAD
100+
task4.arguments = ["abc", "def" + userControlledString] // BAD
101+
try! task4.run()
102+
103+
let task5 = mkProcess()
104+
task5?.executableURL = URL(fileURLWithPath: userControlledString) // BAD
105+
task5?.arguments = [userControlledString] // BAD
106+
try! task5?.run()
107+
108+
let task6 = MyProcess()
109+
task6.executableURL = URL(string: userControlledString)! // BAD [NOT DETECTED]
110+
task6.arguments = [userControlledString] // BAD [NOT DETECTED]
111+
task6.setArguments([userControlledString]) // BAD [NOT DETECTED]
112+
task6.harmlessField = userControlledString // GOOD
113+
try! task6.run()
114+
115+
let task7 = Process()
116+
task7.executableURL = URL(fileURLWithPath: mySafeString) // GOOD
117+
task7.arguments = ["abc", "def"]
118+
task7.arguments?.append(userControlledString) // BAD [NOT DETECTED]
119+
try! task7.run()
120+
121+
_ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", mySafeString]) // GOOD
122+
_ = Process.launchedProcess(launchPath: userControlledString, arguments: ["abc", mySafeString]) // BAD [NOT DETECTED]
123+
_ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // BAD [NOT DETECTED]
124+
125+
_ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", mySafeString]) // GOOD
126+
_ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // BAD [NOT DETECTED]
127+
_ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // BAD [NOT DETECTED]
128+
}

0 commit comments

Comments
 (0)