Swift: Consolidate and extend tests of taint flow through FilePath.

This commit is contained in:
Geoffrey White
2023-05-18 14:25:04 +01:00
parent e402c225f4
commit 371bcc55fa
2 changed files with 224 additions and 74 deletions

View File

@@ -0,0 +1,201 @@
// --- stubs ---
struct URL {
init?(string: String) {}
}
enum CInterop {
typealias Char = CChar
typealias PlatformChar = CInterop.Char
}
struct FilePath {
struct Component {
init?(_ string: String) { }
var string: String { get { return "" } }
}
struct Root {
init?(_ string: String) { }
var string: String { get { return "" } }
}
struct ComponentView {
}
init(_ string: String) { }
init?(_ url: URL) { }
init(cString: [CChar]) { }
init(cString: UnsafePointer<CChar>) { }
init(from decoder: Decoder) { }
init<C>(root: FilePath.Root?, _ components: C) where C : Collection, C.Element == FilePath.Component { }
func encode(to encoder: Encoder) throws { }
mutating func append(_ other: String) { }
func appending(_ other: String) -> FilePath { return FilePath("") }
func lexicallyResolving(_ subpath: FilePath) -> FilePath? { return nil }
func withCString<Result>(_ body: (UnsafePointer<CChar>) throws -> Result) rethrows -> Result {
return 0 as! Result
}
func withPlatformString<Result>(_ body: (UnsafePointer<CInterop.PlatformChar>) throws -> Result) rethrows -> Result {
return 0 as! Result
}
var description: String { get { return "" } }
var debugDescription: String { get { return "" } }
var `extension`: String? { get { return "" } set { } }
var stem: String? { get { return "" } }
var string: String { get { return "" } }
var components: FilePath.ComponentView { get { return FilePath.ComponentView() } set { } }
var lastComponent: FilePath.Component? { get { return nil} }
var root: FilePath.Root? { get { return nil } set { } }
}
extension FilePath.ComponentView: BidirectionalCollection {
typealias Element = FilePath.Component
struct Index: Comparable {
static func < (lhs: Self, rhs: Self) -> Bool {
return false
}
}
var startIndex: Index { Index() }
var endIndex: Index { Index() }
func index(after i: Index) -> Index {
return Index()
}
func index(before i: Index) -> Index {
return Index()
}
subscript(position: Index) -> FilePath.Component {
return FilePath.Component("")!
}
}
extension String {
init(decoding path: FilePath) { self.init() }
init?(validating path: FilePath) { self.init() }
init(platformString: UnsafePointer<CInterop.PlatformChar>) { self.init() }
init?(validatingPlatformString platformStrinbg: UnsafePointer<CInterop.PlatformChar>) { self.init() }
}
// --- tests ---
func sourceString() -> String { return "" }
func sourceCCharArray() -> [CChar] { return [] }
func sourceCString() -> UnsafePointer<CChar> { return (nil as UnsafePointer<CChar>?)! }
func sourceDecoder() -> Decoder { return (nil as Decoder?)! }
func sink(filePath: FilePath) { }
func sink(string: String) { }
func sink(component: FilePath.Component) { }
func sink(root: FilePath.Root) { }
func sink(componentView: FilePath.ComponentView) { }
func sink(encoder: Encoder) { }
func sink<T>(ptr: UnsafePointer<T>) { }
func test_files(e1: Encoder) {
// --- FilePath.Root, FilePath.Component ---
sink(string: FilePath.Root("/")!.string)
sink(string: FilePath.Root(sourceString())!.string) // $ MISSING: tainted=
sink(string: FilePath.Component("path")!.string)
sink(string: FilePath.Component(sourceString())!.string) // $ MISSING: tainted=
// --- FilePath constructors ---
let cleanUrl = URL(string: "https://example.com")!
let taintedUrl = URL(string: sourceString())!
sink(filePath: FilePath("my/path"))
sink(filePath: FilePath(sourceString())) // $ MISSING: tainted=
sink(filePath: FilePath(cleanUrl)!)
sink(filePath: FilePath(taintedUrl)!) // $ MISSING: tainted=
sink(filePath: FilePath(from: sourceDecoder())) // $ MISSING: tainted=
sink(filePath: FilePath(cString: sourceCCharArray())) // $ MISSING: tainted=
sink(filePath: FilePath(cString: sourceCString())) // $ MISSING: tainted=
sink(filePath: FilePath(root: FilePath.Root("/"), [FilePath.Component("my")!, FilePath.Component("path")!]))
sink(filePath: FilePath(root: FilePath.Root(sourceString()), [FilePath.Component("my")!, FilePath.Component("path")!])) // $ MISSING: tainted=
sink(filePath: FilePath(root: FilePath.Root("/"), [FilePath.Component("my")!, FilePath.Component(sourceString())!])) // $ MISSING: tainted=
// --- FilePath methods ---
let clean = FilePath("")
let tainted = FilePath(sourceString())
sink(filePath: clean)
sink(filePath: tainted) // $ MISSING: tainted=
sink(encoder: e1)
try! clean.encode(to: e1)
sink(encoder: e1)
try! tainted.encode(to: e1)
sink(encoder: e1) // $ MISSING: tainted=
sink(string: String(decoding: tainted)) // $ MISSING: tainted=
sink(string: String(validating: tainted)!) // $ MISSING: tainted=
sink(filePath: clean.lexicallyResolving(clean)!)
sink(filePath: tainted.lexicallyResolving(clean)!) // $ MISSING: tainted=
sink(filePath: clean.lexicallyResolving(tainted)!) // $ MISSING: tainted=
let _ = clean.withCString({
ptr in
sink(ptr: ptr)
})
let _ = tainted.withCString({
ptr in
sink(ptr: ptr) // $ MISSING: tainted=
})
let _ = clean.withPlatformString({
ptr in
sink(ptr: ptr)
sink(string: String(platformString: ptr))
sink(string: String(validatingPlatformString: ptr)!)
})
let _ = tainted.withPlatformString({
ptr in
sink(ptr: ptr) // $ MISSING: tainted=
sink(string: String(platformString: ptr)) // $ MISSING: tainted=
sink(string: String(validatingPlatformString: ptr)!) // $ MISSING: tainted=
})
var fp1 = FilePath("")
sink(filePath: fp1)
fp1.append(sourceString())
sink(filePath: fp1) // $ MISSING: tainted=
fp1.append("")
sink(filePath: fp1) // $ MISSING: tainted=
sink(filePath: clean.appending(""))
sink(filePath: clean.appending(sourceString())) // $ MISSING: tainted=
sink(filePath: tainted.appending("")) // $ MISSING: tainted=
sink(filePath: tainted.appending(sourceString())) // $ MISSING: tainted=
// --- FilePath member variables ---
sink(string: tainted.description) // $ MISSING: tainted=
sink(string: tainted.debugDescription) // $ MISSING: tainted=
sink(string: tainted.extension!) // $ MISSING: tainted=
sink(string: tainted.stem!) // $ MISSING: tainted=
sink(string: tainted.string) // $ MISSING: tainted=
sink(component: tainted.lastComponent!) // $ MISSING: tainted=
sink(string: tainted.lastComponent!.string) // $ MISSING: tainted=
sink(root: tainted.root!) // $ MISSING: tainted=
sink(string: tainted.root!.string) // $ MISSING: tainted=
let taintedComponents = tainted.components
sink(componentView: taintedComponents) // $ MISSING: tainted=
sink(string: taintedComponents[taintedComponents.startIndex].string) // $ MISSING: tainted=
}

