Merge pull request #13698 from geoffw0/url2

Swift: Expand taint models for URL
This commit is contained in:
Geoffrey White
2023-07-11 13:14:20 +01:00
committed by GitHub
4 changed files with 457 additions and 53 deletions

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Added detail to the taint model for `URL`.

View File

@@ -35,6 +35,36 @@ private class UrlRequestFieldsInheritTaint extends TaintInheritingContent,
}
}
/**
* A content implying that, if a `URLResource` is tainted, then its fields `name`
* and `subdirectory` are tainted.
*/
private class UrlResourceFieldsInheritTaint extends TaintInheritingContent,
DataFlow::Content::FieldContent
{
UrlResourceFieldsInheritTaint() {
this.getField().getEnclosingDecl().asNominalTypeDecl().getName() = "URLResource" and
this.getField().getName() = ["name", "subdirectory"]
}
}
/**
* A content implying that, if a `URLResourceValues` is tainted, then certain
* fields are tainted.
*/
private class UrlResourceValuesFieldsInheritTaint extends TaintInheritingContent,
DataFlow::Content::FieldContent
{
UrlResourceValuesFieldsInheritTaint() {
this.getField().getEnclosingDecl().asNominalTypeDecl().getName() = "URLResourceValues" and
this.getField().getName() =
[
"name", "path", "canonicalPath", "localizedLabel", "localizedName", "parentDirectory",
"thumbnail"
]
}
}
/**
* A model for `URL` members that are sources of remote flow.
*/
@@ -49,14 +79,74 @@ private class UrlRemoteFlowSource extends SourceModelCsv {
}
/**
* A model for `URL` members that permit taint flow.
* A model for `URL` and related class members that permit taint flow.
*/
private class UrlSummaries extends SummaryModelCsv {
override predicate row(string row) {
row =
[
";URL;true;init(string:);(String);;Argument[0];ReturnValue;taint",
";URL;true;init(string:relativeTo:);(String,URL?);;Argument[0,1];ReturnValue;taint"
";URL;true;init(string:relativeTo:);(String,URL?);;Argument[0..1];ReturnValue;taint",
";URL;true;init(fileURLWithPath:);;;Argument[0];ReturnValue;taint",
";URL;true;init(fileURLWithPath:isDirectory:);;;Argument[0];ReturnValue;taint",
";URL;true;init(fileURLWithPath:relativeTo:);;;Argument[0..1];ReturnValue;taint",
";URL;true;init(fileURLWithPath:isDirectory:relativeTo:);;;Argument[0];ReturnValue;taint",
";URL;true;init(fileURLWithPath:isDirectory:relativeTo:);;;Argument[2];ReturnValue;taint",
";URL;true;init(fileURLWithFileSystemRepresentation:isDirectory:relativeTo:);;;Argument[0];ReturnValue;taint",
";URL;true;init(fileURLWithFileSystemRepresentation:isDirectory:relativeTo:);;;Argument[2];ReturnValue;taint",
";URL;true;init(fileReferenceLiteralResourceName:);;;Argument[0];ReturnValue;taint",
";URL;true;init(_:);;;Argument[0];ReturnValue;taint",
";URL;true;init(_:isDirectory:);;;Argument[0];ReturnValue;taint",
";URL;true;init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:);;;Argument[0];ReturnValue;taint",
";URL;true;init(resolvingBookmarkData:options:relativeTo:bookmarkDataIsStale:);;;Argument[2];ReturnValue;taint",
";URL;true;init(resolvingAliasFileAt:options:);;;Argument[0];ReturnValue;taint",
";URL;true;init(resource:);;;Argument[0];ReturnValue;taint",
";URL;true;init(dataRepresentation:relativeTo:isAbsolute:);;;Argument[0..1];ReturnValue;taint",
";URL;true;init(_:strategy:);;;Argument[0];ReturnValue;taint",
";URL;true;init(filePath:directoryHint:);;;Argument[0];ReturnValue;taint",
";URL;true;init(filePath:directoryHint:relativeTo:);;;Argument[0];ReturnValue;taint",
";URL;true;init(filePath:directoryHint:relativeTo:);;;Argument[2];ReturnValue;taint",
";URL;true;init(for:in:appropriateFor:create:);;;Argument[0..2];ReturnValue;taint",
";URL;true;init(string:encodingInvalidCharacters:);;;Argument[0];ReturnValue;taint",
";URL;true;resourceValues(forKeys:);;;Argument[-1];ReturnValue;taint",
";URL;true;setResourceValues(_:);;;Argument[0];Argument[-1];taint",
";URL;true;setTemporaryResourceValue(_:forKey:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;withUnsafeFileSystemRepresentation(_:);;;Argument[-1],Argument[0].Parameter[0];ReturnValue;taint",
";URL;true;withUnsafeFileSystemRepresentation(_:);;;Argument[0].ReturnValue;ReturnValue;taint",
";URL;true;resolvingSymlinksInPath();;;Argument[-1];ReturnValue;taint",
";URL;true;appendPathComponent(_:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;appendPathComponent(_:isDirectory:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;appendPathComponent(_:conformingTo:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;appendingPathComponent(_:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;appendingPathComponent(_:isDirectory:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;appendingPathComponent(_:conformingTo:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;appendPathExtension(_:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;appendingPathExtension(_:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;deletingLastPathComponent();;;Argument[-1];ReturnValue;taint",
";URL;true;deletingPathExtension();;;Argument[-1];ReturnValue;taint",
";URL;true;bookmarkData(options:includingResourceValuesForKeys:relativeTo:);;;Argument[-1];ReturnValue;taint",
";URL;true;bookmarkData(options:includingResourceValuesForKeys:relativeTo:);;;Argument[1..2];ReturnValue;taint",
";URL;true;bookmarkData(withContentsOf:);;;Argument[0];ReturnValue;taint",
";URL;true;resourceValues(forKeys:fromBookmarkData:);;;Argument[1];ReturnValue;taint",
";URL;true;promisedItemResourceValues(forKeys:);;;Argument[-1];ReturnValue;taint",
";URL;true;append(component:directoryHint:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;append(components:directoryHint:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;append(path:directoryHint:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;append(queryItems:);;;Argument[-1..0];Argument[-1];taint",
";URL;true;appending(component:directoryHint:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;appending(components:directoryHint:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;appending(path:directoryHint:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;appending(queryItems:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;formatted();;;Argument[-1];ReturnValue;taint",
";URL;true;formatted(_:);;;Argument[-1..0];ReturnValue;taint",
";URL;true;fragment(percentEncoded:);;;Argument[-1];ReturnValue;taint",
";URL;true;host(percentEncoded:);;;Argument[-1];ReturnValue;taint",
";URL;true;password(percentEncoded:);;;Argument[-1];ReturnValue;taint",
";URL;true;path(percentEncoded:);;;Argument[-1];ReturnValue;taint",
";URL;true;query(percentEncoded:);;;Argument[-1];ReturnValue;taint",
";URL;true;user(percentEncoded:);;;Argument[-1];ReturnValue;taint",
";URL;true;homeDirectory(forUser:);;;Argument[0];ReturnValue;taint",
";URLResource;true;init(name:subdirectory:locale:bundle:);;;Argument[0..1];ReturnValue;taint",
]
}
}

