Skip to content

Commit c21ec1c

Browse files
committed
Swift: Standardize the taint sources, sinks, sanitizers.
1 parent 6928e62 commit c21ec1c

7 files changed

+141
-45
lines changed

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

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,35 @@
44
*/
55

66
import swift
7+
import codeql.swift.security.SensitiveExprs
78
import codeql.swift.dataflow.DataFlow
89

910
/**
10-
* A `DataFlow::Node` that is something stored in a local database.
11+
* A dataflow sink for cleartext database storage vulnerabilities. That is,
12+
* a `DataFlow::Node` that is something stored in a local database.
1113
*/
12-
abstract class Stored extends DataFlow::Node { }
14+
abstract class CleartextStorageDatabaseSink extends DataFlow::Node { }
15+
16+
/**
17+
* A sanitizer for cleartext database storage vulnerabilities.
18+
*/
19+
abstract class CleartextStorageDatabaseSanitizer extends DataFlow::Node { }
20+
21+
/**
22+
* A unit class for adding additional taint steps.
23+
*/
24+
class CleartextStorageDatabaseAdditionalTaintStep extends Unit {
25+
/**
26+
* Holds if the step from `node1` to `node2` should be considered a taint
27+
* step for paths related to cleartext database storage vulnerabilities.
28+
*/
29+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
30+
}
1331

