Skip to content

Commit 371bcc5

Browse files
committed
Swift: Consolidate and extend tests of taint flow through FilePath.
1 parent e402c22 commit 371bcc5

File tree

2 files changed

+224
-74
lines changed

2 files changed

+224
-74
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// --- stubs ---
2+
3+
struct URL {
4+
init?(string: String) {}
5+
}
6+
7+
enum CInterop {
8+
typealias Char = CChar
9+
typealias PlatformChar = CInterop.Char
10+
}
11+
12+
struct FilePath {
13+
struct Component {
14+
init?(_ string: String) { }
15+
16+
var string: String { get { return "" } }
17+
}
18+
19+
struct Root {
20+
init?(_ string: String) { }
21+
22+
var string: String { get { return "" } }
23+
}
24+
25+
struct ComponentView {
26+
}
27+
28+
init(_ string: String) { }
29+
init?(_ url: URL) { }
30+
init(cString: [CChar]) { }
31+
init(cString: UnsafePointer<CChar>) { }
32+
init(from decoder: Decoder) { }
33+
init<C>(root: FilePath.Root?, _ components: C) where C : Collection, C.Element == FilePath.Component { }
34+
35+
func encode(to encoder: Encoder) throws { }
36+
37+
mutating func append(_ other: String) { }
38+
func appending(_ other: String) -> FilePath { return FilePath("") }
39+
func lexicallyResolving(_ subpath: FilePath) -> FilePath? { return nil }
40+
41+
func withCString<Result>(_ body: (UnsafePointer<CChar>) throws -> Result) rethrows -> Result {
42+
return 0 as! Result
43+
}
44+
func withPlatformString<Result>(_ body: (UnsafePointer<CInterop.PlatformChar>) throws -> Result) rethrows -> Result {
45+
return 0 as! Result
46+
}
47+
48+
var description: String { get { return "" } }
49+
var debugDescription: String { get { return "" } }
50+
var `extension`: String? { get { return "" } set { } }
51+
var stem: String? { get { return "" } }
52+
var string: String { get { return "" } }
53+
54+
var components: FilePath.ComponentView { get { return FilePath.ComponentView() } set { } }
55+
var lastComponent: FilePath.Component? { get { return nil} }
56+
var root: FilePath.Root? { get { return nil } set { } }
57+
}
58+
59+
extension FilePath.ComponentView: BidirectionalCollection {
60+
typealias Element = FilePath.Component
61+
62+
struct Index: Comparable {
63+
static func < (lhs: Self, rhs: Self) -> Bool {
64+
return false
65+
}
66+
}
67+
68+
var startIndex: Index { Index() }
69+
var endIndex: Index { Index() }
70+
71+
func index(after i: Index) -> Index {
72+
return Index()
73+
}
74+
75+
func index(before i: Index) -> Index {
76+
return Index()
77+
}
78+
79+
subscript(position: Index) -> FilePath.Component {
80+
return FilePath.Component("")!
81+
}
82+
}
83+
84+
extension String {
85+
init(decoding path: FilePath) { self.init() }
86+
init?(validating path: FilePath) { self.init() }
87+
init(platformString: UnsafePointer<CInterop.PlatformChar>) { self.init() }
88+
init?(validatingPlatformString platformStrinbg: UnsafePointer<CInterop.PlatformChar>) { self.init() }
89+
}
90+
91+
// --- tests ---
92+
93+
func sourceString() -> String { return "" }
94+
func sourceCCharArray() -> [CChar] { return [] }
95+
func sourceCString() -> UnsafePointer<CChar> { return (nil as UnsafePointer<CChar>?)! }
96+
func sourceDecoder() -> Decoder { return (nil as Decoder?)! }
97+
98+
func sink(filePath: FilePath) { }
99+
func sink(string: String) { }
100+
func sink(component: FilePath.Component) { }
101+
func sink(root: FilePath.Root) { }
102+
func sink(componentView: FilePath.ComponentView) { }
103+
func sink(encoder: Encoder) { }
104+
func sink<T>(ptr: UnsafePointer<T>) { }
105+
106+
func test_files(e1: Encoder) {
107+
// --- FilePath.Root, FilePath.Component ---
108+
109+
sink(string: FilePath.Root("/")!.string)
110+
sink(string: FilePath.Root(sourceString())!.string) // $ MISSING: tainted=
111+
sink(string: FilePath.Component("path")!.string)
112+
sink(string: FilePath.Component(sourceString())!.string) // $ MISSING: tainted=
113+
114+
// --- FilePath constructors ---
115+
116+
let cleanUrl = URL(string: "https://example.com")!
117+
let taintedUrl = URL(string: sourceString())!
118+
119+
sink(filePath: FilePath("my/path"))
120+
sink(filePath: FilePath(sourceString())) // $ MISSING: tainted=
121+
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=
126+
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=
128+
sink(filePath: FilePath(root: FilePath.Root("/"), [FilePath.Component("my")!, FilePath.Component(sourceString())!])) // $ MISSING: tainted=
129+
130+
// --- FilePath methods ---
131+
132+
let clean = FilePath("")
133+
let tainted = FilePath(sourceString())
134+
135+
sink(filePath: clean)
136+
sink(filePath: tainted) // $ MISSING: tainted=
137+
138+
sink(encoder: e1)
139+
try! clean.encode(to: e1)
140+
sink(encoder: e1)
141+
try! tainted.encode(to: e1)
142+
sink(encoder: e1) // $ MISSING: tainted=
143+
144+
sink(string: String(decoding: tainted)) // $ MISSING: tainted=
145+
sink(string: String(validating: tainted)!) // $ MISSING: tainted=
146+
147+
sink(filePath: clean.lexicallyResolving(clean)!)
148+
sink(filePath: tainted.lexicallyResolving(clean)!) // $ MISSING: tainted=
149+
sink(filePath: clean.lexicallyResolving(tainted)!) // $ MISSING: tainted=
150+
151+
let _ = clean.withCString({
152+
ptr in
153+
sink(ptr: ptr)
154+
})
155+
let _ = tainted.withCString({
156+
ptr in
157+
sink(ptr: ptr) // $ MISSING: tainted=
158+
})
159+
160+
let _ = clean.withPlatformString({
161+
ptr in
162+
sink(ptr: ptr)
163+
sink(string: String(platformString: ptr))
164+
sink(string: String(validatingPlatformString: ptr)!)
165+
})
166+
let _ = tainted.withPlatformString({
167+
ptr in
168+
sink(ptr: ptr) // $ MISSING: tainted=
169+
sink(string: String(platformString: ptr)) // $ MISSING: tainted=
170+
sink(string: String(validatingPlatformString: ptr)!) // $ MISSING: tainted=
171+
})
172+
173+
var fp1 = FilePath("")
174+
sink(filePath: fp1)
175+
fp1.append(sourceString())
176+
sink(filePath: fp1) // $ MISSING: tainted=
177+
fp1.append("")
178+
sink(filePath: fp1) // $ MISSING: tainted=
179+
180+
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=
184+
185+
// --- FilePath member variables ---
186+
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=
192+
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=
197+
198+
let taintedComponents = tainted.components
199+
sink(componentView: taintedComponents) // $ MISSING: tainted=
200+
sink(string: taintedComponents[taintedComponents.startIndex].string) // $ MISSING: tainted=
201+
}

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

