mirror of
https://github.com/github/codeql.git
synced 2026-05-05 21:55:19 +02:00
Merge pull request #14502 from geoffw0/xmlquery
Swift: Model RawRepresentable
This commit is contained in:
5
swift/ql/lib/change-notes/2023-10-13-rawrepresentable.md
Normal file
5
swift/ql/lib/change-notes/2023-10-13-rawrepresentable.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
|
||||
* Added taint flow models for `RawRepresentable`.
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Provides models the `RawRepresentable` Swift class.
|
||||
*/
|
||||
|
||||
import swift
|
||||
private import codeql.swift.dataflow.DataFlow
|
||||
private import codeql.swift.dataflow.ExternalFlow
|
||||
private import codeql.swift.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* A model for `RawRepresentable` class members that permit taint flow.
|
||||
*/
|
||||
private class RawRepresentableSummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row = ";RawRepresentable;true;init(rawValue:);;;Argument[0];ReturnValue;taint"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A content implying that, if a `RawRepresentable` is tainted, then the
|
||||
* `rawValue` field is tainted as well. This model has been extended to assume
|
||||
* that any object's `rawValue` field also inherits taint.
|
||||
*/
|
||||
private class RawRepresentableFieldsInheritTaint extends TaintInheritingContent,
|
||||
DataFlow::Content::FieldContent
|
||||
{
|
||||
RawRepresentableFieldsInheritTaint() { this.getField().getName() = "rawValue" }
|
||||
}
|
||||
@@ -17,6 +17,7 @@ private import NsObject
|
||||
private import NsString
|
||||
private import NsUrl
|
||||
private import Numeric
|
||||
private import RawRepresentable
|
||||
private import PointerTypes
|
||||
private import Sequence
|
||||
private import Set
|
||||
|
||||
@@ -172,31 +172,12 @@ private class Libxml2XxeSink extends XxeSink {
|
||||
Libxml2XxeSink() {
|
||||
exists(Libxml2ParseCall c, Libxml2BadOption opt |
|
||||
this.asExpr() = c.getXml() and
|
||||
lib2xmlOptionLocalTaintStep*(DataFlow::exprNode(opt.getAnAccess()),
|
||||
TaintTracking::localTaintStep*(DataFlow::exprNode(opt.getAnAccess()),
|
||||
DataFlow::exprNode(c.getOptions()))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `source` to `sink` in one local step,
|
||||
* including bitwise operations, accesses to `.rawValue`, and casts to `Int32`.
|
||||
*/
|
||||
private predicate lib2xmlOptionLocalTaintStep(DataFlow::Node source, DataFlow::Node sink) {
|
||||
TaintTracking::localTaintStep(source, sink)
|
||||
or
|
||||
exists(MemberRefExpr rawValue | rawValue.getMember().(VarDecl).getName() = "rawValue" |
|
||||
source.asExpr() = rawValue.getBase() and sink.asExpr() = rawValue
|
||||
)
|
||||
or
|
||||
exists(ApplyExpr int32Init |
|
||||
int32Init.getStaticTarget().(Initializer).getEnclosingDecl().asNominalTypeDecl().getName() =
|
||||
"SignedInteger"
|
||||
|
|
||||
source.asExpr() = int32Init.getAnArgument().getExpr() and sink.asExpr() = int32Init
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink defined in a CSV model.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
// --- stubs ---
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func sourceInt() -> Int { return 0 }
|
||||
func sourceUInt() -> UInt { return 0 }
|
||||
func sink(arg: Any) {}
|
||||
|
||||
// ---
|
||||
|
||||
enum MyRawRepresentable : RawRepresentable {
|
||||
case valueOne
|
||||
case valueTwo
|
||||
|
||||
init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 1: self = .valueOne
|
||||
case 2: self = .valueTwo
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
var rawValue: Int {
|
||||
switch self {
|
||||
case .valueOne: return 1
|
||||
case .valueTwo: return 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRawRepresentable() {
|
||||
let rr1 = MyRawRepresentable.valueOne
|
||||
let rr2 = MyRawRepresentable(rawValue: 1)!
|
||||
let rr3 = MyRawRepresentable(rawValue: sourceInt())!
|
||||
|
||||
sink(arg: rr1)
|
||||
sink(arg: rr2)
|
||||
sink(arg: rr3) // $ tainted=35
|
||||
|
||||
sink(arg: rr1.rawValue)
|
||||
sink(arg: rr2.rawValue)
|
||||
sink(arg: rr3.rawValue) // $ tainted=35
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
struct MyOptionSet : OptionSet {
|
||||
let rawValue: UInt
|
||||
|
||||
static let red = MyOptionSet(rawValue: 1 << 0)
|
||||
static let green = MyOptionSet(rawValue: 1 << 1)
|
||||
static let blue = MyOptionSet(rawValue: 1 << 2)
|
||||
}
|
||||
|
||||
func testOptionSet() {
|
||||
sink(arg: MyOptionSet.red)
|
||||
sink(arg: MyOptionSet([.red, .green]))
|
||||
sink(arg: MyOptionSet(rawValue: 0))
|
||||
sink(arg: MyOptionSet(rawValue: sourceUInt())) // $ tainted=60
|
||||
|
||||
sink(arg: MyOptionSet.red.rawValue)
|
||||
sink(arg: MyOptionSet([.red, .green]).rawValue)
|
||||
sink(arg: MyOptionSet(rawValue: 0).rawValue)
|
||||
sink(arg: MyOptionSet(rawValue: sourceUInt()).rawValue) // $ tainted=65
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
failures
|
||||
|
||||
Reference in New Issue
Block a user