зеркало из https://github.com/github/codeql.git
Merge pull request #14925 from geoffw0/flows
Swift: Imprecise Taint Flows
This commit is contained in:
Коммит
d8f53e5524
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added imprecise flow models for `append` and `insert` methods, and initializer calls with a `data` argument.
|
|
@ -6,6 +6,7 @@
|
|||
import swift
|
||||
private import codeql.swift.dataflow.DataFlow
|
||||
private import codeql.swift.dataflow.FlowSources
|
||||
private import codeql.swift.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* An initializer call `ce` that has a "contentsOf" argument, along with a
|
||||
|
@ -51,3 +52,60 @@ private class InitializerContentsOfLocalSource extends LocalFlowSource {
|
|||
|
||||
override string getSourceType() { result = "contentsOf initializer" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An imprecise flow step for an initializer call with a "data" argument. For
|
||||
* example:
|
||||
* ```
|
||||
* let mc = MyClass(data: taintedData)
|
||||
* ```
|
||||
*/
|
||||
private class InitializerFromDataStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(InitializerCallExpr ce, Argument arg |
|
||||
ce.getAnArgument() = arg and
|
||||
arg.getLabel() = "data" and
|
||||
node1.asExpr() = arg.getExpr() and
|
||||
node2.asExpr() = ce
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An imprecise flow step for an `append`, `insert` or similar function. For
|
||||
* example:
|
||||
* ```
|
||||
* mc.append(taintedObj)
|
||||
* mc.insert(taintedObj, at: 0)
|
||||
* ```
|
||||
*/
|
||||
private class AppendCallStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(CallExpr ce, Argument arg |
|
||||
ce.getAnArgument() = arg and
|
||||
ce.getStaticTarget().(Function).getShortName() = ["append", "insert"] and
|
||||
arg.getLabel() = ["", "contentsOf"] and
|
||||
node1.asExpr() = arg.getExpr() and
|
||||
node2.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = ce.getQualifier()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An imprecise flow step for an `appending` or similar function. For
|
||||
* example:
|
||||
* ```
|
||||
* let mc2 = mc.appending(taintedObj)
|
||||
* ```
|
||||
*/
|
||||
private class AppendingCallStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(CallExpr ce, Argument arg |
|
||||
ce.getAnArgument() = arg and
|
||||
ce.getStaticTarget().(Function).getShortName() = ["appending", "inserting"] and
|
||||
arg.getLabel() = ["", "contentsOf"] and
|
||||
node1.asExpr() = arg.getExpr() and
|
||||
node2.asExpr() = ce
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
// --- stubs ---
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func source(_ label: String) -> Int { return 0 }
|
||||
func sink(arg: Any) {}
|
||||
|
||||
// ---
|
||||
|
||||
func testArray() {
|
||||
let arrClean = Array<Int>()
|
||||
|
||||
var arr1 = Array<Int>()
|
||||
arr1.append(1)
|
||||
sink(arg: arr1[arr1.endIndex - 1])
|
||||
arr1.append(source("int1"))
|
||||
sink(arg: arr1[arr1.endIndex - 1]) // $ tainted=int1
|
||||
|
||||
var arr2 = Array<Int>()
|
||||
arr2.append(contentsOf: arrClean)
|
||||
sink(arg: arr2[arr2.endIndex - 1])
|
||||
arr2.append(contentsOf: arr1)
|
||||
sink(arg: arr2[arr2.endIndex - 1]) // $ tainted=int1
|
||||
|
||||
var arr3 = Array<Int>()
|
||||
arr3.insert(1, at: 0)
|
||||
sink(arg: arr3[0])
|
||||
arr3.insert(source("int3"), at: 0)
|
||||
sink(arg: arr3[0]) // $ tainted=int3
|
||||
|
||||
var arr4 = Array<Int>()
|
||||
arr4.insert(contentsOf: arrClean, at: 0)
|
||||
sink(arg: arr4[0])
|
||||
arr4.insert(contentsOf: arr3, at: 0)
|
||||
sink(arg: arr4[0]) // $ tainted=int3
|
||||
|
||||
var arr5 = Array<Int>()
|
||||
arr5.insert(contentsOf: 1...10, at: 0)
|
||||
sink(arg: arr5[arr5.endIndex - 1])
|
||||
arr5.insert(contentsOf: 1...source("int5"), at: 0)
|
||||
sink(arg: arr5[arr5.endIndex - 1]) // $ MISSING: tainted=int5
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// --- stubs ---
|
||||
|
||||
class Data {
|
||||
init<S>(_ elements: S) {}
|
||||
}
|
||||
|
||||
struct URL {
|
||||
init?(string: String) {}
|
||||
}
|
||||
|
||||
// A `MyContainer` contains `Data` in something rather like a `Sequence`.
|
||||
struct MyContainer {
|
||||
init() { }
|
||||
init(data: Data) { }
|
||||
init(data: Data, flags: Int) { }
|
||||
|
||||
mutating func append(_ data: Data) { }
|
||||
mutating func insert(_ data: Data, at: Int) { }
|
||||
func appending(_ data: Data) -> MyContainer { return self }
|
||||
func inserting(_ data: Data, at: Int) -> MyContainer { return self }
|
||||
|
||||
mutating func append(contentsOf other: MyContainer) { }
|
||||
mutating func insert(contentsOf other: MyContainer, at: Int) { }
|
||||
func appending(contentsOf other: MyContainer) -> MyContainer { return self }
|
||||
func inserting(contentsOf other: MyContainer, at: Int) -> MyContainer { return self }
|
||||
|
||||
mutating func append(_ string: String) { }
|
||||
mutating func append(contentsOf array: Array<Data>) { }
|
||||
|
||||
subscript(index: Int) -> Data { return Data(0) }
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func source(_ label: String) -> Data { return Data(0) }
|
||||
func sourceString(_ label: String) -> String { return "" }
|
||||
func sink(arg: Any) {}
|
||||
|
||||
// ---
|
||||
|
||||
func testCustom() {
|
||||
let clean = MyContainer(data: Data(0))
|
||||
let tainted = MyContainer(data: source("data1"))
|
||||
let tainted2 = MyContainer(data: source("data2"), flags: 123)
|
||||
sink(arg: clean)
|
||||
sink(arg: clean[0])
|
||||
sink(arg: tainted) // $ tainted=data1
|
||||
sink(arg: tainted[0]) // $ tainted=data1
|
||||
sink(arg: tainted2) // $ tainted=data2
|
||||
sink(arg: tainted2[0]) // $ tainted=data2
|
||||
|
||||
var mc1 = MyContainer()
|
||||
mc1.append(Data(0))
|
||||
sink(arg: mc1)
|
||||
sink(arg: mc1[0])
|
||||
mc1.append(source("data3"))
|
||||
sink(arg: mc1) // $ tainted=data3
|
||||
sink(arg: mc1[0]) // $ tainted=data3
|
||||
|
||||
var mc2 = MyContainer()
|
||||
mc2.insert(Data(0), at: 0)
|
||||
sink(arg: mc2)
|
||||
sink(arg: mc2[0])
|
||||
mc2.insert(source("data4"), at: 0)
|
||||
sink(arg: mc2) // $ tainted=data4
|
||||
sink(arg: mc2[0]) // $ tainted=data4
|
||||
|
||||
var mc3 = MyContainer()
|
||||
mc3.append(contentsOf: clean)
|
||||
sink(arg: mc3)
|
||||
sink(arg: mc3[0])
|
||||
mc3.append(contentsOf: tainted)
|
||||
sink(arg: mc3) // $ tainted=data1
|
||||
sink(arg: mc3[0]) // $ tainted=data1
|
||||
|
||||
var mc4 = MyContainer()
|
||||
mc4.insert(contentsOf: clean, at: 0)
|
||||
sink(arg: mc4)
|
||||
sink(arg: mc4[0])
|
||||
mc4.insert(contentsOf: tainted, at: 0)
|
||||
sink(arg: mc4) // $ tainted=data1
|
||||
sink(arg: mc4[0]) // $ tainted=data1
|
||||
|
||||
let mc5 = MyContainer()
|
||||
sink(arg: mc5.appending(Data(0)))
|
||||
sink(arg: mc5.appending(Data(0))[0])
|
||||
sink(arg: mc5.appending(source("data5"))) // $ tainted=data5
|
||||
sink(arg: mc5.appending(source("data6"))[0]) // $ tainted=data6
|
||||
sink(arg: mc5.inserting(Data(0), at: 0))
|
||||
sink(arg: mc5.inserting(Data(0), at: 0)[0])
|
||||
sink(arg: mc5.inserting(source("data7"), at: 0)) // $ tainted=data7
|
||||
sink(arg: mc5.inserting(source("data8"), at: 0)[0]) // $ tainted=data8
|
||||
sink(arg: mc5.appending(contentsOf: clean))
|
||||
sink(arg: mc5.appending(contentsOf: clean)[0])
|
||||
sink(arg: mc5.appending(contentsOf: tainted)) // $ tainted=data1
|
||||
sink(arg: mc5.appending(contentsOf: tainted)[0]) // $ tainted=data1
|
||||
sink(arg: mc5.inserting(contentsOf: clean, at: 0))
|
||||
sink(arg: mc5.inserting(contentsOf: clean, at: 0)[0])
|
||||
sink(arg: mc5.inserting(contentsOf: tainted, at: 0)) // $ tainted=data1
|
||||
sink(arg: mc5.inserting(contentsOf: tainted, at: 0)[0]) // $ tainted=data1
|
||||
sink(arg: mc5)
|
||||
|
||||
let taintedString = sourceString("data9")
|
||||
var mc6 = MyContainer()
|
||||
mc6.append("")
|
||||
sink(arg: mc6)
|
||||
sink(arg: mc6[0])
|
||||
mc6.append(taintedString)
|
||||
sink(arg: mc6) // $ tainted=data9
|
||||
sink(arg: mc6[0]) // $ tainted=data9
|
||||
|
||||
let taintedArray = [source("data10")]
|
||||
var mc7 = MyContainer()
|
||||
mc7.append(contentsOf: [])
|
||||
sink(arg: mc7)
|
||||
sink(arg: mc7[0])
|
||||
mc7.append(contentsOf: taintedArray)
|
||||
sink(arg: mc7) // $ MISSING: tainted=data10
|
||||
sink(arg: mc7[0]) // $ MISSING: tainted=data10
|
||||
}
|
|
@ -41,31 +41,42 @@ class UIScene {
|
|||
class OpenURLOptions {}
|
||||
}
|
||||
|
||||
struct CGFloat { }
|
||||
|
||||
class Data {
|
||||
init<S>(_ elements: S) {}
|
||||
}
|
||||
|
||||
class UIImage {
|
||||
init?(data: Data) { }
|
||||
init?(data: Data, scale: CGFloat) { }
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func source() -> Any { return "" }
|
||||
func source(_ label: String) -> Any { return "" }
|
||||
func sink(_: Any) {}
|
||||
|
||||
func testUIOpenURLContext() {
|
||||
let safe = UIOpenURLContext()
|
||||
let tainted = source() as! UIOpenURLContext
|
||||
let tainted = source("OpenURLContext") as! UIOpenURLContext
|
||||
|
||||
sink(safe.url)
|
||||
sink(safe.options)
|
||||
sink(tainted.url) // $ tainted=51
|
||||
sink(tainted.url) // $ tainted=OpenURLContext
|
||||
sink(tainted.options)
|
||||
}
|
||||
|
||||
func testConnectionOptions() {
|
||||
let safe = UIScene.ConnectionOptions()
|
||||
let tainted = source() as! UIScene.ConnectionOptions
|
||||
let tainted = source("ConnectionOptions") as! UIScene.ConnectionOptions
|
||||
|
||||
sink(safe.userActivities)
|
||||
sink(tainted.userActivities) // $ tainted=61
|
||||
sink(tainted.userActivities) // $ tainted=ConnectionOptions
|
||||
sink(safe.shortcutItem)
|
||||
sink(tainted.shortcutItem)
|
||||
sink(safe.urlContexts)
|
||||
sink(tainted.urlContexts) // $ tainted=61
|
||||
sink(tainted.urlContexts) // $ tainted=ConnectionOptions
|
||||
sink(safe.handoffUserActivityType)
|
||||
sink(tainted.handoffUserActivityType)
|
||||
sink(safe.cloudKitShareMetadata)
|
||||
|
@ -75,3 +86,12 @@ func testConnectionOptions() {
|
|||
sink(safe.sourceApplication)
|
||||
sink(tainted.sourceApplication)
|
||||
}
|
||||
|
||||
func testUIImage(scale: CGFloat) {
|
||||
let taintedData = source("UIImage") as! Data
|
||||
|
||||
sink(UIImage(data: Data(0))!)
|
||||
sink(UIImage(data: Data(taintedData))!) // $ tainted=UIImage
|
||||
sink(UIImage(data: Data(0), scale: scale)!)
|
||||
sink(UIImage(data: Data(taintedData), scale: scale)!) // $ tainted=UIImage
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче