Skip to content

Commit 9a2ac65

Browse files
authored
Merge pull request #14394 from geoffw0/sqlpathinject3
Swift: Add sinks for sqlite3 and SQLite.swift to swift/hardcoded-key
2 parents 1297acf + 0d562d4 commit 9a2ac65

File tree

8 files changed

+182
-2
lines changed

8 files changed

+182
-2
lines changed

swift/ql/lib/codeql/swift/frameworks/SQL/SQL.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ private class FilePathSummaries extends SummaryModelCsv {
1818
";Expression;true;init(_:_:);;;Argument[1].CollectionElement;ReturnValue;taint",
1919
";ExpressionType;true;init(_:);;;Argument[0];ReturnValue;taint",
2020
";ExpressionType;true;replace(_:with:);;;Argument[1];ReturnValue;taint",
21+
";Blob;true;init(bytes:);;;Argument[0];ReturnValue;taint",
22+
";Blob;true;init(bytes:length:);;;Argument[0];ReturnValue;taint",
2123
]
2224
}
2325
}

swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Data.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ private class DataSummaries extends SummaryModelCsv {
4444
";Data;true;shuffled();;;Argument[-1];ReturnValue;taint",
4545
";Data;true;shuffled(using:);;;Argument[-1];ReturnValue;taint",
4646
";Data;true;trimmingPrefix(_:);;;Argument[-1];ReturnValue;taint",
47-
";Data;true;trimmingPrefix(while:);;;Argument[-1];ReturnValue;taint"
47+
";Data;true;trimmingPrefix(while:);;;Argument[-1];ReturnValue;taint",
48+
";Data;true;withUnsafeBytes(_:);;;Argument[-1];Argument[0].Parameter[0].CollectionElement;taint",
49+
";Data;true;withUnsafeBytes(_:);;;Argument[-1].CollectionElement;Argument[0].Parameter[0].CollectionElement;taint",
50+
";Data;true;withUnsafeBytes(_:);;;Argument[0].ReturnValue;ReturnValue;value",
51+
";Data;true;withUnsafeMutableBytes(_:);;;Argument[-1];Argument[0].Parameter[0].CollectionElement;taint",
52+
";Data;true;withUnsafeMutableBytes(_:);;;Argument[-1].CollectionElement;Argument[0].Parameter[0].CollectionElement;taint",
53+
";Data;true;withUnsafeMutableBytes(_:);;;Argument[0].Parameter[0].CollectionElement;Argument[-1].CollectionElement;value",
54+
";Data;true;withUnsafeMutableBytes(_:);;;Argument[0].ReturnValue;ReturnValue;value",
4855
]
4956
}
5057
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ private class EncryptionKeySinks extends SinkModelCsv {
7171
";Realm.Configuration;true;init(fileURL:inMemoryIdentifier:syncConfiguration:encryptionKey:readOnly:schemaVersion:migrationBlock:deleteRealmIfMigrationNeeded:shouldCompactOnLaunch:objectTypes:);;;Argument[3];encryption-key",
7272
";Realm.Configuration;true;init(fileURL:inMemoryIdentifier:syncConfiguration:encryptionKey:readOnly:schemaVersion:migrationBlock:deleteRealmIfMigrationNeeded:shouldCompactOnLaunch:objectTypes:seedFilePath:);;;Argument[3];encryption-key",
7373
";Realm.Configuration;true;encryptionKey;;;PostUpdate;encryption-key",
74+
// sqlite3 C API (Encryption Extension)
75+
";;false;sqlite3_key(_:_:_:);;;Argument[1];encryption-key",
76+
";;false;sqlite3_rekey(_:_:_:);;;Argument[1];encryption-key",
77+
";;false;sqlite3_key_v2(_:_:_:_:);;;Argument[2];encryption-key",
78+
";;false;sqlite3_rekey_v2(_:_:_:_:);;;Argument[2];encryption-key",
79+
// SQLite.swift
80+
";Connection;true;key(_:db:);;;Argument[0];encryption-key",
81+
";Connection;true;keyAndMigrate(_:db:);;;Argument[0];encryption-key",
82+
";Connection;true;rekey(_:db:);;;Argument[0];encryption-key",
83+
";Connection;true;sqlcipher_export(_:key:);;;Argument[1];encryption-key",
7484
// GRDB
7585
";Database;true;usePassphrase(_:);;;Argument[0];encryption-key",
7686
";Database;true;changePassphrase(_:);;;Argument[0];encryption-key",

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ abstract class KeySource extends Expr { }
1717
* A literal byte array is a key source.
1818
*/
1919
class ByteArrayLiteralSource extends KeySource {
20-
ByteArrayLiteralSource() { this = any(ArrayExpr arr | arr.getType().getName() = "Array<UInt8>") }
20+
ByteArrayLiteralSource() {
21+
this = any(ArrayExpr arr | arr.getType().getName() = ["Array<UInt8>", "[UInt8]"])
22+
}
2123
}
2224

2325
/**
@@ -39,6 +41,12 @@ module HardcodedKeyConfig implements DataFlow::ConfigSig {
3941
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
4042
any(HardcodedEncryptionKeyAdditionalFlowStep s).step(nodeFrom, nodeTo)
4143
}
44+
45+
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
46+
// flow out of collections at the sink
47+
isSink(node) and
48+
c.getAReadContent() instanceof DataFlow::Content::CollectionContent
49+
}
4250
}
4351

4452
module HardcodedKeyFlow = TaintTracking::Global<HardcodedKeyConfig>;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added sqlite3 and SQLite.swift sinks and flow summaries for the `swift/hardcoded-key` query.

swift/ql/test/query-tests/Security/CWE-321/HardcodedEncryptionKey.expected

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
edges
2+
| SQLite.swift:54:25:54:33 | [...] | SQLite.swift:54:13:54:34 | call to Blob.init(bytes:) |
3+
| cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:80:10:80:28 | call to getConstantString() |
24
| cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:92:18:92:36 | call to getConstantString() |
5+
| cryptoswift.swift:80:2:80:34 | call to Array<Element>.init(_:) [Collection element] | cryptoswift.swift:91:13:91:30 | call to getConstantArray() [Collection element] |
6+
| cryptoswift.swift:80:10:80:28 | call to getConstantString() | cryptoswift.swift:80:10:80:30 | .utf8 |
7+
| cryptoswift.swift:80:10:80:30 | .utf8 | cryptoswift.swift:80:2:80:34 | call to Array<Element>.init(_:) [Collection element] |
38
| cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:117:22:117:22 | key |
49
| cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:118:22:118:22 | key |
510
| cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:128:26:128:26 | key |
@@ -10,6 +15,8 @@ edges
1015
| cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:151:26:151:26 | key |
1116
| cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:161:24:161:24 | key |
1217
| cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:163:24:163:24 | key |
18+
| cryptoswift.swift:91:13:91:30 | call to getConstantArray() [Collection element] | cryptoswift.swift:106:21:106:21 | key2 |
19+
| cryptoswift.swift:91:13:91:30 | call to getConstantArray() [Collection element] | cryptoswift.swift:107:21:107:21 | key2 |
1320
| cryptoswift.swift:92:18:92:36 | call to getConstantString() | cryptoswift.swift:108:21:108:21 | keyString |
1421
| cryptoswift.swift:92:18:92:36 | call to getConstantString() | cryptoswift.swift:109:21:109:21 | keyString |
1522
| cryptoswift.swift:92:18:92:36 | call to getConstantString() | cryptoswift.swift:119:22:119:22 | keyString |
@@ -57,10 +64,27 @@ edges
5764
| rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:81:102:81:102 | myConstKey |
5865
| rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:83:92:83:92 | myConstKey |
5966
| rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:60:19:60:38 | call to Data.init(_:) |
67+
| sqlite3_c_api.swift:33:19:33:38 | call to Data.init(_:) | sqlite3_c_api.swift:40:2:40:2 | myConstKey |
68+
| sqlite3_c_api.swift:33:24:33:24 | abcdef123456 | sqlite3_c_api.swift:33:19:33:38 | call to Data.init(_:) |
69+
| sqlite3_c_api.swift:40:2:40:2 | myConstKey | sqlite3_c_api.swift:40:31:40:31 | buffer [Collection element] |
70+
| sqlite3_c_api.swift:40:31:40:31 | buffer [Collection element] | sqlite3_c_api.swift:41:36:41:36 | buffer |
71+
| sqlite3_c_api.swift:40:31:40:31 | buffer [Collection element] | sqlite3_c_api.swift:42:38:42:38 | buffer |
6072
nodes
73+
| SQLite.swift:43:13:43:13 | hardcoded_key | semmle.label | hardcoded_key |
74+
| SQLite.swift:45:23:45:23 | hardcoded_key | semmle.label | hardcoded_key |
75+
| SQLite.swift:47:15:47:15 | hardcoded_key | semmle.label | hardcoded_key |
76+
| SQLite.swift:49:79:49:79 | hardcoded_key | semmle.label | hardcoded_key |
77+
| SQLite.swift:54:13:54:34 | call to Blob.init(bytes:) | semmle.label | call to Blob.init(bytes:) |
78+
| SQLite.swift:54:25:54:33 | [...] | semmle.label | [...] |
6179
| cryptoswift.swift:76:3:76:3 | this string is constant | semmle.label | this string is constant |
80+
| cryptoswift.swift:80:2:80:34 | call to Array<Element>.init(_:) [Collection element] | semmle.label | call to Array<Element>.init(_:) [Collection element] |
81+
| cryptoswift.swift:80:10:80:28 | call to getConstantString() | semmle.label | call to getConstantString() |
82+
| cryptoswift.swift:80:10:80:30 | .utf8 | semmle.label | .utf8 |
6283
| cryptoswift.swift:90:26:90:121 | [...] | semmle.label | [...] |
84+
| cryptoswift.swift:91:13:91:30 | call to getConstantArray() [Collection element] | semmle.label | call to getConstantArray() [Collection element] |
6385
| cryptoswift.swift:92:18:92:36 | call to getConstantString() | semmle.label | call to getConstantString() |
86+
| cryptoswift.swift:106:21:106:21 | key2 | semmle.label | key2 |
87+
| cryptoswift.swift:107:21:107:21 | key2 | semmle.label | key2 |
6488
| cryptoswift.swift:108:21:108:21 | keyString | semmle.label | keyString |
6589
| cryptoswift.swift:109:21:109:21 | keyString | semmle.label | keyString |
6690
| cryptoswift.swift:117:22:117:22 | key | semmle.label | key |
@@ -119,12 +143,25 @@ nodes
119143
| rncryptor.swift:80:94:80:94 | myConstKey | semmle.label | myConstKey |
120144
| rncryptor.swift:81:102:81:102 | myConstKey | semmle.label | myConstKey |
121145
| rncryptor.swift:83:92:83:92 | myConstKey | semmle.label | myConstKey |
146+
| sqlite3_c_api.swift:33:19:33:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) |
147+
| sqlite3_c_api.swift:33:24:33:24 | abcdef123456 | semmle.label | abcdef123456 |
148+
| sqlite3_c_api.swift:40:2:40:2 | myConstKey | semmle.label | myConstKey |
149+
| sqlite3_c_api.swift:40:31:40:31 | buffer [Collection element] | semmle.label | buffer [Collection element] |
150+
| sqlite3_c_api.swift:41:36:41:36 | buffer | semmle.label | buffer |
151+
| sqlite3_c_api.swift:42:38:42:38 | buffer | semmle.label | buffer |
122152
subpaths
123153
| misc.swift:53:25:53:25 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self | misc.swift:53:2:53:2 | [post] config |
124154
| misc.swift:53:25:53:25 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self [encryptionKey] | misc.swift:53:2:53:2 | [post] config [encryptionKey] |
125155
| misc.swift:57:41:57:41 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self | misc.swift:57:2:57:18 | [post] getter for .config |
126156
| misc.swift:57:41:57:41 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self [encryptionKey] | misc.swift:57:2:57:18 | [post] getter for .config [encryptionKey] |
127157
#select
158+
| SQLite.swift:43:13:43:13 | hardcoded_key | SQLite.swift:43:13:43:13 | hardcoded_key | SQLite.swift:43:13:43:13 | hardcoded_key | The key 'hardcoded_key' has been initialized with hard-coded values from $@. | SQLite.swift:43:13:43:13 | hardcoded_key | hardcoded_key |
159+
| SQLite.swift:45:23:45:23 | hardcoded_key | SQLite.swift:45:23:45:23 | hardcoded_key | SQLite.swift:45:23:45:23 | hardcoded_key | The key 'hardcoded_key' has been initialized with hard-coded values from $@. | SQLite.swift:45:23:45:23 | hardcoded_key | hardcoded_key |
160+
| SQLite.swift:47:15:47:15 | hardcoded_key | SQLite.swift:47:15:47:15 | hardcoded_key | SQLite.swift:47:15:47:15 | hardcoded_key | The key 'hardcoded_key' has been initialized with hard-coded values from $@. | SQLite.swift:47:15:47:15 | hardcoded_key | hardcoded_key |
161+
| SQLite.swift:49:79:49:79 | hardcoded_key | SQLite.swift:49:79:49:79 | hardcoded_key | SQLite.swift:49:79:49:79 | hardcoded_key | The key 'hardcoded_key' has been initialized with hard-coded values from $@. | SQLite.swift:49:79:49:79 | hardcoded_key | hardcoded_key |
162+
| SQLite.swift:54:13:54:34 | call to Blob.init(bytes:) | SQLite.swift:54:25:54:33 | [...] | SQLite.swift:54:13:54:34 | call to Blob.init(bytes:) | The key 'call to Blob.init(bytes:)' has been initialized with hard-coded values from $@. | SQLite.swift:54:25:54:33 | [...] | [...] |
163+
| cryptoswift.swift:106:21:106:21 | key2 | cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:106:21:106:21 | key2 | The key 'key2' has been initialized with hard-coded values from $@. | cryptoswift.swift:76:3:76:3 | this string is constant | this string is constant |
164+
| cryptoswift.swift:107:21:107:21 | key2 | cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:107:21:107:21 | key2 | The key 'key2' has been initialized with hard-coded values from $@. | cryptoswift.swift:76:3:76:3 | this string is constant | this string is constant |
128165
| cryptoswift.swift:108:21:108:21 | keyString | cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:108:21:108:21 | keyString | The key 'keyString' has been initialized with hard-coded values from $@. | cryptoswift.swift:76:3:76:3 | this string is constant | this string is constant |
129166
| cryptoswift.swift:109:21:109:21 | keyString | cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:109:21:109:21 | keyString | The key 'keyString' has been initialized with hard-coded values from $@. | cryptoswift.swift:76:3:76:3 | this string is constant | this string is constant |
130167
| cryptoswift.swift:117:22:117:22 | key | cryptoswift.swift:90:26:90:121 | [...] | cryptoswift.swift:117:22:117:22 | key | The key 'key' has been initialized with hard-coded values from $@. | cryptoswift.swift:90:26:90:121 | [...] | [...] |
@@ -167,3 +204,5 @@ subpaths
167204
| rncryptor.swift:80:94:80:94 | myConstKey | rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:80:94:80:94 | myConstKey | The key 'myConstKey' has been initialized with hard-coded values from $@. | rncryptor.swift:60:24:60:24 | abcdef123456 | abcdef123456 |
168205
| rncryptor.swift:81:102:81:102 | myConstKey | rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:81:102:81:102 | myConstKey | The key 'myConstKey' has been initialized with hard-coded values from $@. | rncryptor.swift:60:24:60:24 | abcdef123456 | abcdef123456 |
169206
| rncryptor.swift:83:92:83:92 | myConstKey | rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:83:92:83:92 | myConstKey | The key 'myConstKey' has been initialized with hard-coded values from $@. | rncryptor.swift:60:24:60:24 | abcdef123456 | abcdef123456 |
207+
| sqlite3_c_api.swift:41:36:41:36 | buffer | sqlite3_c_api.swift:33:24:33:24 | abcdef123456 | sqlite3_c_api.swift:41:36:41:36 | buffer | The key 'buffer' has been initialized with hard-coded values from $@. | sqlite3_c_api.swift:33:24:33:24 | abcdef123456 | abcdef123456 |
208+
| sqlite3_c_api.swift:42:38:42:38 | buffer | sqlite3_c_api.swift:33:24:33:24 | abcdef123456 | sqlite3_c_api.swift:42:38:42:38 | buffer | The key 'buffer' has been initialized with hard-coded values from $@. | sqlite3_c_api.swift:33:24:33:24 | abcdef123456 | abcdef123456 |
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
// --- stubs ---
3+
4+
enum URIQueryParameter {
5+
}
6+
7+
struct Blob {
8+
public init(bytes: [UInt8]) { }
9+
public init(bytes: UnsafeRawPointer, length: Int) { }
10+
}
11+
12+
class Connection {
13+
enum Location {
14+
case inMemory
15+
case uri(String, parameters: [URIQueryParameter] = [])
16+
}
17+
18+
init(_ location: Location = .inMemory, readonly: Bool = false) throws { }
19+
convenience init(_ filename: String, readonly: Bool = false) throws { try self.init() }
20+
}
21+
22+
extension Connection {
23+
func key(_ key: String, db: String = "main") throws { }
24+
func key(_ key: Blob, db: String = "main") throws { }
25+
func keyAndMigrate(_ key: String, db: String = "main") throws { }
26+
func keyAndMigrate(_ key: Blob, db: String = "main") throws { }
27+
28+
func rekey(_ key: String, db: String = "main") throws { }
29+
func rekey(_ key: Blob, db: String = "main") throws { }
30+
31+
func sqlcipher_export(_ location: Location, key: String) throws { }
32+
}
33+
34+
// --- tests ---
35+
36+
func test_sqlite_swift_api(dbPath: String, goodKey: String, goodArray: [UInt8]) throws {
37+
let db = try Connection(dbPath)
38+
let badArray: [UInt8] = [1, 2, 3]
39+
40+
// methods taking a string key
41+
42+
try db.key(goodKey)
43+
try db.key("hardcoded_key") // BAD
44+
try db.keyAndMigrate(goodKey)
45+
try db.keyAndMigrate("hardcoded_key") // BAD
46+
try db.rekey(goodKey)
47+
try db.rekey("hardcoded_key") // BAD
48+
try db.sqlcipher_export(Connection.Location.uri("encryptedDb.sqlite3"), key: goodKey)
49+
try db.sqlcipher_export(Connection.Location.uri("encryptedDb.sqlite3"), key: "hardcoded_key") // BAD
50+
51+
// Blob variant
52+
53+
try db.key(Blob(bytes: goodArray))
54+
try db.key(Blob(bytes: [1, 2, 3])) // BAD [NOT DETECTED]
55+
56+
try goodArray.withUnsafeBytes { bytes in
57+
if let ptr = bytes.baseAddress {
58+
try db.key(Blob(bytes: ptr, length: bytes.count))
59+
}
60+
}
61+
try badArray.withUnsafeBytes { bytes in
62+
if let ptr = bytes.baseAddress {
63+
try db.key(Blob(bytes: ptr, length: bytes.count)) // BAD [NOT DETECTED]
64+
}
65+
}
66+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
// --- stubs ---
3+
4+
struct Data {
5+
init<S>(_ elements: S) { count = 0 }
6+
7+
func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
8+
return 0 as! ResultType//body(0 as UnsafePointer<ContentType>())
9+
}
10+
11+
var count: Int
12+
}
13+
14+
var SQLITE_OK : Int32 = 0
15+
16+
func sqlite3_key_v2(
17+
_ db: OpaquePointer?,
18+
_ zDbName: UnsafePointer<CChar>?,
19+
_ pKey: UnsafeRawPointer?,
20+
_ nKey: Int32
21+
) -> Int32 { return SQLITE_OK }
22+
23+
func sqlite3_rekey_v2(
24+
_ db: OpaquePointer?,
25+
_ zDbName: UnsafePointer<CChar>?,
26+
_ pKey: UnsafeRawPointer?,
27+
_ nKey: Int32
28+
) -> Int32 { return SQLITE_OK }
29+
30+
// --- tests ---
31+
32+
func test_sqlite3_c_api(db: OpaquePointer?, myVarKey: Data) {
33+
let myConstKey = Data("abcdef123456")
34+
35+
// SQLite (C API) Encryption Extension
36+
myVarKey.withUnsafeBytes { buffer in
37+
_ = sqlite3_key_v2(db, "dbname", buffer, Int32(myVarKey.count))
38+
_ = sqlite3_rekey_v2(db, "dbname", buffer, Int32(myVarKey.count))
39+
}
40+
myConstKey.withUnsafeBytes { buffer in
41+
_ = sqlite3_key_v2(db, "dbname", buffer, Int32(myVarKey.count)) // BAD
42+
_ = sqlite3_rekey_v2(db, "dbname", buffer, Int32(myVarKey.count)) // BAD
43+
}
44+
}

0 commit comments

Comments
 (0)