Skip to content

Commit 6dfad79

Browse files
committed
Swift: Model FilePath.
1 parent 371bcc5 commit 6dfad79

File tree

2 files changed

+111
-35
lines changed
  • swift/ql

2 files changed

+111
-35
lines changed

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

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
*/
44

55
import swift
6+
private import codeql.swift.dataflow.DataFlow
67
private import codeql.swift.dataflow.ExternalFlow
8+
private import codeql.swift.dataflow.FlowSteps
79

8-
/** The struct `FilePath`. */
10+
/**
11+
* The struct `FilePath`.
12+
*/
913
class FilePath extends StructDecl {
1014
FilePath() { this.getFullName() = "FilePath" }
1115
}
@@ -15,6 +19,78 @@ class FilePath extends StructDecl {
1519
*/
1620
private class FilePathSummaries extends SummaryModelCsv {
1721
override predicate row(string row) {
18-
row = ";FilePath;true;init(stringLiteral:);(String);;Argument[0];ReturnValue;taint"
22+
row =
23+
[
24+
";FilePath;true;init(stringLiteral:);(String);;Argument[0];ReturnValue;taint",
25+
";FilePath;true;init(extendedGraphemeClusterLiteral:);;;Argument[0];ReturnValue;taint",
26+
";FilePath;true;init(unicodeScalarLiteral:);;;Argument[0];ReturnValue;taint",
27+
";FilePath;true;init(from:);;;Argument[0];ReturnValue;taint",
28+
";FilePath;true;init(_:);;;Argument[0];ReturnValue;taint",
29+
";FilePath;true;init(cString:);;;Argument[0];ReturnValue;taint",
30+
";FilePath;true;init(platformString:);;;Argument[0];ReturnValue;taint",
31+
";FilePath;true;init(root:_:);;;Argument[0..1];ReturnValue;taint",
32+
";FilePath;true;init(root:components:);;;Argument[0..1];ReturnValue;taint",
33+
";FilePath;true;init();;;Argument[0];ReturnValue;taint",
34+
";FilePath;true;init();;;Argument[0];ReturnValue;taint",
35+
";FilePath;true;encode(to:);;;Argument[-1];Argument[0];taint",
36+
";FilePath;true;withCString(_:);;;Argument[-1];Argument[0].Parameter[0];taint",
37+
";FilePath;true;withPlatformString(_:);;;Argument[-1];Argument[0].Parameter[0];taint",
38+
";FilePath;true;append(_:);;;Argument[0];Argument[-1];taint",
39+
";FilePath;true;appending(_:);;;Argument[-1..0];ReturnValue;taint",
40+
";FilePath;true;lexicallyNormalized();;;Argument[-1];ReturnValue;taint",
41+
";FilePath;true;lexicallyResolving(_:);;;Argument[-1..0];ReturnValue;taint",
42+
";FilePath;true;push(_:);;;Argument[0];Argument[-1];taint",
43+
";FilePath;true;pushing(_:);;;Argument[-1..0];ReturnValue;taint",
44+
";FilePath;true;removingLastComponent();;;Argument[-1];ReturnValue;taint",
45+
";FilePath;true;removingRoot();;;Argument[-1];ReturnValue;taint",
46+
";FilePath.Component;true;init(_:);;;Argument[0];ReturnValue;taint",
47+
";FilePath.Component;true;init(platformString:);;;Argument[0];ReturnValue;taint",
48+
";FilePath.Component;true;withPlatformString(_:);;;Argument[-1];Argument[0].Parameter[0];taint",
49+
";FilePath.Root;true;init(_:);;;Argument[0];ReturnValue;taint",
50+
";FilePath.Root;true;init(platformString:);;;Argument[0];ReturnValue;taint",
51+
";FilePath.Root;true;withPlatformString(_:);;;Argument[-1];Argument[0].Parameter[0];taint"
52+
]
53+
}
54+
}
55+
56+
/**
57+
* A content implying that, if a `FilePath` is tainted, certain fields are also
58+
* tainted.
59+
*/
60+
private class FilePathFieldsInheritTaint extends TaintInheritingContent,
61+
DataFlow::Content::FieldContent
62+
{
63+
FilePathFieldsInheritTaint() {
64+
exists(FieldDecl f | this.getField() = f |
65+
(
66+
f.getEnclosingDecl().(NominalTypeDecl) instanceof FilePath or
67+
f.getEnclosingDecl().(ExtensionDecl).getExtendedTypeDecl() instanceof FilePath
68+
) and
69+
f.getName() =
70+
[
71+
"description", "debugDescription", "components", "extension", "lastComponent", "root",
72+
"stem", "string"
73+
]
74+
)
75+
}
76+
}
77+
78+
/**
79+
* A content implying that, if a `FilePath.Component` or `FilePath.Root` is tainted, certain fields
80+
* are also tainted.
81+
*/
82+
private class FilePathComponentFieldsInheritTaint extends TaintInheritingContent,
83+
DataFlow::Content::FieldContent
84+
{
85+
FilePathComponentFieldsInheritTaint() {
86+
exists(FieldDecl f | this.getField() = f |
87+
(
88+
f.getEnclosingDecl().(NominalTypeDecl).getFullName() =
89+
["FilePath.Component", "FilePath.Root"] or
90+
f.getEnclosingDecl().(ExtensionDecl).getExtendedTypeDecl().getName() =
91+
["FilePath.Component", "FilePath.Root"]
92+
) and
93+
f.getName() = ["extension", "stem", "string"]
94+
)
1995
}
2096
}

swift/ql/test/library-tests/dataflow/taint/libraries/files.swift

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,24 @@ func test_files(e1: Encoder) {
107107
// --- FilePath.Root, FilePath.Component ---
108108

109109
sink(string: FilePath.Root("/")!.string)
110-
sink(string: FilePath.Root(sourceString())!.string) // $ MISSING: tainted=
110+
sink(string: FilePath.Root(sourceString())!.string) // $ tainted=110
111111
sink(string: FilePath.Component("path")!.string)
112-
sink(string: FilePath.Component(sourceString())!.string) // $ MISSING: tainted=
112+
sink(string: FilePath.Component(sourceString())!.string) // $ tainted=112
113113

114114
// --- FilePath constructors ---
115115

116116
let cleanUrl = URL(string: "https://example.com")!
117117
let taintedUrl = URL(string: sourceString())!
118118

119119
sink(filePath: FilePath("my/path"))
120-
sink(filePath: FilePath(sourceString())) // $ MISSING: tainted=
120+
sink(filePath: FilePath(sourceString())) // $ tainted=120
121121
sink(filePath: FilePath(cleanUrl)!)
122-
sink(filePath: FilePath(taintedUrl)!) // $ MISSING: tainted=
123-
sink(filePath: FilePath(from: sourceDecoder())) // $ MISSING: tainted=
124-
sink(filePath: FilePath(cString: sourceCCharArray())) // $ MISSING: tainted=
125-
sink(filePath: FilePath(cString: sourceCString())) // $ MISSING: tainted=
122+
sink(filePath: FilePath(taintedUrl)!) // $ tainted=117
123+
sink(filePath: FilePath(from: sourceDecoder())) // $ tainted=123
124+
sink(filePath: FilePath(cString: sourceCCharArray())) // $ tainted=124
125+
sink(filePath: FilePath(cString: sourceCString())) // $ tainted=125
126126
sink(filePath: FilePath(root: FilePath.Root("/"), [FilePath.Component("my")!, FilePath.Component("path")!]))
127-
sink(filePath: FilePath(root: FilePath.Root(sourceString()), [FilePath.Component("my")!, FilePath.Component("path")!])) // $ MISSING: tainted=
127+
sink(filePath: FilePath(root: FilePath.Root(sourceString()), [FilePath.Component("my")!, FilePath.Component("path")!])) // $ tainted=127
128128
sink(filePath: FilePath(root: FilePath.Root("/"), [FilePath.Component("my")!, FilePath.Component(sourceString())!])) // $ MISSING: tainted=
129129

130130
// --- FilePath methods ---
@@ -133,28 +133,28 @@ func test_files(e1: Encoder) {
133133
let tainted = FilePath(sourceString())
134134

135135
sink(filePath: clean)
136-
sink(filePath: tainted) // $ MISSING: tainted=
136+
sink(filePath: tainted) // $ tainted=133
137137

138138
sink(encoder: e1)
139139
try! clean.encode(to: e1)
140140
sink(encoder: e1)
141141
try! tainted.encode(to: e1)
142142
sink(encoder: e1) // $ MISSING: tainted=
143143

144-
sink(string: String(decoding: tainted)) // $ MISSING: tainted=
145-
sink(string: String(validating: tainted)!) // $ MISSING: tainted=
144+
sink(string: String(decoding: tainted)) // $ tainted=133
145+
sink(string: String(validating: tainted)!) // $ tainted=133
146146

147147
sink(filePath: clean.lexicallyResolving(clean)!)
148-
sink(filePath: tainted.lexicallyResolving(clean)!) // $ MISSING: tainted=
149-
sink(filePath: clean.lexicallyResolving(tainted)!) // $ MISSING: tainted=
148+
sink(filePath: tainted.lexicallyResolving(clean)!) // $ tainted=133
149+
sink(filePath: clean.lexicallyResolving(tainted)!) // $ tainted=133
150150

151151
let _ = clean.withCString({
152152
ptr in
153153
sink(ptr: ptr)
154154
})
155155
let _ = tainted.withCString({
156156
ptr in
157-
sink(ptr: ptr) // $ MISSING: tainted=
157+
sink(ptr: ptr) // $ tainted=133
158158
})
159159

160160
let _ = clean.withPlatformString({
@@ -165,37 +165,37 @@ func test_files(e1: Encoder) {
165165
})
166166
let _ = tainted.withPlatformString({
167167
ptr in
168-
sink(ptr: ptr) // $ MISSING: tainted=
169-
sink(string: String(platformString: ptr)) // $ MISSING: tainted=
170-
sink(string: String(validatingPlatformString: ptr)!) // $ MISSING: tainted=
168+
sink(ptr: ptr) // $ tainted=133
169+
sink(string: String(platformString: ptr)) // $ tainted=133
170+
sink(string: String(validatingPlatformString: ptr)!) // $ tainted=133
171171
})
172172

173173
var fp1 = FilePath("")
174174
sink(filePath: fp1)
175175
fp1.append(sourceString())
176-
sink(filePath: fp1) // $ MISSING: tainted=
176+
sink(filePath: fp1) // $ tainted=175
177177
fp1.append("")
178-
sink(filePath: fp1) // $ MISSING: tainted=
178+
sink(filePath: fp1) // $ tainted=175
179179

180180
sink(filePath: clean.appending(""))
181-
sink(filePath: clean.appending(sourceString())) // $ MISSING: tainted=
182-
sink(filePath: tainted.appending("")) // $ MISSING: tainted=
183-
sink(filePath: tainted.appending(sourceString())) // $ MISSING: tainted=
181+
sink(filePath: clean.appending(sourceString())) // $ tainted=181
182+
sink(filePath: tainted.appending("")) // $ tainted=133
183+
sink(filePath: tainted.appending(sourceString())) // $ tainted=133 tainted=183
184184

185185
// --- FilePath member variables ---
186186

187-
sink(string: tainted.description) // $ MISSING: tainted=
188-
sink(string: tainted.debugDescription) // $ MISSING: tainted=
189-
sink(string: tainted.extension!) // $ MISSING: tainted=
190-
sink(string: tainted.stem!) // $ MISSING: tainted=
191-
sink(string: tainted.string) // $ MISSING: tainted=
187+
sink(string: tainted.description) // $ tainted=133
188+
sink(string: tainted.debugDescription) // $ tainted=133
189+
sink(string: tainted.extension!) // $ tainted=133
190+
sink(string: tainted.stem!) // $ tainted=133
191+
sink(string: tainted.string) // $ tainted=133
192192

193-
sink(component: tainted.lastComponent!) // $ MISSING: tainted=
194-
sink(string: tainted.lastComponent!.string) // $ MISSING: tainted=
195-
sink(root: tainted.root!) // $ MISSING: tainted=
196-
sink(string: tainted.root!.string) // $ MISSING: tainted=
193+
sink(component: tainted.lastComponent!) // $ tainted=133
194+
sink(string: tainted.lastComponent!.string) // $ tainted=133
195+
sink(root: tainted.root!) // $ tainted=133
196+
sink(string: tainted.root!.string) // $ tainted=133
197197

198198
let taintedComponents = tainted.components
199-
sink(componentView: taintedComponents) // $ MISSING: tainted=
200-
sink(string: taintedComponents[taintedComponents.startIndex].string) // $ MISSING: tainted=
199+
sink(componentView: taintedComponents) // $ tainted=133
200+
sink(string: taintedComponents[taintedComponents.startIndex].string) // $ tainted=133
201201
}

0 commit comments

Comments
 (0)