Lines changed: 23 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ typealias unichar = UInt16
66
struct Locale {
77
}
88

9-
struct FilePath {
10-
init(_ string: String) {}
119

12-
var `extension`: String? { get { "" } set {} }
13-
var stem: String? { get { "" } }
14-
var string: String { get { "" } }
15-
var description: String { get { "" } }
16-
var debugDescription: String { get { "" } }
1710

18-
mutating func append(_ other: String) {}
19-
func appending(_ other: String) -> FilePath { return FilePath("") }
2011

21-
func withCString<Result>(_ body: (UnsafePointer<CChar>) throws -> Result) rethrows -> Result {
22-
return 0 as! Result
23-
}
24-
func withPlatformString<Result>(_ body: (UnsafePointer<CInterop.PlatformChar>) throws -> Result) rethrows -> Result {
25-
return 0 as! Result
26-
}
27-
}
12+
13+
14+
15+
16+
17+
18+
19+
20+
21+
22+
23+
24+
25+
26+
27+
2828

2929
enum CInterop {
3030
typealias Char = CChar
@@ -59,7 +59,7 @@ extension String : CVarArg {
5959

6060
init?(data: Data, encoding: Encoding) { self.init() }
6161

62-
init(decoding path: FilePath) { self.init() }
62+
6363

6464
init(format: String, _ arguments: CVarArg...) { self.init() }
6565
init(format: String, arguments: [CVarArg]) { self.init() }
@@ -78,10 +78,10 @@ extension String : CVarArg {
7878
init(utf16CodeUnitsNoCopy: UnsafePointer<unichar>, count: Int, freeWhenDone flag: Bool) { self.init() }
7979

8080
init(platformString: UnsafePointer<CInterop.PlatformChar>) { self.init() }
81-
init?(validatingPlatformString platformStrinbg: UnsafePointer<CInterop.PlatformChar>) { self.init() }
81+
8282
func withPlatformString<Result>(_ body: (UnsafePointer<CInterop.PlatformChar>) throws -> Result) rethrows -> Result { return 0 as! Result }
8383

84-
init?(validating path: FilePath) { self.init() }
84+
8585

8686
mutating func replaceSubrange<C>(_ subrange: Range<String.Index>, with newElements: C)
8787
where C : Collection, C.Element == Character {}
@@ -580,67 +580,16 @@ func taintThroughSubstring() {
580580
sink(arg: String(sub6)) // $ tainted=554
581581
}
582582

583-
func taintedThroughFilePath() {
584-
let clean = FilePath("")
585-
let tainted = FilePath(source2())
586-
587-
sink(arg: clean)
588-
sink(arg: tainted) // $ MISSING: tainted=585
589-
590-
sink(arg: tainted.extension!) // $ MISSING: tainted=585
591-
sink(arg: tainted.stem!) // $ MISSING: tainted=585
592-
sink(arg: tainted.string) // $ MISSING: tainted=585
593-
sink(arg: tainted.description) // $ MISSING: tainted=585
594-
sink(arg: tainted.debugDescription) // $ MISSING: tainted=585
595-
596-
sink(arg: String(decoding: tainted)) // $ MISSING: tainted=585
597-
sink(arg: String(validating: tainted)!) // $ MISSING: tainted=585
598-
599-
let _ = clean.withCString({
600-
ptr in
601-
sink(arg: ptr)
602-
})
603-
let _ = tainted.withCString({
604-
ptr in
605-
sink(arg: ptr) // $ MISSING: tainted=585
606-
})
607-
608-
let _ = clean.withPlatformString({
609-
ptr in
610-
sink(arg: ptr)
611-
sink(arg: String(platformString: ptr))
612-
sink(arg: String(validatingPlatformString: ptr)!)
613-
})
614-
let _ = tainted.withPlatformString({
615-
ptr in
616-
sink(arg: ptr) // $ MISSING: tainted=585
617-
sink(arg: String(platformString: ptr)) // $ MISSING: tainted=585
618-
sink(arg: String(validatingPlatformString: ptr)!) // $ MISSING: tainted=585
619-
})
620-
621-
var fp1 = FilePath("")
622-
sink(arg: fp1)
623-
fp1.append(source2())
624-
sink(arg: fp1) // $ MISSING: tainted=623
625-
fp1.append("")
626-
sink(arg: fp1) // $ MISSING: tainted=623
627-
628-
sink(arg: clean.appending(""))
629-
sink(arg: clean.appending(source2())) // $ MISSING: tainted=629
630-
sink(arg: tainted.appending("")) // $ MISSING: tainted=585
631-
sink(arg: tainted.appending(source2())) // $ MISSING: tainted=585,631
632-
}
633-
634583
func taintedThroughConversion() {
635584
sink(arg: String(0))
636-
sink(arg: String(source())) // $ tainted=636
585+
sink(arg: String(source())) // $ tainted=585
637586
sink(arg: Int(0).description)
638-
sink(arg: source().description) // $ MISSING: tainted=638
587+
sink(arg: source().description) // $ MISSING: tainted=587
639588
sink(arg: String(describing: 0))
640-
sink(arg: String(describing: source())) // $ tainted=640
589+
sink(arg: String(describing: source())) // $ tainted=589
641590

642591
sink(arg: Int("123")!)
643-
sink(arg: Int(source2())!) // $ MISSING: tainted=643
592+
sink(arg: Int(source2())!) // $ MISSING: tainted=592
644593
}
645594

646595
func untaintedFields() {

0 commit comments

Comments
 (0)