Skip to content

Commit 37cdef7

Browse files
committed
Swift: add basic Alamofire taint source model.
1 parent afb5dc7 commit 37cdef7

File tree

4 files changed

+75
-16
lines changed

4 files changed

+75
-16
lines changed

swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private module Frameworks {
8686
private import codeql.swift.frameworks.StandardLibrary.Url
8787
private import codeql.swift.frameworks.StandardLibrary.UrlSession
8888
private import codeql.swift.frameworks.StandardLibrary.WebView
89+
private import codeql.swift.frameworks.Alamofire.Alamofire
8990
}
9091

9192
/**
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Models for the Alamofire networking library.
3+
*/
4+
5+
import swift
6+
private import codeql.swift.dataflow.DataFlow
7+
private import codeql.swift.dataflow.ExternalFlow
8+
private import codeql.swift.dataflow.FlowSources
9+
10+
/**
11+
* An Alamofire response handler type.
12+
*/
13+
private class AlamofireResponseType extends NominalTypeDecl {
14+
AlamofireResponseType() {
15+
this.getFullName() = ["DataResponse", "DownloadResponse"] or
16+
this.getABaseTypeDecl() instanceof AlamofireResponseType
17+
}
18+
19+
/**
20+
* A response handler field that contains remote data.
21+
*/
22+
FieldDecl getADataField() {
23+
result = this.getAMember() and
24+
result.getName() = ["data", "value", "result"]
25+
}
26+
}
27+
28+
/**
29+
* A remote flow source that is an access to a remote data from an Alamofire response handler.
30+
*/
31+
private class AlamofireResponseSource extends RemoteFlowSource {
32+
AlamofireResponseSource() {
33+
exists(AlamofireResponseType responseType |
34+
this.asExpr().(MemberRefExpr).getMember() = responseType.getADataField()
35+
)
36+
}
37+
38+
override string getSourceType() { result = "Data from an Alamofire response" }
39+
}

swift/ql/test/library-tests/dataflow/flowsources/FlowSources.expected

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1+
| alamofire.swift:91:27:91:27 | .result | Data from an Alamofire response |
2+
| alamofire.swift:99:27:99:27 | .result | Data from an Alamofire response |
3+
| alamofire.swift:342:23:342:32 | .data | Data from an Alamofire response |
4+
| alamofire.swift:349:22:349:31 | .value | Data from an Alamofire response |
5+
| alamofire.swift:356:23:356:32 | .value | Data from an Alamofire response |
6+
| alamofire.swift:363:22:363:31 | .value | Data from an Alamofire response |
7+
| alamofire.swift:370:23:370:32 | .value | Data from an Alamofire response |
8+
| alamofire.swift:377:28:377:37 | .value | Data from an Alamofire response |
19
| alamofire.swift:387:28:387:28 | call to init(contentsOfFile:) | external |
210
| alamofire.swift:387:28:387:55 | call to init(contentsOfFile:) | external |
11+
| alamofire.swift:394:22:394:31 | .value | Data from an Alamofire response |
12+
| alamofire.swift:401:22:401:31 | .value | Data from an Alamofire response |
313
| alamofire.swift:402:28:402:28 | call to init(contentsOf:) | external |
414
| alamofire.swift:402:28:402:50 | call to init(contentsOf:) | external |
15+
| alamofire.swift:409:23:409:32 | .value | Data from an Alamofire response |
16+
| alamofire.swift:416:22:416:31 | .value | Data from an Alamofire response |
17+
| alamofire.swift:423:23:423:32 | .value | Data from an Alamofire response |
18+
| alamofire.swift:429:28:429:37 | .value | Data from an Alamofire response |
519
| alamofire.swift:446:20:446:20 | call to init(contentsOfFile:) | external |
620
| alamofire.swift:446:20:446:49 | call to init(contentsOfFile:) | external |
21+
| alamofire.swift:453:23:453:32 | .data | Data from an Alamofire response |
22+
| alamofire.swift:459:23:459:32 | .data | Data from an Alamofire response |
723
| customurlschemes.swift:30:44:30:54 | url | external |
824
| customurlschemes.swift:34:52:34:68 | url | external |
925
| customurlschemes.swift:38:52:38:62 | url | external |
1026
| customurlschemes.swift:43:9:43:28 | ...[...] | Remote URL in UIApplicationDelegate.application.launchOptions |
1127
| customurlschemes.swift:48:9:48:28 | ...[...] | Remote URL in UIApplicationDelegate.application.launchOptions |
1228
| data.swift:18:20:18:20 | call to init(contentsOf:options:) | external |
1329
| data.swift:18:20:18:54 | call to init(contentsOf:options:) | external |
30+
| file://:0:0:0:0 | .data | Data from an Alamofire response |
31+
| file://:0:0:0:0 | .result | Data from an Alamofire response |
32+
| file://:0:0:0:0 | .result | Data from an Alamofire response |
1433
| nsdata.swift:18:17:18:17 | call to init(contentsOf:) | external |
1534
| nsdata.swift:18:17:18:40 | call to init(contentsOf:) | external |
1635
| nsdata.swift:19:17:19:17 | call to init(contentsOf:options:) | external |

