Skip to content

Commit 2148e8b

Browse files
authored
Merge pull request github#10892 from atorralba/atorralba/swift/customurlschemes
Swift: Add a new Custom URL Scheme source
2 parents 5ff98cd + 30f5fb6 commit 2148e8b

File tree

8 files changed

+113
-9
lines changed

8 files changed

+113
-9
lines changed

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ private module Cached {
148148
// flow through `!`
149149
nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr()
150150
or
151+
// flow through `?`
152+
nodeFrom.asExpr() = nodeTo.asExpr().(BindOptionalExpr).getSubExpr()
153+
or
154+
nodeFrom.asExpr() = nodeTo.asExpr().(OptionalEvaluationExpr).getSubExpr()
155+
or
151156
// flow through a flow summary (extension of `SummaryModelCsv`)
152157
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
153158
}
Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
11
import swift
2+
private import codeql.swift.dataflow.DataFlow
23
private import codeql.swift.dataflow.ExternalFlow
4+
private import codeql.swift.dataflow.FlowSources
35

46
private class UrlRemoteFlowSource extends SourceModelCsv {
57
override predicate row(string row) {
68
row =
79
[
810
";UIApplicationDelegate;true;application(_:open:options:);;;Parameter[1];remote",
911
";UIApplicationDelegate;true;application(_:handleOpen:);;;Parameter[1];remote",
10-
";UIApplicationDelegate;true;application(_:open:sourceApplication:annotation:);;;Parameter[1];remote"
12+
";UIApplicationDelegate;true;application(_:open:sourceApplication:annotation:);;;Parameter[1];remote",
13+
// TODO: The actual source is the value of `UIApplication.LaunchOptionsKey.url` in the launchOptions dictionary.
14+
// Use dictionary value contents when available.
15+
// ";UIApplicationDelegate;true;application(_:didFinishLaunchingWithOptions:);;;Parameter[1].MapValue;remote",
16+
// ";UIApplicationDelegate;true;application(_:willFinishLaunchingWithOptions:);;;Parameter[1].MapValue;remote"
1117
]
1218
}
1319
}
20+
21+
/**
22+
* A read of `UIApplication.LaunchOptionsKey.url` on a dictionary received in
23+
* `UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:)` or
24+
* `UIApplicationDelegate.application(_:willFinishLaunchingWithOptions:)`.
25+
*/
26+
// This is a temporary workaround until the TODO above is addressed.
27+
private class UrlLaunchOptionsRemoteFlowSource extends RemoteFlowSource {
28+
UrlLaunchOptionsRemoteFlowSource() {
29+
exists(ApplicationWithLaunchOptionsFunc f, SubscriptExpr e |
30+
DataFlow::localExprFlow(f.getParam(1).getAnAccess(), e.getBase()) and
31+
e.getAnArgument().getExpr().(MemberRefExpr).getMember() instanceof LaunchOptionsUrlVarDecl and
32+
this.asExpr() = e
33+
)
34+
}
35+
36+
override string getSourceType() {
37+
result = "Remote URL in UIApplicationDelegate.application.launchOptions"
38+
}
39+
}
40+
41+
private class ApplicationWithLaunchOptionsFunc extends FuncDecl {
42+
ApplicationWithLaunchOptionsFunc() {
43+
this.getName() = "application(_:" + ["did", "will"] + "FinishLaunchingWithOptions:)" and
44+
this.getEnclosingDecl().(ClassOrStructDecl).getABaseTypeDecl*().(ProtocolDecl).getName() =
45+
"UIApplicationDelegate"
46+
}
47+
}
48+
49+
private class LaunchOptionsUrlVarDecl extends VarDecl {
50+
LaunchOptionsUrlVarDecl() {
51+
this.getEnclosingDecl().(StructDecl).getFullName() = "UIApplication.LaunchOptionsKey" and
52+
this.getName() = "url"
53+
}
54+
}

swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ edges
9898
| test.swift:219:13:219:15 | .a [x] : | test.swift:219:13:219:17 | .x |
9999
| test.swift:225:14:225:21 | call to source() : | test.swift:235:13:235:15 | .source_value |
100100
| test.swift:225:14:225:21 | call to source() : | test.swift:238:13:238:15 | .source_value |
101+
| test.swift:259:12:259:19 | call to source() : | test.swift:263:13:263:28 | call to optionalSource() : |
102+
| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:264:15:264:16 | ...! |
101103
nodes
102104
| file://:0:0:0:0 | .a [x] : | semmle.label | .a [x] : |
103105
| file://:0:0:0:0 | .x : | semmle.label | .x : |
@@ -208,6 +210,9 @@ nodes
208210
| test.swift:225:14:225:21 | call to source() : | semmle.label | call to source() : |
209211
| test.swift:235:13:235:15 | .source_value | semmle.label | .source_value |
210212
| test.swift:238:13:238:15 | .source_value | semmle.label | .source_value |
213+
| test.swift:259:12:259:19 | call to source() : | semmle.label | call to source() : |
214+
| test.swift:263:13:263:28 | call to optionalSource() : | semmle.label | call to optionalSource() : |
215+
| test.swift:264:15:264:16 | ...! | semmle.label | ...! |
211216
subpaths
212217
| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:31:75:32 | [post] &... : |
213218
| test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : |
@@ -263,3 +268,4 @@ subpaths
263268
| test.swift:219:13:219:17 | .x | test.swift:218:11:218:18 | call to source() : | test.swift:219:13:219:17 | .x | result |
264269
| test.swift:235:13:235:15 | .source_value | test.swift:225:14:225:21 | call to source() : | test.swift:235:13:235:15 | .source_value | result |
265270
| test.swift:238:13:238:15 | .source_value | test.swift:225:14:225:21 | call to source() : | test.swift:238:13:238:15 | .source_value | result |
271+
| test.swift:264:15:264:16 | ...! | test.swift:259:12:259:19 | call to source() : | test.swift:264:15:264:16 | ...! | result |

swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import swift
66
import codeql.swift.dataflow.DataFlow
7+
import codeql.swift.dataflow.ExternalFlow
78

89
class TestConfiguration extends DataFlow::Configuration {
910
TestConfiguration() { this = "TestConfiguration" }
@@ -14,10 +15,17 @@ class TestConfiguration extends DataFlow::Configuration {
1415

1516
override predicate isSink(DataFlow::Node sink) {
1617
exists(CallExpr sinkCall |
17-
sinkCall.getStaticTarget().getName() = "sink(arg:)" and
18+
sinkCall.getStaticTarget().getName() = ["sink(arg:)", "sink(opt:)"] and
1819
sinkCall.getAnArgument().getExpr() = sink.asExpr()
1920
)
2021
}
2122

2223
override int explorationLimit() { result = 100 }
2324
}
25+
26+
private class TestSummaries extends SummaryModelCsv {
27+
override predicate row(string row) {
28+
// model to allow data flow through `signum()` as though it were an identity function, for the benefit of testing flow through optional chaining (`x?.`).
29+
row = ";Int;true;signum();;;Argument[-1];ReturnValue;value"
30+
}
31+
}

swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,11 @@
184184
| test.swift:247:9:247:9 | [post] self | test.swift:246:5:248:5 | self[return] |
185185
| test.swift:247:9:247:9 | self | test.swift:246:5:248:5 | self[return] |
186186
| test.swift:252:23:252:23 | value | test.swift:252:23:252:23 | WriteDef |
187+
| test.swift:263:9:263:9 | WriteDef | test.swift:264:15:264:15 | x |
188+
| test.swift:263:13:263:28 | call to optionalSource() | test.swift:263:9:263:9 | WriteDef |
189+
| test.swift:264:15:264:15 | x | test.swift:264:15:264:16 | ...! |
190+
| test.swift:264:15:264:15 | x | test.swift:266:15:266:15 | x |
191+
| test.swift:266:15:266:15 | x | test.swift:266:15:266:16 | ...? |
192+
| test.swift:266:15:266:15 | x | test.swift:267:15:267:15 | x |
193+
| test.swift:266:15:266:25 | call to signum() | test.swift:266:15:266:25 | OptionalEvaluationExpr |
194+
| test.swift:267:15:267:15 | x | test.swift:268:16:268:16 | x |

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,21 @@ func test_computed_property() {
251251
func test_property_wrapper() {
252252
@DidSetSource var x = 42
253253
sink(arg: x) // $ MISSING: flow=243
254-
}
254+
}
255+
256+
func sink(opt: Int?) {}
257+
258+
func optionalSource() -> Int? {
259+
return source()
260+
}
261+
262+
func test_optionals() {
263+
let x = optionalSource()
264+
sink(arg: x!) // $ flow=259
265+
sink(arg: source().signum()) // $ MISSING: flow=259
266+
sink(opt: x?.signum()) // $ MISSING: flow=259
267+
sink(arg: x ?? 0) // $ MISSING: flow=259
268+
if let y = x {
269+
sink(arg: y) // $ MISSING: flow=259
270+
}
271+
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
| customurlschemes.swift:23:44:23:54 | url | external |
2-
| customurlschemes.swift:27:52:27:68 | url | external |
3-
| customurlschemes.swift:31:52:31:62 | url | external |
1+
| customurlschemes.swift:30:44:30:54 | url | external |
2+
| customurlschemes.swift:34:52:34:68 | url | external |
3+
| customurlschemes.swift:38:52:38:62 | url | external |
4+
| customurlschemes.swift:43:9:43:28 | ...[...] | Remote URL in UIApplicationDelegate.application.launchOptions |
5+
| customurlschemes.swift:48:9:48:28 | ...[...] | Remote URL in UIApplicationDelegate.application.launchOptions |
46
| string.swift:27:21:27:21 | call to init(contentsOf:) | external |
57
| string.swift:27:21:27:44 | call to init(contentsOf:) | external |
68
| url.swift:53:15:53:19 | .resourceBytes | external |

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ class UIApplication {
77

88
func hash(into hasher: inout Hasher) {}
99
}
10+
struct LaunchOptionsKey: Hashable {
11+
init(rawValue: String) {}
12+
public static let url: UIApplication.LaunchOptionsKey = UIApplication.LaunchOptionsKey(rawValue: "")
13+
func hash(into hasher: inout Hasher) {}
14+
}
1015
}
1116

1217
struct URL {}
@@ -15,21 +20,33 @@ protocol UIApplicationDelegate {
1520
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
1621
func application(_ application: UIApplication, handleOpen url: URL) -> Bool
1722
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool
23+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool
24+
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool
1825
}
1926

2027
// --- tests ---
2128

2229
class AppDelegate: UIApplicationDelegate {
2330
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool { // SOURCE
24-
return true;
31+
return true
2532
}
2633

2734
func application(_ application: UIApplication, handleOpen url: URL) -> Bool { // SOURCE
28-
return true;
35+
return true
2936
}
3037

3138
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { // SOURCE
32-
return true;
39+
return true
40+
}
41+
42+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
43+
launchOptions?[.url] // SOURCE
44+
return true
45+
}
46+
47+
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
48+
launchOptions?[.url] // SOURCE
49+
return true
3350
}
3451

3552
}

0 commit comments

Comments
 (0)