View File

@@ -6,25 +6,25 @@ typealias unichar = UInt16
struct Locale {
}
struct FilePath {
init(_ string: String) {}
var `extension`: String? { get { "" } set {} }
var stem: String? { get { "" } }
var string: String { get { "" } }
var description: String { get { "" } }
var debugDescription: String { get { "" } }
mutating func append(_ other: String) {}
func appending(_ other: String) -> FilePath { return FilePath("") }
func withCString<Result>(_ body: (UnsafePointer<CChar>) throws -> Result) rethrows -> Result {
return 0 as! Result
}
func withPlatformString<Result>(_ body: (UnsafePointer<CInterop.PlatformChar>) throws -> Result) rethrows -> Result {
return 0 as! Result
}
}
enum CInterop {
typealias Char = CChar
@@ -59,7 +59,7 @@ extension String : CVarArg {
init?(data: Data, encoding: Encoding) { self.init() }
init(decoding path: FilePath) { self.init() }
init(format: String, _ arguments: CVarArg...) { self.init() }
init(format: String, arguments: [CVarArg]) { self.init() }
@@ -78,10 +78,10 @@ extension String : CVarArg {
init(utf16CodeUnitsNoCopy: UnsafePointer<unichar>, count: Int, freeWhenDone flag: Bool) { self.init() }
init(platformString: UnsafePointer<CInterop.PlatformChar>) { self.init() }
init?(validatingPlatformString platformStrinbg: UnsafePointer<CInterop.PlatformChar>) { self.init() }
func withPlatformString<Result>(_ body: (UnsafePointer<CInterop.PlatformChar>) throws -> Result) rethrows -> Result { return 0 as! Result }
init?(validating path: FilePath) { self.init() }
mutating func replaceSubrange<C>(_ subrange: Range<String.Index>, with newElements: C)
where C : Collection, C.Element == Character {}
@@ -580,67 +580,16 @@ func taintThroughSubstring() {
sink(arg: String(sub6)) // $ tainted=554
}
func taintedThroughFilePath() {
let clean = FilePath("")
let tainted = FilePath(source2())
sink(arg: clean)
sink(arg: tainted) // $ MISSING: tainted=585
sink(arg: tainted.extension!) // $ MISSING: tainted=585
sink(arg: tainted.stem!) // $ MISSING: tainted=585
sink(arg: tainted.string) // $ MISSING: tainted=585
sink(arg: tainted.description) // $ MISSING: tainted=585
sink(arg: tainted.debugDescription) // $ MISSING: tainted=585
sink(arg: String(decoding: tainted)) // $ MISSING: tainted=585
sink(arg: String(validating: tainted)!) // $ MISSING: tainted=585
let _ = clean.withCString({
ptr in
sink(arg: ptr)
})
let _ = tainted.withCString({
ptr in
sink(arg: ptr) // $ MISSING: tainted=585
})
let _ = clean.withPlatformString({
ptr in
sink(arg: ptr)
sink(arg: String(platformString: ptr))
sink(arg: String(validatingPlatformString: ptr)!)
})
let _ = tainted.withPlatformString({
ptr in
sink(arg: ptr) // $ MISSING: tainted=585
sink(arg: String(platformString: ptr)) // $ MISSING: tainted=585
sink(arg: String(validatingPlatformString: ptr)!) // $ MISSING: tainted=585
})
var fp1 = FilePath("")
sink(arg: fp1)
fp1.append(source2())
sink(arg: fp1) // $ MISSING: tainted=623
fp1.append("")
sink(arg: fp1) // $ MISSING: tainted=623
sink(arg: clean.appending(""))
sink(arg: clean.appending(source2())) // $ MISSING: tainted=629
sink(arg: tainted.appending("")) // $ MISSING: tainted=585
sink(arg: tainted.appending(source2())) // $ MISSING: tainted=585,631
}
func taintedThroughConversion() {
sink(arg: String(0))
sink(arg: String(source())) // $ tainted=636
sink(arg: String(source())) // $ tainted=585
sink(arg: Int(0).description)
sink(arg: source().description) // $ MISSING: tainted=638
sink(arg: source().description) // $ MISSING: tainted=587
sink(arg: String(describing: 0))
sink(arg: String(describing: source())) // $ tainted=640
sink(arg: String(describing: source())) // $ tainted=589
sink(arg: Int("123")!)
sink(arg: Int(source2())!) // $ MISSING: tainted=643
sink(arg: Int(source2())!) // $ MISSING: tainted=592
}
func untaintedFields() {