swift/ql/test/library-tests/dataflow/flowsources/alamofire.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ struct DataResponse<Success, Failure: Error> {
8888

8989
let result: Result<Success, Failure>
9090

91-
var value: Success? { result.success }
91+
var value: Success? { result.success } // SOURCE
9292
}
9393

9494
struct DownloadResponse<Success, Failure: Error> {
9595
let fileURL: URL?
9696

9797
let result: Result<Success, Failure>
9898

99-
var value: Success? { result.success }
99+
var value: Success? { result.success } // SOURCE
100100
}
101101

102102
typealias AFDataResponse<Success> = DataResponse<Success, AFError>
@@ -339,42 +339,42 @@ func testAlamofire() {
339339

340340
AF.request("http://example.com/").response {
341341
response in
342-
if let data = response.data { // SOURCE [NOT DETECTED]
342+
if let data = response.data { // SOURCE
343343
// ...
344344
}
345345
}
346346

347347
AF.request("http://example.com/").response(responseSerializer: MySerializer()) {
348348
response in
349-
if let obj = response.value { // SOURCE [NOT DETECTED]
349+
if let obj = response.value { // SOURCE
350350
// ...
351351
}
352352
}
353353

354354
AF.request("http://example.com/").responseData {
355355
response in
356-
if let data = response.value { // SOURCE [NOT DETECTED]
356+
if let data = response.value { // SOURCE
357357
// ...
358358
}
359359
}
360360

361361
AF.request("http://example.com/").responseString {
362362
response in
363-
if let str = response.value { // SOURCE [NOT DETECTED]
363+
if let str = response.value { // SOURCE
364364
// ...
365365
}
366366
}
367367

368368
AF.request("http://example.com/").responseJSON {
369369
response in
370-
if let json = response.value { // SOURCE [NOT DETECTED]
370+
if let json = response.value { // SOURCE
371371
// ...
372372
}
373373
}
374374

375375
AF.request("http://example.com/").responseDecodable(of: MyDecodable.self) {
376376
response in
377-
if let decodable = response.value { // SOURCE [NOT DETECTED]
377+
if let decodable = response.value { // SOURCE
378378
// ...
379379
}
380380
}
@@ -391,42 +391,42 @@ func testAlamofire() {
391391

392392
AF.download("http://example.com/").response(responseSerializer: MySerializer()) {
393393
response in
394-
if let obj = response.value { // SOURCE [NOT DETECTED]
394+
if let obj = response.value { // SOURCE
395395
// ...
396396
}
397397
}
398398

399399
AF.download("http://example.com/").responseURL {
400400
response in
401-
if let url = response.value {
401+
if let url = response.value { // just the URL [FALSE POSITIVE]
402402
let str = try? String(contentsOf: url) // SOURCE
403403
// ...
404404
}
405405
}
406406

407407
AF.download("http://example.com/").responseData {
408408
response in
409-
if let data = response.value { // SOURCE [NOT DETECTED]
409+
if let data = response.value { // SOURCE
410410
// ...
411411
}
412412
}
413413

414414
AF.download("http://example.com/").responseString {
415415
response in
416-
if let str = response.value { // SOURCE [NOT DETECTED]
416+
if let str = response.value { // SOURCE
417417
// ...
418418
}
419419
}
420420

421421
AF.download("http://example.com/").responseJSON {
422422
response in
423-
if let json = response.value { // SOURCE [NOT DETECTED]
423+
if let json = response.value { // SOURCE
424424
}
425425
}
426426

427427
AF.download("http://example.com/").responseDecodable(of: MyDecodable.self) {
428428
response in
429-
if let decodable = response.value { // SOURCE [NOT DETECTED]
429+
if let decodable = response.value { // SOURCE
430430
// ...
431431
}
432432
}
@@ -450,13 +450,13 @@ func testAlamofire() {
450450

451451
AF.request("http://example.com/").response {
452452
response in
453-
if let data = response.data { // SOURCE [NOT DETECTED]
453+
if let data = response.data { // SOURCE
454454
// ...
455455
}
456456
}
457457
.response {
458458
response in
459-
if let data = response.data { // SOURCE [NOT DETECTED]
459+
if let data = response.data { // SOURCE
460460
// ...
461461
}
462462
}

0 commit comments

Comments
 (0)