Skip to content

Commit 2e93c1d

Browse files
authored
Merge pull request #14879 from geoffw0/contentsof
Swift: "contentsOf" sources
2 parents e89d8e2 + 06ae374 commit 2e93c1d

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed
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 imprecise flow sources matching initializers such as `init(contentsOfFile:)`.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ private import SQL.SQL
88
private import StandardLibrary.StandardLibrary
99
private import UIKit.UIKit
1010
private import Xml.Xml
11+
private import Heuristic
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Provides heuristic models that match taint sources and flow through unknown
3+
* classes and libraries.
4+
*/
5+
6+
import swift
7+
private import codeql.swift.dataflow.DataFlow
8+
private import codeql.swift.dataflow.FlowSources
9+
10+
/**
11+
* An initializer call `ce` that has a "contentsOf" argument, along with a
12+
* guess `isRemote` as to whether it is the contents of a remote source. For
13+
* example:
14+
* ```
15+
* let myObject = MyClass(contentsOf: url) // isRemote = true
16+
* let myObject = MyClass(contentsOfFile: "foo.txt") // isRemote = false
17+
* ```
18+
*/
19+
private predicate contentsOfInitializer(InitializerCallExpr ce, boolean isRemote) {
20+
exists(Argument arg |
21+
ce.getAnArgument() = arg and
22+
arg.getLabel() = ["contentsOf", "contentsOfFile", "contentsOfPath", "contentsOfDirectory"] and
23+
if arg.getExpr().getType().getUnderlyingType().getName() = ["URL", "NSURL"]
24+
then isRemote = true
25+
else isRemote = false
26+
)
27+
}
28+
29+
/**
30+
* An imprecise flow source for an initializer call with a "contentsOf"
31+
* argument that appears to be remote. For example:
32+
* ```
33+
* let myObject = MyClass(contentsOf: url)
34+
* ```
35+
*/
36+
private class InitializerContentsOfRemoteSource extends RemoteFlowSource {
37+
InitializerContentsOfRemoteSource() { contentsOfInitializer(this.asExpr(), true) }
38+
39+
override string getSourceType() { result = "contentsOf initializer" }
40+
}
41+
42+
/**
43+
* An imprecise flow source for an initializer call with a "contentsOf"
44+
* argument that appears to be local. For example:
45+
* ```
46+
* let myObject = MyClass(contentsOfFile: "foo.txt")
47+
* ```
48+
*/
49+
private class InitializerContentsOfLocalSource extends LocalFlowSource {
50+
InitializerContentsOfLocalSource() { contentsOfInitializer(this.asExpr(), false) }
51+
52+
override string getSourceType() { result = "contentsOf initializer" }
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
// --- stubs ---
3+
4+
struct URL {
5+
init?(string: String) {}
6+
}
7+
8+
class MyClass {
9+
init(contentsOfFile: String) { }
10+
init(contentsOfFile: String?, options: [Int]) { }
11+
init(contentsOf: URL, fileTypeHint: Int) { }
12+
13+
init(contentsOfPath: String) { }
14+
init(contentsOfDirectory: String) { }
15+
16+
func append(contentsOf: String) { }
17+
func appending(contentsOf: String) -> MyClass { return self }
18+
}
19+
20+
// --- tests ---
21+
22+
func testCustom() {
23+
let url = URL(string: "http://example.com/")!
24+
25+
let x = MyClass(contentsOfFile: "foo.txt") // $ source=local
26+
_ = MyClass(contentsOfFile: "foo.txt", options: []) // $ source=local
27+
_ = MyClass(contentsOf: url, fileTypeHint: 1) // $ source=remote
28+
29+
_ = MyClass(contentsOfPath: "/foo/bar") // $ source=local
30+
_ = MyClass(contentsOfDirectory: "/foo/bar") // $ source=local
31+
32+
x.append(contentsOf: "abcdef") // (not a flow source)
33+
_ = x.appending(contentsOf: "abcdef") // (not a flow source)
34+
}

0 commit comments

Comments
 (0)