Skip to content

Commit cea3477

Browse files
committed
Qhelp and examples
1 parent e45a317 commit cea3477

File tree

6 files changed

+106
-1
lines changed

6 files changed

+106
-1
lines changed

swift/ql/lib/codeql/swift/security/CommandInjectionExtensions.qll

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,26 @@ private class ProcessType extends NominalType {
4242
ProcessType() { this.getFullName() = "Process" }
4343
}
4444

45+
/**
46+
* A `DataFlow::Node` that is an expression stored with the Realm database
47+
* library.
48+
*/
49+
private class ProcessSink extends CommandInjectionSink instanceof DataFlow::Node {
50+
ProcessSink() {
51+
// any write into a class derived from `Process` is a sink. For
52+
// example in `Process.launchPath = sensitive` the post-update node corresponding
53+
// with `Process.launchPath` is a sink.
54+
exists(NominalType t, Expr e |
55+
t.getABaseType*().getUnderlyingType().getName() = "Process" and
56+
e.getFullyConverted() = this.asExpr() and
57+
e.getFullyConverted().getType() = t
58+
)
59+
}
60+
}
61+
4562
/**
4663
* A sink defined in a CSV model.
4764
*/
4865
private class DefaultCommandInjectionSink extends CommandInjectionSink {
49-
DefaultCommandInjectionSink() { sinkNode(this, "command-injection") }
66+
DefaultCommandInjectionSink() { sinkNode(this, "command-line-injection") }
5067
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* Added new query "Command injection" (`swift/command-line-injection`). The query finds places where user input is used to execute system commands without proper escaping.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Constructing a system command with unsanitized user input is dangerous,
9+
since a malicious user to execute code.
10+
</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>
15+
If possible, use hard-coded string literals to specify the command to run. Instead of interpreting
16+
user input directly as command names, examine the input and then choose among hard-coded string
17+
literals.
18+
</p>
19+
<p>
20+
If this is not possible, then add sanitization code to verify that the user input is safe before
21+
using it.
22+
</p>
23+
</recommendation>
24+
25+
<example>
26+
<p>
27+
The following examples execute code from user input without
28+
sanitizing it first:
29+
</p>
30+
<sample src="CommandInjectionBad.swift" />
31+
<p>
32+
If user input is used to construct a regular expression it should be checked
33+
first. This ensures that the user cannot insert characters that have special
34+
meanings.
35+
</p>
36+
<sample src="CommandInjectionGood.swift" />
37+
</example>
38+
39+
<references>
40+
<li>
41+
OWASP:
42+
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
43+
</li>
44+
</references>
45+
</qhelp>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name System command built from user-controlled sources
3+
* @description Building a system command from user-controlled sources is vulnerable to insertion of malicious code by the user.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @security-severity 9.8
7+
* @precision high
8+
* @id swift/command-line-injection
9+
* @tags security
10+
* external/cwe/cwe-078
11+
*/
12+
13+
import swift
14+
import codeql.swift.dataflow.DataFlow
15+
import codeql.swift.security.CommandInjectionQuery
16+
import CommandInjectionFlow::PathGraph
17+
18+
from CommandInjectionFlow::PathNode sourceNode, CommandInjectionFlow::PathNode sinkNode
19+
where CommandInjectionFlow::flowPath(sourceNode, sinkNode)
20+
select sinkNode.getNode(), sourceNode, sinkNode, "This command depends on a $@.",
21+
sourceNode.getNode(), "user-provided value"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var task = Process()
2+
task.launchPath = "/bin/bash"
3+
task.arguments = ["-c", userControlledString] // BAD
4+
5+
task.launch()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
func validateCommand(_ command: String) -> String? {
2+
let allowedCommands = ["ls -l", "pwd", "echo"]
3+
if allowedCommands.contains(command) {
4+
return command
5+
}
6+
return nil
7+
}
8+
9+
var task = Process()
10+
task.launchPath = "/bin/bash"
11+
task.arguments = ["-c", validateCommand(userControlledString)] // GOOD
12+
13+
task.launch()

0 commit comments

Comments
 (0)