Skip to content

Commit 8f45629

Browse files
Merge pull request github#11185 from karimhamdanali/swift-string-taint-steps
Swift: add `String` taint steps
2 parents b8c11de + f6bc884 commit 8f45629

File tree

7 files changed

+119
-4
lines changed

7 files changed

+119
-4
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import swift
2+
private import codeql.swift.dataflow.DataFlow
23
private import codeql.swift.dataflow.ExternalFlow
4+
private import codeql.swift.dataflow.FlowSteps
35

46
private class StringSource extends SourceModelCsv {
57
override predicate row(string row) {
@@ -16,3 +18,15 @@ private class StringSource extends SourceModelCsv {
1618
]
1719
}
1820
}
21+
22+
/**
23+
* A content implying that, if a `String` is tainted, then all its fields are tainted.
24+
*/
25+
private class StringFieldsInheritTaint extends TaintInheritingContent,
26+
DataFlow::Content::FieldContent {
27+
StringFieldsInheritTaint() {
28+
this.getField().getEnclosingDecl().(ClassOrStructDecl).getFullName() = "String" or
29+
this.getField().getEnclosingDecl().(ExtensionDecl).getExtendedTypeDecl().getFullName() =
30+
"String"
31+
}
32+
}

swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@
120120
| data.swift:80:6:80:6 | SSA def(dataClean) | data.swift:84:12:84:12 | dataClean |
121121
| data.swift:80:18:80:18 | Data.Type | data.swift:80:18:80:18 | call to init(_:) |
122122
| data.swift:80:18:80:36 | call to init(_:) | data.swift:80:6:80:6 | SSA def(dataClean) |
123+
| data.swift:80:23:80:23 | 123456 | data.swift:80:23:80:32 | .utf8 |
123124
| data.swift:80:23:80:32 | .utf8 | data.swift:80:18:80:36 | call to init(_:) |
124125
| data.swift:81:6:81:6 | SSA def(dataTainted) | data.swift:82:26:82:26 | dataTainted |
125126
| data.swift:81:20:81:20 | Data.Type | data.swift:81:20:81:20 | call to init(_:) |
126127
| data.swift:81:20:81:51 | call to init(_:) | data.swift:81:6:81:6 | SSA def(dataTainted) |
127128
| data.swift:81:25:81:47 | .utf8 | data.swift:81:20:81:51 | call to init(_:) |
129+
| data.swift:81:26:81:33 | call to source() | data.swift:81:25:81:47 | .utf8 |
128130
| data.swift:82:6:82:6 | SSA def(dataTainted2) | data.swift:86:12:86:12 | dataTainted2 |
129131
| data.swift:82:21:82:21 | Data.Type | data.swift:82:21:82:21 | call to init(_:) |
130132
| data.swift:82:21:82:37 | call to init(_:) | data.swift:82:6:82:6 | SSA def(dataTainted2) |
@@ -898,9 +900,13 @@
898900
| string.swift:81:31:81:31 | clean | string.swift:84:13:84:13 | clean |
899901
| string.swift:82:31:82:31 | tainted | string.swift:85:13:85:13 | tainted |
900902
| string.swift:84:13:84:13 | [post] clean | string.swift:87:13:87:13 | clean |
903+
| string.swift:84:13:84:13 | clean | string.swift:84:13:84:19 | .description |
901904
| string.swift:84:13:84:13 | clean | string.swift:87:13:87:13 | clean |
902905
| string.swift:85:13:85:13 | [post] tainted | string.swift:88:13:88:13 | tainted |
906+
| string.swift:85:13:85:13 | tainted | string.swift:85:13:85:21 | .description |
903907
| string.swift:85:13:85:13 | tainted | string.swift:88:13:88:13 | tainted |
908+
| string.swift:87:13:87:13 | clean | string.swift:87:13:87:19 | .debugDescription |
909+
| string.swift:88:13:88:13 | tainted | string.swift:88:13:88:21 | .debugDescription |
904910
| string.swift:91:7:91:7 | SSA def(self) | string.swift:91:7:91:7 | self[return] |
905911
| string.swift:91:7:91:7 | self | string.swift:91:7:91:7 | SSA def(self) |
906912
| string.swift:93:5:93:5 | SSA def(self) | string.swift:93:5:93:29 | self[return] |
@@ -923,6 +929,17 @@
923929
| string.swift:109:23:109:77 | call to init(data:encoding:) | string.swift:109:7:109:7 | SSA def(stringTainted) |
924930
| string.swift:111:12:111:12 | stringClean | string.swift:111:12:111:23 | ...! |
925931
| string.swift:112:12:112:12 | stringTainted | string.swift:112:12:112:25 | ...! |
932+
| string.swift:120:7:120:7 | SSA def(clean) | string.swift:125:13:125:13 | clean |
933+
| string.swift:120:15:120:15 | | string.swift:120:7:120:7 | SSA def(clean) |
934+
| string.swift:121:7:121:7 | SSA def(tainted) | string.swift:126:13:126:13 | tainted |
935+
| string.swift:121:17:121:25 | call to source2() | string.swift:121:17:121:27 | .utf8 |
936+
| string.swift:121:17:121:27 | .utf8 | string.swift:121:7:121:7 | SSA def(tainted) |
937+
| string.swift:122:7:122:7 | SSA def(taintedCString) | string.swift:127:13:127:13 | taintedCString |
938+
| string.swift:122:24:122:32 | call to source2() | string.swift:122:24:122:34 | .utf8CString |
939+
| string.swift:122:24:122:34 | .utf8CString | string.swift:122:7:122:7 | SSA def(taintedCString) |
940+
| string.swift:123:7:123:7 | SSA def(taintedUnicodeScalars) | string.swift:128:13:128:13 | taintedUnicodeScalars |
941+
| string.swift:123:31:123:39 | call to source2() | string.swift:123:31:123:41 | .unicodeScalars |
942+
| string.swift:123:31:123:41 | .unicodeScalars | string.swift:123:7:123:7 | SSA def(taintedUnicodeScalars) |
926943
| subscript.swift:1:7:1:7 | SSA def(self) | subscript.swift:1:7:1:7 | self[return] |
927944
| subscript.swift:1:7:1:7 | SSA def(self) | subscript.swift:1:7:1:7 | self[return] |
928945
| subscript.swift:1:7:1:7 | self | subscript.swift:1:7:1:7 | SSA def(self) |

