// --- stubs ---
class NSObject
{
}
struct URL
{
init?(string: String) {}
init?(string: String, relativeTo: URL?) {}
}
extension String {
init(contentsOf: URL) throws {
var data = ""
// ...
self.init(data)
}
}
class NSURLRequest : NSObject
{
enum CachePolicy : UInt
{
case useProtocolCachePolicy
}
}
typealias TimeInterval = Double
class URLRequest
{
typealias CachePolicy = NSURLRequest.CachePolicy
init(url: URL, cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, timeoutInterval: TimeInterval = 60.0) {}
}
class Data
{
init(_ elements: S) {}
}
class WKNavigation : NSObject
{
}
class UIResponder : NSObject
{
}
class UIView : UIResponder
{
}
class UIWebView : UIView
{
func loadRequest(_ request: URLRequest) {} // deprecated
func load(_ data: Data, mimeType MIMEType: String, textEncodingName: String, baseURL: URL) {} // deprecated
func loadHTMLString(_ string: String, baseURL: URL?) {} // deprecated
}
class WKWebView : UIView
{
func load(_ request: URLRequest) -> WKNavigation? {
// ...
return WKNavigation()
}
func load(_ data: Data, mimeType MIMEType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation? {
// ...
return WKNavigation()
}
func loadHTMLString(_ string: String, baseURL: URL?) -> WKNavigation? {
// ...
return WKNavigation()
}
}
// --- tests ---
func getRemoteData() -> String {
let url = URL(string: "http://example.com/")
do
{
return try String(contentsOf: url!)
} catch {
return ""
}
}
func testSimpleFlows() {
let webview = UIWebView()
webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD
let data = try! String(contentsOf: URL(string: "http://example.com/")!)
webview.loadHTMLString(data, baseURL: nil) // BAD
let url = URL(string: "http://example.com/")
webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD
}
func testUIWebView() {
let webview = UIWebView()
let localString = "
Local HTML
" let localStringFragment = "Local HTML
" let remoteString = getRemoteData() webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets webview.loadHTMLString(remoteString, baseURL: nil) // BAD webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD let localSafeURL = URL(string: "about:blank") let localURL = URL(string: "http://example.com/") let remoteURL = URL(string: remoteString) let remoteURL2 = URL(string: "/path", relativeTo: remoteURL) webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD let localRequest = URLRequest(url: localURL!) let remoteRequest = URLRequest(url: remoteURL!) webview.loadRequest(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL webview.loadRequest(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL let localData = Data(localString.utf8) let remoteData = Data(remoteString.utf8) webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD } func testWKWebView() { let webview = WKWebView() // note: `WKWebView` is safer than `UIWebView` as it has better security configuration options // and is more locked down by default. let localString = "Local HTML
" let localStringFragment = "Local HTML
" let remoteString = getRemoteData() webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD webview.loadHTMLString(remoteString, baseURL: nil) // BAD webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD let localSafeURL = URL(string: "about:blank") let localURL = URL(string: "http://example.com/") let remoteURL = URL(string: remoteString) let remoteURL2 = URL(string: "/path", relativeTo: remoteURL) webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD let localRequest = URLRequest(url: localURL!) let remoteRequest = URLRequest(url: remoteURL!) webview.load(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL webview.load(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL let localData = Data(localString.utf8) let remoteData = Data(remoteString.utf8) webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD } func testQHelpExamples() { let webview = UIWebView() let htmlData = getRemoteData() // ... webview.loadHTMLString(htmlData, baseURL: nil) // BAD webview.loadHTMLString(htmlData, baseURL: URL(string: "about:blank")) // GOOD } testSimpleFlows() testUIWebView() testWKWebView() testQHelpExamples()