1432
/**
1533
* A `DataFlow::Node` that is an expression stored with the Core Data library.
1634
*/
17-
class CoreDataStore extends Stored {
35+
class CoreDataStore extends CleartextStorageDatabaseSink {
1836
CoreDataStore() {
1937
// values written into Core Data objects through `set*Value` methods are a sink.
2038
exists(CallExpr call |
@@ -42,7 +60,7 @@ class CoreDataStore extends Stored {
4260
* A `DataFlow::Node` that is an expression stored with the Realm database
4361
* library.
4462
*/
45-
class RealmStore extends Stored instanceof DataFlow::PostUpdateNode {
63+
class RealmStore extends CleartextStorageDatabaseSink instanceof DataFlow::PostUpdateNode {
4664
RealmStore() {
4765
// any write into a class derived from `RealmSwiftObject` is a sink. For
4866
// example in `realmObj.data = sensitive` the post-update node corresponding
@@ -59,7 +77,7 @@ class RealmStore extends Stored instanceof DataFlow::PostUpdateNode {
5977
/**
6078
* A `DataFlow::Node` that is an expression stored with the GRDB library.
6179
*/
62-
class GrdbStore extends Stored {
80+
class GrdbStore extends CleartextStorageDatabaseSink {
6381
GrdbStore() {
6482
exists(CallExpr call, MethodDecl method |
6583
call.getStaticTarget() = method and
@@ -110,3 +128,25 @@ class GrdbStore extends Stored {
110128
)
111129
}
112130
}
131+
132+
/**
133+
* An encryption sanitizer for cleartext database storage vulnerabilities.
134+
*/
135+
class CleartextStorageDatabaseEncryptionSanitizer extends CleartextStorageDatabaseSanitizer {
136+
CleartextStorageDatabaseEncryptionSanitizer() {
137+
this.asExpr() instanceof EncryptedExpr
138+
}
139+
}
140+
141+
/**
142+
* An additional taint step for cleartext database storage vulnerabilities.
143+
* Needed until we have proper content flow through arrays.
144+
*/
145+
class CleartextStorageDatabaseArrayAdditionalTaintStep extends CleartextStorageDatabaseAdditionalTaintStep {
146+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
147+
exists(ArrayExpr arr |
148+
nodeFrom.asExpr() = arr.getAnElement() and
149+
nodeTo.asExpr() = arr
150+
)
151+
}
152+
}

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

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,19 @@ class CleartextStorageConfig extends TaintTracking::Configuration {
1818

1919
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
2020

21-
override predicate isSink(DataFlow::Node node) { node instanceof Stored }
21+
override predicate isSink(DataFlow::Node node) { node instanceof CleartextStorageDatabaseSink }
2222

23-
override predicate isSanitizerIn(DataFlow::Node node) {
24-
// make sources barriers so that we only report the closest instance
25-
isSource(node)
23+
override predicate isSanitizer(DataFlow::Node sanitizer) {
24+
sanitizer instanceof CleartextStorageDatabaseSanitizer
2625
}
2726

28-
override predicate isSanitizer(DataFlow::Node node) {
29-
// encryption barrier
30-
node.asExpr() instanceof EncryptedExpr
27+
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
28+
any(CleartextStorageDatabaseAdditionalTaintStep s).step(nodeFrom, nodeTo)
3129
}
3230

33-
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
34-
// Needed until we have proper content flow through arrays
35-
exists(ArrayExpr arr |
36-
node1.asExpr() = arr.getAnElement() and
37-
node2.asExpr() = arr
38-
)
31+
override predicate isSanitizerIn(DataFlow::Node node) {
32+
// make sources barriers so that we only report the closest instance
33+
isSource(node)
3934
}
4035

4136
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {

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

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,36 @@
44
*/
55

66
import swift
7+
import codeql.swift.security.SensitiveExprs
78
import codeql.swift.dataflow.DataFlow
89

910
/**
10-
* A `DataFlow::Node` of something that gets stored in an application preference store.
11+
* A dataflow sink for cleartext preferences storage vulnerabilities. That is,
12+
* a `DataFlow::Node` of something that gets stored in an application
13+
* preference store.
1114
*/
12-
abstract class Stored extends DataFlow::Node {
15+
abstract class CleartextStoragePreferencesSink extends DataFlow::Node {
1316
abstract string getStoreName();
1417
}
1518

19+
/**
20+
* A sanitizer for cleartext preferences storage vulnerabilities.
21+
*/
22+
abstract class CleartextStoragePreferencesSanitizer extends DataFlow::Node { }
23+
24+
/**
25+
* A unit class for adding additional taint steps.
26+
*/
27+
class CleartextStoragePreferencesAdditionalTaintStep extends Unit {
28+
/**
29+
* Holds if the step from `node1` to `node2` should be considered a taint
30+
* step for paths related to cleartext preferences storage vulnerabilities.
31+
*/
32+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
33+
}
34+
1635
/** The `DataFlow::Node` of an expression that gets written to the user defaults database */
17-
class UserDefaultsStore extends Stored {
36+
class UserDefaultsStore extends CleartextStoragePreferencesSink {
1837
UserDefaultsStore() {
1938
exists(CallExpr call |
2039
call.getStaticTarget().(MethodDecl).hasQualifiedName("UserDefaults", "set(_:forKey:)") and
@@ -26,7 +45,7 @@ class UserDefaultsStore extends Stored {
2645
}
2746

2847
/** The `DataFlow::Node` of an expression that gets written to the iCloud-backed NSUbiquitousKeyValueStore */
29-
class NSUbiquitousKeyValueStore extends Stored {
48+
class NSUbiquitousKeyValueStore extends CleartextStoragePreferencesSink {
3049
NSUbiquitousKeyValueStore() {
3150
exists(CallExpr call |
3251
call.getStaticTarget()
@@ -45,8 +64,17 @@ class NSUbiquitousKeyValueStore extends Stored {
4564
* object via reflection (`perform(Selector)`) or the `NSKeyValueCoding`,
4665
* `NSKeyValueBindingCreation` APIs. (TODO)
4766
*/
48-
class NSUserDefaultsControllerStore extends Stored {
67+
class NSUserDefaultsControllerStore extends CleartextStoragePreferencesSink {
4968
NSUserDefaultsControllerStore() { none() }
5069

5170
override string getStoreName() { result = "the user defaults database" }
5271
}
72+
73+
/**
74+
* An encryption sanitizer for cleartext preferences storage vulnerabilities.
75+
*/
76+
class CleartextStoragePreferencesEncryptionSanitizer extends CleartextStoragePreferencesSanitizer {
77+
CleartextStoragePreferencesEncryptionSanitizer() {
78+
this.asExpr() instanceof EncryptedExpr
79+
}
80+
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ class CleartextStorageConfig extends TaintTracking::Configuration {
1818

1919
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
2020

21-
override predicate isSink(DataFlow::Node node) { node instanceof Stored }
21+
override predicate isSink(DataFlow::Node node) { node instanceof CleartextStoragePreferencesSink }
22+
23+
override predicate isSanitizer(DataFlow::Node sanitizer) {
24+
sanitizer instanceof CleartextStoragePreferencesSanitizer
25+
}
26+
27+
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
28+
any(CleartextStoragePreferencesAdditionalTaintStep s).step(nodeFrom, nodeTo)
29+
}
2230

2331
override predicate isSanitizerIn(DataFlow::Node node) {
2432
// make sources barriers so that we only report the closest instance
2533
this.isSource(node)
2634
}
27-
28-
override predicate isSanitizer(DataFlow::Node node) {
29-
// encryption barrier
30-
node.asExpr() instanceof EncryptedExpr
31-
}
3235
}

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

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,42 @@
44
*/
55

66
import swift
7+
import codeql.swift.security.SensitiveExprs
78
import codeql.swift.dataflow.DataFlow
89

910
/**
10-
* An `Expr` that is transmitted over a network.
11+
* A dataflow sink for cleartext transmission vulnerabilities. That is,
12+
* a `DataFlow::Node` of something that is transmitted over a network.
1113
*/
12-
abstract class Transmitted extends Expr { }
14+
abstract class CleartextTransmissionSink extends DataFlow::Node { }
15+
16+
/**
17+
* A sanitizer for cleartext transmission vulnerabilities.
18+
*/
19+
abstract class CleartextTransmissionSanitizer extends DataFlow::Node { }
20+
21+
/**
22+
* A unit class for adding additional taint steps.
23+
*/
24+
class CleartextTransmissionAdditionalTaintStep extends Unit {
25+
/**
26+
* Holds if the step from `node1` to `node2` should be considered a taint
27+
* step for paths related to cleartext transmission vulnerabilities.
28+
*/
29+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
30+
}
1331

1432
/**
1533
* An `Expr` that is transmitted with `NWConnection.send`.
1634
*/
17-
class NWConnectionSend extends Transmitted {
35+
class NWConnectionSend extends CleartextTransmissionSink {
1836
NWConnectionSend() {
1937
// `content` arg to `NWConnection.send` is a sink
2038
exists(CallExpr call |
2139
call.getStaticTarget()
2240
.(MethodDecl)
2341
.hasQualifiedName("NWConnection", "send(content:contentContext:isComplete:completion:)") and
24-
call.getArgument(0).getExpr() = this
42+
call.getArgument(0).getExpr() = this.asExpr()
2543
)
2644
}
2745
}
@@ -30,33 +48,42 @@ class NWConnectionSend extends Transmitted {
3048
* An `Expr` that is used to form a `URL`. Such expressions are very likely to
3149
* be transmitted over a network, because that's what URLs are for.
3250
*/
33-
class Url extends Transmitted {
51+
class Url extends CleartextTransmissionSink {
3452
Url() {
3553
// `string` arg in `URL.init` is a sink
3654
// (we assume here that the URL goes on to be used in a network operation)
3755
exists(CallExpr call |
3856
call.getStaticTarget()
3957
.(MethodDecl)
4058
.hasQualifiedName("URL", ["init(string:)", "init(string:relativeTo:)"]) and
41-
call.getArgument(0).getExpr() = this
59+
call.getArgument(0).getExpr() = this.asExpr()
4260
)
4361
}
4462
}
4563

4664
/**
4765
* An `Expr` that transmitted through the Alamofire library.
4866
*/
49-
class AlamofireTransmitted extends Transmitted {
67+
class AlamofireTransmitted extends CleartextTransmissionSink {
5068
AlamofireTransmitted() {
5169
// sinks are the first argument containing the URL, and the `parameters`
5270
// and `headers` arguments to appropriate methods of `Session`.
5371
exists(CallExpr call, string fName |
5472
call.getStaticTarget().(MethodDecl).hasQualifiedName("Session", fName) and
5573
fName.regexpMatch("(request|streamRequest|download)\\(.*") and
5674
(
57-
call.getArgument(0).getExpr() = this or
58-
call.getArgumentWithLabel(["headers", "parameters"]).getExpr() = this
75+
call.getArgument(0).getExpr() = this.asExpr() or
76+
call.getArgumentWithLabel(["headers", "parameters"]).getExpr() = this.asExpr()
5977
)
6078
)
6179
}
6280
}
81+
82+
/**
83+
* An encryption sanitizer for cleartext transmission vulnerabilities.
84+
*/
85+
class CleartextTransmissionEncryptionSanitizer extends CleartextTransmissionSanitizer {
86+
CleartextTransmissionEncryptionSanitizer() {
87+
this.asExpr() instanceof EncryptedExpr
88+
}
89+
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ class CleartextTransmissionConfig extends TaintTracking::Configuration {
1818

1919
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr }
2020

21-
override predicate isSink(DataFlow::Node node) { node.asExpr() instanceof Transmitted }
21+
override predicate isSink(DataFlow::Node node) { node instanceof CleartextTransmissionSink }
22+
23+
override predicate isSanitizer(DataFlow::Node sanitizer) {
24+
sanitizer instanceof CleartextTransmissionSanitizer
25+
}
26+
27+
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
28+
any(CleartextTransmissionAdditionalTaintStep s).step(nodeFrom, nodeTo)
29+
}
2230

2331
override predicate isSanitizerIn(DataFlow::Node node) {
2432
// make sources barriers so that we only report the closest instance
2533
isSource(node)
2634
}
27-
28-
override predicate isSanitizer(DataFlow::Node node) {
29-
// encryption barrier
30-
node.asExpr() instanceof EncryptedExpr
31-
}
3235
}

swift/ql/src/queries/Security/CWE-312/CleartextStoragePreferences.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ from CleartextStorageConfig config, DataFlow::PathNode sourceNode, DataFlow::Pat
2929
where config.hasFlowPath(sourceNode, sinkNode)
3030
select cleanupNode(sinkNode.getNode()), sourceNode, sinkNode,
3131
"This operation stores '" + sinkNode.getNode().toString() + "' in " +
32-
sinkNode.getNode().(Stored).getStoreName() +
32+
sinkNode.getNode().(CleartextStoragePreferencesSink).getStoreName() +
3333
". It may contain unencrypted sensitive data from $@.", sourceNode,
3434
sourceNode.getNode().toString()

0 commit comments

Comments
 (0)