View File

@@ -0,0 +1,2 @@
deadEnd
| url.swift:493:2:493:28 | call to sink(any:) |

View File

@@ -5,8 +5,53 @@ class NSObject
struct URL
{
typealias BookmarkResolutionOptions = NSURL.BookmarkResolutionOptions
struct URLResourceKey : Hashable {
init(_ rawValue: String) { }
init(rawValue: String) { }
//
}
struct URLResourceValues {
var name: String?
var path: String?
var canonicalPath: String?
var localizedLabel: String?
var localizedName: String?
var parentDirectory: URL?
//
}
struct AsyncBytes {
//
}
enum DirectoryHint {
case inferFromPath
}
init?(string: String) {}
init?(string: String, relativeTo: URL?) {}
init(fileURLWithPath path: String) { }
init(fileURLWithPath path: String, isDirectory: Bool) { }
init(fileURLWithPath path: String, relativeTo base: URL?) { }
init(fileURLWithPath path: String, isDirectory: Bool, relativeTo base: URL?) { }
init(fileURLWithFileSystemRepresentation path: UnsafePointer<Int8>, isDirectory: Bool, relativeTo baseURL: URL?) { }
init(fileReferenceLiteralResourceName name: String) { }
init?(_ path: FilePath) { }
init?(_ path: FilePath, isDirectory: Bool) { }
init(resolvingBookmarkData data: Data, options: URL.BookmarkResolutionOptions = [], relativeTo url: URL? = nil, bookmarkDataIsStale: inout Bool) throws { }
init(resolvingAliasFileAt url: URL, options: URL.BookmarkResolutionOptions = []) throws { }
init?(resource: URLResource) { }
init?(dataRepresentation: Data, relativeTo url: URL?, isAbsolute: Bool = false) { }
init?(filePath path: FilePath, directoryHint: URL.DirectoryHint = .inferFromPath) { }
init(filePath path: String, directoryHint: URL.DirectoryHint = .inferFromPath, relativeTo base: URL? = nil) { }
var dataRepresentation: Data { get { return Data(0) } }
var absoluteString: String { get { return "" } }
var absoluteURL: URL { get {return URL(string: "")!} }
var baseURL: URL { get {return URL(string: "")!} }
var fragment: String? { get {return nil} }
@@ -24,6 +69,54 @@ struct URL
var standardizedFileURL: URL { get {return URL(string: "")!} }
var user: String? { get {return nil} }
var password: String? { get {return nil} }
var resourceBytes: URL.AsyncBytes { get { return (nil as AsyncBytes?)! } }
var lines: AsyncLineSequence<URL.AsyncBytes> { get { return (nil as AsyncLineSequence<URL.AsyncBytes>?)! } }
func resourceValues(forKeys keys: Set<URLResourceKey>) throws -> URLResourceValues { return URLResourceValues() }
static func resourceValues(forKeys keys: Set<URLResourceKey>, fromBookmarkData data: Data) -> URLResourceValues? { return nil }
mutating func setResourceValues(_ values: URLResourceValues) throws { }
mutating func setTemporaryResourceValue(_ value: Any, forKey key: URLResourceKey) { }
func withUnsafeFileSystemRepresentation<ResultType>(_ block: (UnsafePointer<Int8>?) throws -> ResultType) rethrows -> ResultType { return (nil as ResultType?)! }
func resolvingSymlinksInPath() -> URL { return URL(string: "")! }
mutating func appendPathComponent(_ pathComponent: String) { }
mutating func appendPathComponent(_ pathComponent: String, isDirectory: Bool) { }
func appendingPathComponent(_ pathComponent: String) -> URL { return URL(string: "")! }
func appendingPathComponent(_ pathComponent: String, isDirectory: Bool) -> URL { return URL(string: "")! }
mutating func appendPathExtension(_ pathExtension: String) { }
func appendingPathExtension(_ pathExtension: String) -> URL { return URL(string: "")! }
func deletingLastPathComponent() -> URL { return URL(string: "")! }
func deletingPathExtension() -> URL { return URL(string: "")! }
mutating func append<S>(component: S, directoryHint: URL.DirectoryHint = .inferFromPath) where S: StringProtocol { }
mutating func append<S>(components: S..., directoryHint: URL.DirectoryHint = .inferFromPath) where S: StringProtocol { }
mutating func append<S>(path: S, directoryHint: URL.DirectoryHint = .inferFromPath) where S: StringProtocol { }
mutating func append(queryItems: [URLQueryItem]) { }
func appending<S>(component: S, directoryHint: URL.DirectoryHint = .inferFromPath) -> URL where S: StringProtocol { return URL(string: "")! }
func appending<S>(components: S..., directoryHint: URL.DirectoryHint = .inferFromPath) -> URL where S: StringProtocol { return URL(string: "")! }
func appending<S>(path: S, directoryHint: URL.DirectoryHint = .inferFromPath) -> URL where S: StringProtocol { return URL(string: "")! }
func appending(queryItems: [URLQueryItem]) -> URL { return URL(string: "")! }
func promisedItemResourceValues(forKeys keys: Set<URLResourceKey>) throws -> URLResourceValues { return (nil as URLResourceValues?)! }
func formatted() -> String { return "" }
func fragment(percentEncoded: Bool = true) -> String? { return nil }
func host(percentEncoded: Bool = true) -> String? { return nil }
func password(percentEncoded: Bool = true) -> String? { return nil }
func path(percentEncoded: Bool = true) -> String { return "" }
func query(percentEncoded: Bool = true) -> String? { return nil }
func user(percentEncoded: Bool = true) -> String? { return nil }
// simplified:
func bookmarkData(options: Int = 0, includingResourceValuesForKeys keys: Set<URLResourceKey>? = nil, relativeTo url: URL? = nil) throws -> Data { return Data(0) }
static func bookmarkData(withContentsOf url: URL) throws -> Data { return Data(0) }
static var homeDirectory: URL { get { return URL(string: "")! } }
static func homeDirectory(forUser user: String) -> URL? { return nil }
}
class NSURL {
struct BookmarkResolutionOptions : OptionSet {
let rawValue: Int
}
init?(string: String) {}
}
class Data
@@ -31,12 +124,36 @@ class Data
init<S>(_ elements: S) {}
}
struct FilePath {
init(_ string: String) {}
}
struct AsyncLineSequence<Base> : AsyncSequence {
typealias Element = String
struct AsyncIterator : AsyncIteratorProtocol {
typealias Element = String
mutating func next() async -> String? { return nil }
}
func makeAsyncIterator() -> AsyncIterator { return AsyncIterator() }
}
class InputStream {}
struct Mirror {}
typealias TimeInterval = Double
struct URLResource {
// simplified:
init(name: String, subdirectory: String? = nil, locale: Int = 0, bundle: Int = 0) {
self.name = name
self.subdirectory = subdirectory
}
let name: String
let subdirectory: String?
}
struct URLRequest {
enum CachePolicy { case none }
enum NetworkServiceType { case none }
@@ -64,7 +181,9 @@ struct URLRequest {
var requiresDNSSECValidation: Bool = false
}
class URLResponse : NSObject {}
struct URLQueryItem { }
class URLResponse : NSObject { }
class URLSessionTask : NSObject { }
@@ -93,54 +212,56 @@ func taintThroughURL() {
let urlTainted = URL(string: tainted)!
sink(arg: urlClean)
sink(arg: urlTainted) // $ tainted=91
sink(arg: urlTainted) // $ tainted=210
// Fields
sink(arg: urlTainted.absoluteURL) // $ tainted=91
sink(arg: urlTainted.baseURL) // $ SPURIOUS: $ tainted=91
sink(string: urlTainted.fragment!) // $ tainted=91
sink(string: urlTainted.host!) // $ tainted=91
sink(string: urlTainted.lastPathComponent) // $ tainted=91
sink(string: urlTainted.path) // $ tainted=91
sink(string: urlTainted.pathComponents[0]) // $ tainted=91
sink(string: urlTainted.pathExtension) // $ tainted=91
sink(int: urlTainted.port!) // $ tainted=91
sink(string: urlTainted.query!) // $ tainted=91
sink(string: urlTainted.relativePath) // $ tainted=91
sink(string: urlTainted.relativeString) // $ tainted=91
sink(string: urlTainted.scheme!) // $ tainted=91
sink(arg: urlTainted.standardized) // $ tainted=91
sink(arg: urlTainted.standardizedFileURL) // $ tainted=91
sink(string: urlTainted.user!) // $ tainted=91
sink(string: urlTainted.password!) // $ tainted=91
sink(data: urlTainted.dataRepresentation) // $ tainted=210
sink(string: urlTainted.absoluteString) // $ tainted=210
sink(arg: urlTainted.absoluteURL) // $ tainted=210
sink(arg: urlTainted.baseURL) // $ tainted=210
sink(string: urlTainted.fragment!) // $ tainted=210
sink(string: urlTainted.host!) // $ tainted=210
sink(string: urlTainted.lastPathComponent) // $ tainted=210
sink(string: urlTainted.path) // $ tainted=210
sink(string: urlTainted.pathComponents[0]) // $ tainted=210
sink(string: urlTainted.pathExtension) // $ tainted=210
sink(int: urlTainted.port!) // $ tainted=210
sink(string: urlTainted.query!) // $ tainted=210
sink(string: urlTainted.relativePath) // $ tainted=210
sink(string: urlTainted.relativeString) // $ tainted=210
sink(string: urlTainted.scheme!) // $ tainted=210
sink(arg: urlTainted.standardized) // $ tainted=210
sink(arg: urlTainted.standardizedFileURL) // $ tainted=210
sink(string: urlTainted.user!) // $ tainted=210
sink(string: urlTainted.password!) // $ tainted=210
sink(any: urlTainted.resourceBytes) // $ tainted=210
sink(arg: URL(string: clean, relativeTo: nil)!)
sink(arg: URL(string: tainted, relativeTo: nil)!) // $ tainted=91
sink(arg: URL(string: tainted, relativeTo: nil)!) // $ tainted=210
sink(arg: URL(string: clean, relativeTo: urlClean)!)
// Fields (assuming `clean` was a relative path instead of a full URL)
sink(arg: URL(string: clean, relativeTo: urlTainted)!.absoluteURL) // $ tainted=91
sink(arg: URL(string: clean, relativeTo: urlTainted)!.baseURL) // $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.fragment!) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.host!) // $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.lastPathComponent) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.path) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.pathComponents[0]) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.pathExtension) // $ SPURIOUS: $ tainted=91
sink(int: URL(string: clean, relativeTo: urlTainted)!.port!) // $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.query!) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.relativePath) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.relativeString) // $ SPURIOUS: $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.scheme!) // $ tainted=91
sink(arg: URL(string: clean, relativeTo: urlTainted)!.standardized) // $ tainted=91
sink(arg: URL(string: clean, relativeTo: urlTainted)!.standardizedFileURL) // $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.user!) // $ tainted=91
sink(string: URL(string: clean, relativeTo: urlTainted)!.password!) // $ tainted=91
sink(arg: URL(string: clean, relativeTo: urlTainted)!.absoluteURL) // $ tainted=210
sink(arg: URL(string: clean, relativeTo: urlTainted)!.baseURL) // $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.fragment!) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.host!) // $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.lastPathComponent) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.path) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.pathComponents[0]) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.pathExtension) // $ $ tainted=210
sink(int: URL(string: clean, relativeTo: urlTainted)!.port!) // $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.query!) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.relativePath) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.relativeString) // $ $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.scheme!) // $ tainted=210
sink(arg: URL(string: clean, relativeTo: urlTainted)!.standardized) // $ tainted=210
sink(arg: URL(string: clean, relativeTo: urlTainted)!.standardizedFileURL) // $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.user!) // $ tainted=210
sink(string: URL(string: clean, relativeTo: urlTainted)!.password!) // $ tainted=210
if let x = URL(string: clean) {
sink(arg: x)
}
if let y = URL(string: tainted) {
sink(arg: y) // $ tainted=91
sink(arg: y) // $ tainted=210
}
var urlClean2 : URL!
@@ -149,11 +270,160 @@ func taintThroughURL() {
var urlTainted2 : URL!
urlTainted2 = URL(string: tainted)
sink(arg: urlTainted2) // $ tainted=91
sink(arg: urlTainted2) // $ tainted=210
let task = URLSession.shared.dataTask(with: urlTainted) { (data, response, error) in
sink(data: data!) // $ tainted=91
let _ = URLSession.shared.dataTask(with: urlTainted) { (data, response, error) in
sink(data: data!) // $ tainted=210
}
sink(arg: URL(fileURLWithPath: tainted)) // $ tainted=210
sink(arg: URL(fileURLWithPath: tainted, isDirectory: false)) // $ tainted=210
sink(arg: URL(fileURLWithPath: tainted, relativeTo: urlClean)) // $ tainted=210
sink(arg: URL(fileURLWithPath: clean, relativeTo: urlTainted)) // $ tainted=210
sink(arg: URL(fileURLWithPath: tainted, isDirectory: false, relativeTo: urlClean)) // $ tainted=210
sink(arg: URL(fileURLWithPath: clean, isDirectory: false, relativeTo: urlTainted)) // $ tainted=210
sink(arg: URL(fileURLWithPath: tainted)) // $ tainted=210
let _ = clean.withCString({
ptrClean in
sink(arg: URL(fileURLWithFileSystemRepresentation: ptrClean, isDirectory: false, relativeTo: nil))
sink(arg: URL(fileURLWithFileSystemRepresentation: ptrClean, isDirectory: false, relativeTo: urlTainted)) // $ MISSING: tainted=210
});
sink(arg: URL(fileURLWithFileSystemRepresentation: 0 as! UnsafePointer<Int8>, isDirectory: false, relativeTo: urlTainted)) // $ tainted=210
let _ = tainted.withCString({
ptrTainted in
sink(arg: URL(fileURLWithFileSystemRepresentation: ptrTainted, isDirectory: false, relativeTo: nil)) // $ MISSING: tainted=210
})
sink(arg: URL(fileReferenceLiteralResourceName: tainted)) // $ tainted=210
sink(arg: URL(FilePath(tainted))!) // $ tainted=210
sink(arg: URL(FilePath(tainted), isDirectory: false)!) // $ tainted=210
if let values = try? urlTainted.resourceValues(forKeys: []) {
sink(any: values) // $ tainted=210
sink(string: values.name!) // $ tainted=210
sink(string: values.path!) // $ tainted=210
sink(string: values.canonicalPath!) // $ tainted=210
sink(string: values.localizedLabel!) // $ tainted=210
sink(string: values.localizedName!) // $ tainted=210
sink(any: values.parentDirectory!) // $ tainted=210
}
if let values = try? urlTainted.promisedItemResourceValues(forKeys: []) {
sink(any: values) // $ tainted=210
sink(string: values.name!) // $ tainted=210
sink(string: values.path!) // $ tainted=210
sink(string: values.canonicalPath!) // $ tainted=210
sink(string: values.localizedLabel!) // $ tainted=210
sink(string: values.localizedName!) // $ tainted=210
sink(any: values.parentDirectory!) // $ tainted=210
}
urlClean.withUnsafeFileSystemRepresentation({
ptr in
sink(any: ptr!)
})
urlTainted.withUnsafeFileSystemRepresentation({
ptr in
sink(any: ptr!) // $ MISSING: tainted=210
})
sink(arg: urlTainted.resolvingSymlinksInPath()) // $ tainted=210
sink(arg: urlTainted.appendingPathComponent(clean)) // $ tainted=210
sink(arg: urlClean.appendingPathComponent(tainted)) // $ tainted=210
sink(arg: urlTainted.appendingPathComponent(clean, isDirectory: false)) // $ tainted=210
sink(arg: urlClean.appendingPathComponent(tainted, isDirectory: false)) // $ tainted=210
sink(arg: urlTainted.appendingPathExtension(clean)) // $ tainted=210
sink(arg: urlClean.appendingPathExtension(tainted)) // $ tainted=210
sink(arg: urlTainted.deletingLastPathComponent()) // $ tainted=210
sink(arg: urlTainted.deletingPathExtension()) // $ tainted=210
sink(arg: urlTainted.appending(component: clean)) // $ tainted=210
sink(arg: urlClean.appending(component: tainted)) // $ tainted=210
sink(arg: urlTainted.appending(components: clean)) // $ tainted=210
sink(arg: urlClean.appending(components: tainted)) // $ MISSING: tainted=210
sink(arg: urlClean.appending(components: clean, tainted)) // $ MISSING: tainted=210
sink(arg: urlTainted.appending(path: clean)) // $ tainted=210
sink(arg: urlClean.appending(path: tainted)) // $ tainted=210
sink(arg: urlTainted.appending(queryItems: [])) // $ tainted=210
sink(arg: urlClean.appending(queryItems: [source() as! URLQueryItem])) // $ MISSING: tainted=210
sink(arg: URL(filePath: tainted)) // $ tainted=210
sink(arg: URL(filePath: tainted, relativeTo: nil)) // $ tainted=210
sink(arg: URL(filePath: clean, relativeTo: urlTainted)) // $ tainted=210
sink(arg: try! URL(resolvingAliasFileAt: urlTainted)) // $ tainted=210
sink(arg: URL(resource: URLResource(name: tainted))!) // $ tainted=210
sink(arg: URL(resource: URLResource(name: clean, subdirectory: tainted))!) // $ tainted=210
let dataClean = Data(clean)
let dataTainted = Data(tainted)
var stale = true
sink(arg: URL(dataRepresentation: dataTainted, relativeTo: urlClean)!) // $ tainted=210
sink(arg: URL(dataRepresentation: dataClean, relativeTo: urlTainted)!) // $ tainted=210
sink(arg: try! URL(resolvingBookmarkData: dataTainted, bookmarkDataIsStale: &stale)) // $ tainted=210
sink(arg: try! URL(resolvingBookmarkData: dataClean, relativeTo: urlTainted, bookmarkDataIsStale: &stale)) // $ tainted=210
sink(string: urlTainted.formatted()) // $ tainted=210
sink(string: urlTainted.fragment()!) // $ tainted=210
sink(string: urlTainted.host()!) // $ tainted=210
sink(string: urlTainted.password()!) // $ tainted=210
sink(string: urlTainted.path()) // $ tainted=210
sink(string: urlTainted.query()!) // $ tainted=210
sink(string: urlTainted.user()!) // $ tainted=210
var url1 = URL(string: clean)!
if let values = try? urlClean.resourceValues(forKeys: []) {
try! url1.setResourceValues(values)
}
sink(arg: url1)
if let values = try? urlTainted.resourceValues(forKeys: []) {
try! url1.setResourceValues(values)
}
sink(arg: url1) // $ tainted=210
var url2 = URL(string: clean)!
url2.setTemporaryResourceValue(source(), forKey: URL.URLResourceKey(""))
sink(arg: url2) // $ tainted=383
var url3 = URL(string: clean)!
url3.appendPathComponent(clean)
sink(arg: url3)
url3.appendPathComponent(tainted)
sink(arg: url3) // $ tainted=210
var url4 = URL(string: clean)!
url4.appendPathComponent(tainted, isDirectory: false)
sink(arg: url4) // $ tainted=210
var url5 = URL(string: clean)!
url5.appendPathExtension(tainted)
sink(arg: url5) // $ tainted=210
var url6 = URL(string: clean)!
url6.append(component: tainted)
sink(arg: url6) // $ tainted=210
var url7 = URL(string: clean)!
url7.append(components: tainted)
sink(arg: url7) // $ MISSING: tainted=210
var url8 = URL(string: clean)!
url8.append(components: clean, tainted)
sink(arg: url8) // $ MISSING: tainted=210
var url9 = URL(string: clean)!
url9.append(path: tainted)
sink(arg: url9) // $ tainted=210
var url10 = URL(string: clean)!
url10.append(queryItems: [source() as! URLQueryItem])
sink(arg: url10) // $ MISSING: tainted=210
sink(data: try! urlTainted.bookmarkData()) // $ tainted=210
sink(data: try! URL.bookmarkData(withContentsOf: urlTainted)) // $ tainted=210
sink(any: URL.resourceValues(forKeys: [], fromBookmarkData: dataTainted)!) // $ tainted=210
sink(arg: URL.homeDirectory) // (static var, not tainted)
sink(arg: URL.homeDirectory(forUser: clean)!)
sink(arg: URL.homeDirectory(forUser: tainted)!) // $ tainted=210
}
func taintThroughUrlRequest() {
@@ -161,21 +431,21 @@ func taintThroughUrlRequest() {
let tainted = source() as! URLRequest
sink(any: clean)
sink(any: tainted) // $tainted=161
sink(any: tainted) // $ tainted=431
sink(any: clean.cachePolicy)
sink(any: tainted.cachePolicy)
sink(any: clean.httpMethod)
sink(any: tainted.httpMethod)
sink(any: clean.url)
sink(any: tainted.url) // $tainted=161
sink(any: tainted.url) // $ tainted=431
sink(any: clean.httpBody)
sink(any: tainted.httpBody) // $tainted=161
sink(any: clean.httpBodyStream)
sink(any: tainted.httpBodyStream) // $tainted=161
sink(any: tainted.httpBody) // $ tainted=431
sink(any: clean.httpBodyStream!)
sink(any: tainted.httpBodyStream!) // $ tainted=431
sink(any: clean.mainDocument)
sink(any: tainted.mainDocument) // $tainted=161
sink(any: clean.allHTTPHeaderFields)
sink(any: tainted.allHTTPHeaderFields) // $tainted=161
sink(any: tainted.mainDocument) // $ tainted=431
sink(any: clean.allHTTPHeaderFields!)
sink(any: tainted.allHTTPHeaderFields!) // $ tainted=431
sink(any: clean.timeoutInterval)
sink(any: tainted.timeoutInterval)
sink(any: clean.httpShouldHandleCookies)
@@ -204,4 +474,41 @@ func taintThroughUrlRequest() {
sink(any: tainted.assumesHTTP3Capable)
sink(any: clean.requiresDNSSECValidation)
sink(any: tainted.requiresDNSSECValidation)
}
}
func taintThroughUrlResource() {
let clean = URLResource(name: "")
let tainted = source() as! URLResource
sink(string: clean.name)
sink(string: tainted.name) // $ tainted=481
sink(string: clean.subdirectory!)
sink(string: tainted.subdirectory!) // $ tainted=481
}
func taintUrlAsync() async throws {
let tainted = source() as! String
let urlTainted = URL(string: tainted)!
sink(any: urlTainted.lines) // $ tainted=490
for try await line in urlTainted.lines {
sink(string: line) // $ MISSING: tainted=490
}
}
func closureReturnValue() {
let url = URL(string: "http://example.com/")!
let r1 = url.withUnsafeFileSystemRepresentation({
ptr in
return "abc"
})
sink(string: r1)
let r2 = url.withUnsafeFileSystemRepresentation({
ptr in
return source() as! String
})
sink(string: r2) // $ tainted=511
}