Skip to content

Commit 06b1cd9

Browse files
authored
Merge pull request github#14502 from geoffw0/xmlquery
Swift: Model RawRepresentable
2 parents 9652679 + e2ac376 commit 06b1cd9

File tree

6 files changed

+102
-21
lines changed

6 files changed

+102
-21
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
5+
* Added taint flow models for `RawRepresentable`.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Provides models the `RawRepresentable` Swift class.
3+
*/
4+
5+
import swift
6+
private import codeql.swift.dataflow.DataFlow
7+
private import codeql.swift.dataflow.ExternalFlow
8+
private import codeql.swift.dataflow.FlowSteps
9+
10+
/**
11+
* A model for `RawRepresentable` class members that permit taint flow.
12+
*/
13+
private class RawRepresentableSummaries extends SummaryModelCsv {
14+
override predicate row(string row) {
15+
row = ";RawRepresentable;true;init(rawValue:);;;Argument[0];ReturnValue;taint"
16+
}
17+
}
18+
19+
/**
20+
* A content implying that, if a `RawRepresentable` is tainted, then the
21+
* `rawValue` field is tainted as well. This model has been extended to assume
22+
* that any object's `rawValue` field also inherits taint.
23+
*/
24+
private class RawRepresentableFieldsInheritTaint extends TaintInheritingContent,
25+
DataFlow::Content::FieldContent
26+
{
27+
RawRepresentableFieldsInheritTaint() { this.getField().getName() = "rawValue" }
28+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ private import NsObject
1717
private import NsString
1818
private import NsUrl
1919
private import Numeric
20+
private import RawRepresentable
2021
private import PointerTypes
2122
private import Sequence
2223
private import Set

swift/ql/lib/codeql/swift/security/XXEExtensions.qll

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -172,31 +172,12 @@ private class Libxml2XxeSink extends XxeSink {
172172
Libxml2XxeSink() {
173173
exists(Libxml2ParseCall c, Libxml2BadOption opt |
174174
this.asExpr() = c.getXml() and
175-
lib2xmlOptionLocalTaintStep*(DataFlow::exprNode(opt.getAnAccess()),
175+
TaintTracking::localTaintStep*(DataFlow::exprNode(opt.getAnAccess()),
176176
DataFlow::exprNode(c.getOptions()))
177177
)
178178
}
179179
}
180180

181-
/**
182-
* Holds if taint can flow from `source` to `sink` in one local step,
183-
* including bitwise operations, accesses to `.rawValue`, and casts to `Int32`.
184-
*/
185-
private predicate lib2xmlOptionLocalTaintStep(DataFlow::Node source, DataFlow::Node sink) {
186-
TaintTracking::localTaintStep(source, sink)
187-
or
188-
exists(MemberRefExpr rawValue | rawValue.getMember().(VarDecl).getName() = "rawValue" |
189-
source.asExpr() = rawValue.getBase() and sink.asExpr() = rawValue
190-
)
191-
or
192-
exists(ApplyExpr int32Init |
193-
int32Init.getStaticTarget().(Initializer).getEnclosingDecl().asNominalTypeDecl().getName() =
194-
"SignedInteger"
195-
|
196-
source.asExpr() = int32Init.getAnArgument().getExpr() and sink.asExpr() = int32Init
197-
)
198-
}
199-
200181
/**
201182
* A sink defined in a CSV model.
202183
*/
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
// --- stubs ---
3+
4+
// --- tests ---
5+
6+
func sourceInt() -> Int { return 0 }
7+
func sourceUInt() -> UInt { return 0 }
8+
func sink(arg: Any) {}
9+
10+
// ---
11+
12+
enum MyRawRepresentable : RawRepresentable {
13+
case valueOne
14+
case valueTwo
15+
16+
init?(rawValue: Int) {
17+
switch rawValue {
18+
case 1: self = .valueOne
19+
case 2: self = .valueTwo
20+
default: return nil
21+
}
22+
}
23+
24+
var rawValue: Int {
25+
switch self {
26+
case .valueOne: return 1
27+
case .valueTwo: return 2
28+
}
29+
}
30+
}
31+
32+
func testRawRepresentable() {
33+
let rr1 = MyRawRepresentable.valueOne
34+
let rr2 = MyRawRepresentable(rawValue: 1)!
35+
let rr3 = MyRawRepresentable(rawValue: sourceInt())!
36+
37+
sink(arg: rr1)
38+
sink(arg: rr2)
39+
sink(arg: rr3) // $ tainted=35
40+
41+
sink(arg: rr1.rawValue)
42+
sink(arg: rr2.rawValue)
43+
sink(arg: rr3.rawValue) // $ tainted=35
44+
}
45+
46+
// ---
47+
48+
struct MyOptionSet : OptionSet {
49+
let rawValue: UInt
50+
51+
static let red = MyOptionSet(rawValue: 1 << 0)
52+
static let green = MyOptionSet(rawValue: 1 << 1)
53+
static let blue = MyOptionSet(rawValue: 1 << 2)
54+
}
55+
56+
func testOptionSet() {
57+
sink(arg: MyOptionSet.red)
58+
sink(arg: MyOptionSet([.red, .green]))
59+
sink(arg: MyOptionSet(rawValue: 0))
60+
sink(arg: MyOptionSet(rawValue: sourceUInt())) // $ tainted=60
61+
62+
sink(arg: MyOptionSet.red.rawValue)
63+
sink(arg: MyOptionSet([.red, .green]).rawValue)
64+
sink(arg: MyOptionSet(rawValue: 0).rawValue)
65+
sink(arg: MyOptionSet(rawValue: sourceUInt()).rawValue) // $ tainted=65
66+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
failures
21
testFailures
2+
failures

0 commit comments

Comments
 (0)