swift/ql/test/library-tests/dataflow/taint/Taint.expected

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
edges
2+
| data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : |
23
| data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : |
34
| data.swift:26:2:26:61 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : |
45
| data.swift:27:2:27:62 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : |
@@ -34,6 +35,14 @@ edges
3435
| data.swift:62:2:62:58 | [summary param] this in shuffled(using:) : | file://:0:0:0:0 | [summary] to write: return (return) in shuffled(using:) : |
3536
| data.swift:63:2:63:123 | [summary param] this in trimmingPrefix(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in trimmingPrefix(_:) : |
3637
| data.swift:64:2:64:72 | [summary param] this in trimmingPrefix(while:) : | file://:0:0:0:0 | [summary] to write: return (return) in trimmingPrefix(while:) : |
38+
| data.swift:81:20:81:51 | call to init(_:) : | data.swift:82:26:82:26 | dataTainted : |
39+
| data.swift:81:20:81:51 | call to init(_:) : | data.swift:85:12:85:12 | dataTainted |
40+
| data.swift:81:25:81:47 | .utf8 : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : |
41+
| data.swift:81:25:81:47 | .utf8 : | data.swift:81:20:81:51 | call to init(_:) : |
42+
| data.swift:81:26:81:33 | call to source() : | data.swift:81:25:81:47 | .utf8 : |
43+
| data.swift:82:21:82:37 | call to init(_:) : | data.swift:86:12:86:12 | dataTainted2 |
44+
| data.swift:82:26:82:26 | dataTainted : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : |
45+
| data.swift:82:26:82:26 | dataTainted : | data.swift:82:21:82:37 | call to init(_:) : |
3746
| data.swift:89:21:89:71 | call to init(base64Encoded:options:) : | data.swift:90:12:90:12 | dataTainted3 |
3847
| data.swift:89:41:89:48 | call to source() : | data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : |
3948
| data.swift:89:41:89:48 | call to source() : | data.swift:89:21:89:71 | call to init(base64Encoded:options:) : |
@@ -301,6 +310,11 @@ edges
301310
| string.swift:28:17:28:25 | call to source2() : | string.swift:35:13:35:23 | ... .+(_:_:) ... |
302311
| string.swift:28:17:28:25 | call to source2() : | string.swift:36:13:36:23 | ... .+(_:_:) ... |
303312
| string.swift:28:17:28:25 | call to source2() : | string.swift:39:13:39:29 | ... .+(_:_:) ... |
313+
| string.swift:74:17:74:25 | call to source2() : | string.swift:85:13:85:21 | .description |
314+
| string.swift:74:17:74:25 | call to source2() : | string.swift:88:13:88:21 | .debugDescription |
315+
| string.swift:121:17:121:25 | call to source2() : | string.swift:126:13:126:13 | tainted |
316+
| string.swift:122:24:122:32 | call to source2() : | string.swift:127:13:127:13 | taintedCString |
317+
| string.swift:123:31:123:39 | call to source2() : | string.swift:128:13:128:13 | taintedUnicodeScalars |
304318
| subscript.swift:13:15:13:22 | call to source() : | subscript.swift:13:15:13:25 | ...[...] |
305319
| subscript.swift:14:15:14:23 | call to source2() : | subscript.swift:14:15:14:26 | ...[...] |
306320
| try.swift:9:17:9:24 | call to source() : | try.swift:9:13:9:24 | try ... |
@@ -541,6 +555,7 @@ edges
541555
| webview.swift:137:34:137:41 | call to source() : | webview.swift:66:5:66:126 | [summary param] 0 in init(source:injectionTime:forMainFrameOnly:in:) : |
542556
| webview.swift:137:34:137:41 | call to source() : | webview.swift:137:13:137:113 | call to init(source:injectionTime:forMainFrameOnly:in:) : |
543557
nodes
558+
| data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | semmle.label | [summary param] 0 in init(_:) : |
544559
| data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : | semmle.label | [summary param] 0 in init(base64Encoded:options:) : |
545560
| data.swift:26:2:26:61 | [summary param] 0 in init(buffer:) : | semmle.label | [summary param] 0 in init(buffer:) : |
546561
| data.swift:27:2:27:62 | [summary param] 0 in init(buffer:) : | semmle.label | [summary param] 0 in init(buffer:) : |
@@ -576,6 +591,13 @@ nodes
576591
| data.swift:62:2:62:58 | [summary param] this in shuffled(using:) : | semmle.label | [summary param] this in shuffled(using:) : |
577592
| data.swift:63:2:63:123 | [summary param] this in trimmingPrefix(_:) : | semmle.label | [summary param] this in trimmingPrefix(_:) : |
578593
| data.swift:64:2:64:72 | [summary param] this in trimmingPrefix(while:) : | semmle.label | [summary param] this in trimmingPrefix(while:) : |
594+
| data.swift:81:20:81:51 | call to init(_:) : | semmle.label | call to init(_:) : |
595+
| data.swift:81:25:81:47 | .utf8 : | semmle.label | .utf8 : |
596+
| data.swift:81:26:81:33 | call to source() : | semmle.label | call to source() : |
597+
| data.swift:82:21:82:37 | call to init(_:) : | semmle.label | call to init(_:) : |
598+
| data.swift:82:26:82:26 | dataTainted : | semmle.label | dataTainted : |
599+
| data.swift:85:12:85:12 | dataTainted | semmle.label | dataTainted |
600+
| data.swift:86:12:86:12 | dataTainted2 | semmle.label | dataTainted2 |
579601
| data.swift:89:21:89:71 | call to init(base64Encoded:options:) : | semmle.label | call to init(base64Encoded:options:) : |
580602
| data.swift:89:41:89:48 | call to source() : | semmle.label | call to source() : |
581603
| data.swift:90:12:90:12 | dataTainted3 | semmle.label | dataTainted3 |
@@ -734,6 +756,7 @@ nodes
734756
| file://:0:0:0:0 | [summary] to write: return (return) in flatMap(_:) : | semmle.label | [summary] to write: return (return) in flatMap(_:) : |
735757
| file://:0:0:0:0 | [summary] to write: return (return) in flatMap(_:) : | semmle.label | [summary] to write: return (return) in flatMap(_:) : |
736758
| file://:0:0:0:0 | [summary] to write: return (return) in forProperty(_:) : | semmle.label | [summary] to write: return (return) in forProperty(_:) : |
759+
| file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | semmle.label | [summary] to write: return (return) in init(_:) : |
737760
| file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | semmle.label | [summary] to write: return (return) in init(base64Encoded:options:) : |
738761
| file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | semmle.label | [summary] to write: return (return) in init(base64Encoded:options:) : |
739762
| file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | semmle.label | [summary] to write: return (return) in init(base64Encoded:options:) : |
@@ -940,6 +963,15 @@ nodes
940963
| string.swift:35:13:35:23 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
941964
| string.swift:36:13:36:23 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
942965
| string.swift:39:13:39:29 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
966+
| string.swift:74:17:74:25 | call to source2() : | semmle.label | call to source2() : |
967+
| string.swift:85:13:85:21 | .description | semmle.label | .description |
968+
| string.swift:88:13:88:21 | .debugDescription | semmle.label | .debugDescription |
969+
| string.swift:121:17:121:25 | call to source2() : | semmle.label | call to source2() : |
970+
| string.swift:122:24:122:32 | call to source2() : | semmle.label | call to source2() : |
971+
| string.swift:123:31:123:39 | call to source2() : | semmle.label | call to source2() : |
972+
| string.swift:126:13:126:13 | tainted | semmle.label | tainted |
973+
| string.swift:127:13:127:13 | taintedCString | semmle.label | taintedCString |
974+
| string.swift:128:13:128:13 | taintedUnicodeScalars | semmle.label | taintedUnicodeScalars |
943975
| subscript.swift:13:15:13:22 | call to source() : | semmle.label | call to source() : |
944976
| subscript.swift:13:15:13:25 | ...[...] | semmle.label | ...[...] |
945977
| subscript.swift:14:15:14:23 | call to source2() : | semmle.label | call to source2() : |
@@ -1139,6 +1171,8 @@ nodes
11391171
| webview.swift:138:10:138:10 | c | semmle.label | c |
11401172
| webview.swift:139:10:139:12 | .source | semmle.label | .source |
11411173
subpaths
1174+
| data.swift:81:25:81:47 | .utf8 : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | data.swift:81:20:81:51 | call to init(_:) : |
1175+
| data.swift:82:26:82:26 | dataTainted : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | data.swift:82:21:82:37 | call to init(_:) : |
11421176
| data.swift:89:41:89:48 | call to source() : | data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | data.swift:89:21:89:71 | call to init(base64Encoded:options:) : |
11431177
| data.swift:93:34:93:41 | call to source() : | data.swift:26:2:26:61 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : | data.swift:93:21:93:73 | call to init(buffer:) : |
11441178
| data.swift:95:34:95:41 | call to source() : | data.swift:27:2:27:62 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : | data.swift:95:21:95:74 | call to init(buffer:) : |
@@ -1260,6 +1294,8 @@ subpaths
12601294
| webview.swift:132:34:132:41 | call to source() : | webview.swift:65:5:65:93 | [summary param] 0 in init(source:injectionTime:forMainFrameOnly:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(source:injectionTime:forMainFrameOnly:) : | webview.swift:132:13:132:102 | call to init(source:injectionTime:forMainFrameOnly:) : |
12611295
| webview.swift:137:34:137:41 | call to source() : | webview.swift:66:5:66:126 | [summary param] 0 in init(source:injectionTime:forMainFrameOnly:in:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(source:injectionTime:forMainFrameOnly:in:) : | webview.swift:137:13:137:113 | call to init(source:injectionTime:forMainFrameOnly:in:) : |
12621296
#select
1297+
| data.swift:85:12:85:12 | dataTainted | data.swift:81:26:81:33 | call to source() : | data.swift:85:12:85:12 | dataTainted | result |
1298+
| data.swift:86:12:86:12 | dataTainted2 | data.swift:81:26:81:33 | call to source() : | data.swift:86:12:86:12 | dataTainted2 | result |
12631299
| data.swift:90:12:90:12 | dataTainted3 | data.swift:89:41:89:48 | call to source() : | data.swift:90:12:90:12 | dataTainted3 | result |
12641300
| data.swift:94:12:94:12 | dataTainted4 | data.swift:93:34:93:41 | call to source() : | data.swift:94:12:94:12 | dataTainted4 | result |
12651301
| data.swift:96:12:96:12 | dataTainted5 | data.swift:95:34:95:41 | call to source() : | data.swift:96:12:96:12 | dataTainted5 | result |
@@ -1340,6 +1376,11 @@ subpaths
13401376
| string.swift:35:13:35:23 | ... .+(_:_:) ... | string.swift:28:17:28:25 | call to source2() : | string.swift:35:13:35:23 | ... .+(_:_:) ... | result |
13411377
| string.swift:36:13:36:23 | ... .+(_:_:) ... | string.swift:28:17:28:25 | call to source2() : | string.swift:36:13:36:23 | ... .+(_:_:) ... | result |
13421378
| string.swift:39:13:39:29 | ... .+(_:_:) ... | string.swift:28:17:28:25 | call to source2() : | string.swift:39:13:39:29 | ... .+(_:_:) ... | result |
1379+
| string.swift:85:13:85:21 | .description | string.swift:74:17:74:25 | call to source2() : | string.swift:85:13:85:21 | .description | result |
1380+
| string.swift:88:13:88:21 | .debugDescription | string.swift:74:17:74:25 | call to source2() : | string.swift:88:13:88:21 | .debugDescription | result |
1381+
| string.swift:126:13:126:13 | tainted | string.swift:121:17:121:25 | call to source2() : | string.swift:126:13:126:13 | tainted | result |
1382+
| string.swift:127:13:127:13 | taintedCString | string.swift:122:24:122:32 | call to source2() : | string.swift:127:13:127:13 | taintedCString | result |
1383+
| string.swift:128:13:128:13 | taintedUnicodeScalars | string.swift:123:31:123:39 | call to source2() : | string.swift:128:13:128:13 | taintedUnicodeScalars | result |
13431384
| subscript.swift:13:15:13:25 | ...[...] | subscript.swift:13:15:13:22 | call to source() : | subscript.swift:13:15:13:25 | ...[...] | result |
13441385
| subscript.swift:14:15:14:26 | ...[...] | subscript.swift:14:15:14:23 | call to source2() : | subscript.swift:14:15:14:26 | ...[...] | result |
13451386
| try.swift:9:13:9:24 | try ... | try.swift:9:17:9:24 | call to source() : | try.swift:9:13:9:24 | try ... | result |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| data.swift:85:12:85:12 | dataTainted | Fixed missing result:tainted=81 |
2+
| data.swift:86:12:86:12 | dataTainted2 | Fixed missing result:tainted=81 |

swift/ql/test/library-tests/dataflow/taint/string.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ func taintThroughStringOperations() {
8282
sink(arg: String(repeating: tainted, count: 2)) // $ MISSING: tainted=74
8383

8484
sink(arg: clean.description)
85-
sink(arg: tainted.description) // $ MISSING: tainted=74
85+
sink(arg: tainted.description) // $ tainted=74
8686

8787
sink(arg: clean.debugDescription)
88-
sink(arg: tainted.debugDescription) // $ MISSING: tainted=74
88+
sink(arg: tainted.debugDescription) // $ tainted=74
8989
}
9090

9191
class Data
@@ -111,3 +111,19 @@ func taintThroughData() {
111111
sink(arg: stringClean!)
112112
sink(arg: stringTainted!) // $ MISSING: tainted=100
113113
}
114+
115+
func sink(arg: String.UTF8View) {}
116+
func sink(arg: ContiguousArray<CChar>) {}
117+
func sink(arg: String.UnicodeScalarView) {}
118+
119+
func taintThroughStringFields() {
120+
let clean = ""
121+
let tainted = source2().utf8
122+
let taintedCString = source2().utf8CString
123+
let taintedUnicodeScalars = source2().unicodeScalars
124+
125+
sink(arg: clean)
126+
sink(arg: tainted) // $ tainted=121
127+
sink(arg: taintedCString) // $ tainted=122
128+
sink(arg: taintedUnicodeScalars) // $ tainted=123
129+
}

0 commit comments

Comments
 (0)