mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
Merge pull request #10892 from atorralba/atorralba/swift/customurlschemes
Swift: Add a new Custom URL Scheme source
This commit is contained in:
@@ -148,6 +148,11 @@ private module Cached {
|
||||
// flow through `!`
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr()
|
||||
or
|
||||
// flow through `?`
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(BindOptionalExpr).getSubExpr()
|
||||
or
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(OptionalEvaluationExpr).getSubExpr()
|
||||
or
|
||||
// flow through a flow summary (extension of `SummaryModelCsv`)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import swift
|
||||
private import codeql.swift.dataflow.DataFlow
|
||||
private import codeql.swift.dataflow.ExternalFlow
|
||||
private import codeql.swift.dataflow.FlowSources
|
||||
|
||||
private class UrlRemoteFlowSource extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
@@ -7,7 +9,46 @@ private class UrlRemoteFlowSource extends SourceModelCsv {
|
||||
[
|
||||
";UIApplicationDelegate;true;application(_:open:options:);;;Parameter[1];remote",
|
||||
";UIApplicationDelegate;true;application(_:handleOpen:);;;Parameter[1];remote",
|
||||
";UIApplicationDelegate;true;application(_:open:sourceApplication:annotation:);;;Parameter[1];remote"
|
||||
";UIApplicationDelegate;true;application(_:open:sourceApplication:annotation:);;;Parameter[1];remote",
|
||||
// TODO: The actual source is the value of `UIApplication.LaunchOptionsKey.url` in the launchOptions dictionary.
|
||||
// Use dictionary value contents when available.
|
||||
// ";UIApplicationDelegate;true;application(_:didFinishLaunchingWithOptions:);;;Parameter[1].MapValue;remote",
|
||||
// ";UIApplicationDelegate;true;application(_:willFinishLaunchingWithOptions:);;;Parameter[1].MapValue;remote"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A read of `UIApplication.LaunchOptionsKey.url` on a dictionary received in
|
||||
* `UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:)` or
|
||||
* `UIApplicationDelegate.application(_:willFinishLaunchingWithOptions:)`.
|
||||
*/
|
||||
// This is a temporary workaround until the TODO above is addressed.
|
||||
private class UrlLaunchOptionsRemoteFlowSource extends RemoteFlowSource {
|
||||
UrlLaunchOptionsRemoteFlowSource() {
|
||||
exists(ApplicationWithLaunchOptionsFunc f, SubscriptExpr e |
|
||||
DataFlow::localExprFlow(f.getParam(1).getAnAccess(), e.getBase()) and
|
||||
e.getAnArgument().getExpr().(MemberRefExpr).getMember() instanceof LaunchOptionsUrlVarDecl and
|
||||
this.asExpr() = e
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() {
|
||||
result = "Remote URL in UIApplicationDelegate.application.launchOptions"
|
||||
}
|
||||
}
|
||||
|
||||
private class ApplicationWithLaunchOptionsFunc extends FuncDecl {
|
||||
ApplicationWithLaunchOptionsFunc() {
|
||||
this.getName() = "application(_:" + ["did", "will"] + "FinishLaunchingWithOptions:)" and
|
||||
this.getEnclosingDecl().(ClassOrStructDecl).getABaseTypeDecl*().(ProtocolDecl).getName() =
|
||||
"UIApplicationDelegate"
|
||||
}
|
||||
}
|
||||
|
||||
private class LaunchOptionsUrlVarDecl extends VarDecl {
|
||||
LaunchOptionsUrlVarDecl() {
|
||||
this.getEnclosingDecl().(StructDecl).getFullName() = "UIApplication.LaunchOptionsKey" and
|
||||
this.getName() = "url"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,8 @@ edges
|
||||
| test.swift:219:13:219:15 | .a [x] : | test.swift:219:13:219:17 | .x |
|
||||
| test.swift:225:14:225:21 | call to source() : | test.swift:235:13:235:15 | .source_value |
|
||||
| test.swift:225:14:225:21 | call to source() : | test.swift:238:13:238:15 | .source_value |
|
||||
| test.swift:259:12:259:19 | call to source() : | test.swift:263:13:263:28 | call to optionalSource() : |
|
||||
| test.swift:263:13:263:28 | call to optionalSource() : | test.swift:264:15:264:16 | ...! |
|
||||
nodes
|
||||
| file://:0:0:0:0 | .a [x] : | semmle.label | .a [x] : |
|
||||
| file://:0:0:0:0 | .x : | semmle.label | .x : |
|
||||
@@ -208,6 +210,9 @@ nodes
|
||||
| test.swift:225:14:225:21 | call to source() : | semmle.label | call to source() : |
|
||||
| test.swift:235:13:235:15 | .source_value | semmle.label | .source_value |
|
||||
| test.swift:238:13:238:15 | .source_value | semmle.label | .source_value |
|
||||
| test.swift:259:12:259:19 | call to source() : | semmle.label | call to source() : |
|
||||
| test.swift:263:13:263:28 | call to optionalSource() : | semmle.label | call to optionalSource() : |
|
||||
| test.swift:264:15:264:16 | ...! | semmle.label | ...! |
|
||||
subpaths
|
||||
| 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] &... : |
|
||||
| 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
|
||||
| test.swift:219:13:219:17 | .x | test.swift:218:11:218:18 | call to source() : | test.swift:219:13:219:17 | .x | result |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| test.swift:264:15:264:16 | ...! | test.swift:259:12:259:19 | call to source() : | test.swift:264:15:264:16 | ...! | result |
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import swift
|
||||
import codeql.swift.dataflow.DataFlow
|
||||
import codeql.swift.dataflow.ExternalFlow
|
||||
|
||||
class TestConfiguration extends DataFlow::Configuration {
|
||||
TestConfiguration() { this = "TestConfiguration" }
|
||||
@@ -14,10 +15,17 @@ class TestConfiguration extends DataFlow::Configuration {
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(CallExpr sinkCall |
|
||||
sinkCall.getStaticTarget().getName() = "sink(arg:)" and
|
||||
sinkCall.getStaticTarget().getName() = ["sink(arg:)", "sink(opt:)"] and
|
||||
sinkCall.getAnArgument().getExpr() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override int explorationLimit() { result = 100 }
|
||||
}
|
||||
|
||||
private class TestSummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
// model to allow data flow through `signum()` as though it were an identity function, for the benefit of testing flow through optional chaining (`x?.`).
|
||||
row = ";Int;true;signum();;;Argument[-1];ReturnValue;value"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,3 +184,11 @@
|
||||
| test.swift:247:9:247:9 | [post] self | test.swift:246:5:248:5 | self[return] |
|
||||
| test.swift:247:9:247:9 | self | test.swift:246:5:248:5 | self[return] |
|
||||
| test.swift:252:23:252:23 | value | test.swift:252:23:252:23 | WriteDef |
|
||||
| test.swift:263:9:263:9 | WriteDef | test.swift:264:15:264:15 | x |
|
||||
| test.swift:263:13:263:28 | call to optionalSource() | test.swift:263:9:263:9 | WriteDef |
|
||||
| test.swift:264:15:264:15 | x | test.swift:264:15:264:16 | ...! |
|
||||
| test.swift:264:15:264:15 | x | test.swift:266:15:266:15 | x |
|
||||
| test.swift:266:15:266:15 | x | test.swift:266:15:266:16 | ...? |
|
||||
| test.swift:266:15:266:15 | x | test.swift:267:15:267:15 | x |
|
||||
| test.swift:266:15:266:25 | call to signum() | test.swift:266:15:266:25 | OptionalEvaluationExpr |
|
||||
| test.swift:267:15:267:15 | x | test.swift:268:16:268:16 | x |
|
||||
|
||||
@@ -251,4 +251,21 @@ func test_computed_property() {
|
||||
func test_property_wrapper() {
|
||||
@DidSetSource var x = 42
|
||||
sink(arg: x) // $ MISSING: flow=243
|
||||
}
|
||||
}
|
||||
|
||||
func sink(opt: Int?) {}
|
||||
|
||||
func optionalSource() -> Int? {
|
||||
return source()
|
||||
}
|
||||
|
||||
func test_optionals() {
|
||||
let x = optionalSource()
|
||||
sink(arg: x!) // $ flow=259
|
||||
sink(arg: source().signum()) // $ MISSING: flow=259
|
||||
sink(opt: x?.signum()) // $ MISSING: flow=259
|
||||
sink(arg: x ?? 0) // $ MISSING: flow=259
|
||||
if let y = x {
|
||||
sink(arg: y) // $ MISSING: flow=259
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
| customurlschemes.swift:23:44:23:54 | url | external |
|
||||
| customurlschemes.swift:27:52:27:68 | url | external |
|
||||
| customurlschemes.swift:31:52:31:62 | url | external |
|
||||
| customurlschemes.swift:30:44:30:54 | url | external |
|
||||
| customurlschemes.swift:34:52:34:68 | url | external |
|
||||
| customurlschemes.swift:38:52:38:62 | url | external |
|
||||
| customurlschemes.swift:43:9:43:28 | ...[...] | Remote URL in UIApplicationDelegate.application.launchOptions |
|
||||
| customurlschemes.swift:48:9:48:28 | ...[...] | Remote URL in UIApplicationDelegate.application.launchOptions |
|
||||
| string.swift:27:21:27:21 | call to init(contentsOf:) | external |
|
||||
| string.swift:27:21:27:44 | call to init(contentsOf:) | external |
|
||||
| url.swift:53:15:53:19 | .resourceBytes | external |
|
||||
|
||||
@@ -7,6 +7,11 @@ class UIApplication {
|
||||
|
||||
func hash(into hasher: inout Hasher) {}
|
||||
}
|
||||
struct LaunchOptionsKey: Hashable {
|
||||
init(rawValue: String) {}
|
||||
public static let url: UIApplication.LaunchOptionsKey = UIApplication.LaunchOptionsKey(rawValue: "")
|
||||
func hash(into hasher: inout Hasher) {}
|
||||
}
|
||||
}
|
||||
|
||||
struct URL {}
|
||||
@@ -15,21 +20,33 @@ protocol UIApplicationDelegate {
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
|
||||
func application(_ application: UIApplication, handleOpen url: URL) -> Bool
|
||||
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool
|
||||
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
class AppDelegate: UIApplicationDelegate {
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool { // SOURCE
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, handleOpen url: URL) -> Bool { // SOURCE
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { // SOURCE
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
|
||||
launchOptions?[.url] // SOURCE
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
|
||||
launchOptions?[.url] // SOURCE
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user