Swift: UnsafeJsEval test finally compiles

This commit is contained in:
Nora Dimitrijević 2022-11-02 12:49:59 +01:00
Родитель 7b599f5fef
Коммит 28b7f0884f
3 изменённых файлов: 300 добавлений и 0 удалений

Просмотреть файл

Просмотреть файл

@ -0,0 +1 @@
queries/Security/CWE-094/UnsafeJsEval.ql

Просмотреть файл

@ -0,0 +1,299 @@
// --- stubs ---
class NSObject {}
@MainActor class UIResponder : NSObject {}
@MainActor class UIView : UIResponder {}
@MainActor class NSResponder : NSObject {}
class NSView : NSResponder {}
class WKFrameInfo : NSObject {}
class WKContentWorld : NSObject {
class var defaultClient: WKContentWorld { WKContentWorld() }
}
class WKWebView : UIView {
func evaluateJavaScript(
_ javaScriptString: String
) async throws -> Any { "" }
func evaluateJavaScript(
_ javaScriptString: String,
completionHandler: ((Any?, Error?) -> Void)? = nil
) {
completionHandler?(nil, nil)
}
@MainActor func evaluateJavaScript(
_ javaScript: String,
in frame: WKFrameInfo? = nil,
in contentWorld: WKContentWorld,
completionHandler: ((Result<Any, Error>) -> Void)? = nil
) {
completionHandler?(.success(""))
}
@MainActor func evaluateJavaScript(
_ javaScript: String,
in frame: WKFrameInfo? = nil,
contentWorld: WKContentWorld
) async throws -> Any? { nil }
@MainActor func callAsyncJavaScript(
_ functionBody: String,
arguments: [String : Any] = [:],
in frame: WKFrameInfo? = nil,
in contentWorld: WKContentWorld,
completionHandler: ((Result<Any, Error>) -> Void)? = nil
) {
completionHandler?(.success(""))
}
@MainActor func callAsyncJavaScript(
_ functionBody: String,
arguments: [String : Any] = [:],
in frame: WKFrameInfo? = nil,
contentWorld: WKContentWorld
) async throws -> Any? { nil }
}
enum WKUserScriptInjectionTime : Int, @unchecked Sendable {
case atDocumentStart, atDocumentEnd
}
class WKUserScript : NSObject {
init(
source: String,
injectionTime: WKUserScriptInjectionTime,
forMainFrameOnly: Bool
) {}
init(
source: String,
injectionTime: WKUserScriptInjectionTime,
forMainFrameOnly: Bool,
in contentWorld: WKContentWorld
) {}
}
class WKUserContentController : NSObject {
func addUserScript(_ userScript: WKUserScript) {}
}
class UIWebView : UIView {
// deprecated
func stringByEvaluatingJavaScript(from script: String) -> String? { nil }
}
class WebView : NSView {
// deprecated
func stringByEvaluatingJavaScript(from script: String!) -> String! { "" }
}
class JSValue : NSObject {}
class JSContext {
func evaluateScript(_ script: String!) -> JSValue! { return JSValue() }
func evaluateScript(
_ script: String!,
withSourceURL sourceURL: URL!
) -> JSValue! { return JSValue() }
}
typealias JSContextRef = OpaquePointer
typealias JSStringRef = OpaquePointer
typealias JSObjectRef = OpaquePointer
typealias JSValueRef = OpaquePointer
typealias JSChar = UInt16
func JSStringCreateWithCharacters(
_ chars: UnsafePointer<JSChar>!,
_ numChars: Int
) -> JSStringRef! {
return chars.withMemoryRebound(to: CChar.self, capacity: numChars) {
cchars in OpaquePointer(cchars)
}
}
func JSStringCreateWithUTF8CString(_ string: UnsafePointer<CChar>!) -> JSStringRef! {
return OpaquePointer(string)
}
func JSStringRetain(_ string: JSStringRef!) -> JSStringRef! { return string }
func JSStringRelease(_ string: JSStringRef!) { }
func JSEvaluateScript(
_ ctx: JSContextRef!,
_ script: JSStringRef!,
_ thisObject: JSObjectRef!,
_ sourceURL: JSStringRef!,
_ startingLineNumber: Int32,
_ exception: UnsafeMutablePointer<JSValueRef?>!
) -> JSValueRef! { return OpaquePointer(bitPattern: 0) }
@frozen
public struct Data: Collection {
public typealias Index = Int
public typealias Element = UInt8
public subscript(x: Index) -> Element { 0 }
public var startIndex: Index { 0 }
public var endIndex: Index { 0 }
public func index(after i: Index) -> Index { i + 1 }
init<S>(_ elements: S) {}
}
struct URL {
init?(string: String) {}
init?(string: String, relativeTo: URL?) {}
}
extension String {
init(contentsOf: URL) throws {
let data = ""
// ...
self.init(data)
}
}
// --- tests ---
func getRemoteData() -> String {
let url = URL(string: "http://example.com/")
do {
return try String(contentsOf: url!)
} catch {
return ""
}
}
func testUsage(_ sink: @escaping (String) async throws -> ()) {
Task {
let localString = "console.log('localString')"
let localStringFragment = "'localStringFragment'"
let remoteString = getRemoteData()
try! await sink(localString) // GOOD: the HTML data is local
try! await sink(getRemoteData()) // BAD: HTML contains remote input, may access local secrets
try! await sink(remoteString) // BAD
try! await sink("console.log(" + localStringFragment + ")") // GOOD: the HTML data is local
try! await sink("console.log(" + remoteString + ")") // BAD
let localData = Data(localString.utf8)
let remoteData = Data(remoteString.utf8)
try! await sink(String(decoding: localData, as: UTF8.self)) // GOOD: the data is local
try! await sink(String(decoding: remoteData, as: UTF8.self)) // BAD: the data is remote
try! await sink("console.log(" + String(Int(localStringFragment) ?? 0) + ")") // GOOD: Primitive conversion
try! await sink("console.log(" + String(Int(remoteString) ?? 0) + ")") // GOOD: Primitive conversion
try! await sink("console.log(" + (localStringFragment.count != 0 ? "1" : "0") + ")") // GOOD: Primitive conversion
try! await sink("console.log(" + (remoteString.count != 0 ? "1" : "0") + ")") // GOOD: Primitive conversion
}
}
func testUIWebView() {
let webview = UIWebView()
testUsage { string in
_ = await webview.stringByEvaluatingJavaScript(from: string)
}
}
func testWebView() {
let webview = WebView()
testUsage { string in
_ = await webview.stringByEvaluatingJavaScript(from: string)
}
}
func testWKWebView() {
let webview = WKWebView()
testUsage { string in
_ = try await webview.evaluateJavaScript(string)
}
testUsage { string in
await webview.evaluateJavaScript(string) { _, _ in }
}
testUsage { string in
await webview.evaluateJavaScript(string, in: nil, in: WKContentWorld.defaultClient) { _ in }
}
testUsage { string in
_ = try await webview.evaluateJavaScript(string, contentWorld: .defaultClient)
}
testUsage { string in
await webview.callAsyncJavaScript(string, in: nil, in: .defaultClient) { _ in () }
}
testUsage { string in
_ = try await webview.callAsyncJavaScript(string, contentWorld: WKContentWorld.defaultClient)
}
}
func testWKUserContentController() {
let ctrl = WKUserContentController()
testUsage { string in
ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentStart, forMainFrameOnly: false))
}
testUsage { string in
ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentEnd, forMainFrameOnly: true, in: .defaultClient))
}
}
func testJSContext() {
let ctx = JSContext()
testUsage { string in
_ = ctx.evaluateScript(string)
}
testUsage { string in
_ = ctx.evaluateScript(string, withSourceURL: URL(string: "https://example.com"))
}
}
func testJSEvaluateScript() {
testUsage { string in
string.utf16.withContiguousStorageIfAvailable { stringBytes in
let jsstr = JSStringRetain(JSStringCreateWithCharacters(stringBytes.baseAddress, string.count))
defer { JSStringRelease(jsstr) }
_ = JSEvaluateScript(
/*ctx:*/ OpaquePointer(bitPattern: 0),
/*script:*/ jsstr,
/*thisObject:*/ OpaquePointer(bitPattern: 0),
/*sourceURL:*/ OpaquePointer(bitPattern: 0),
/*startingLineNumber:*/ 0,
/*exception:*/ UnsafeMutablePointer(bitPattern: 0)
)
}
}
testUsage { string in
string.utf8CString.withUnsafeBufferPointer { stringBytes in
let jsstr = JSStringRetain(JSStringCreateWithUTF8CString(stringBytes.baseAddress))
defer { JSStringRelease(jsstr) }
_ = JSEvaluateScript(
/*ctx:*/ OpaquePointer(bitPattern: 0),
/*script:*/ jsstr,
/*thisObject:*/ OpaquePointer(bitPattern: 0),
/*sourceURL:*/ OpaquePointer(bitPattern: 0),
/*startingLineNumber:*/ 0,
/*exception:*/ UnsafeMutablePointer(bitPattern: 0)
)
}
}
}
func testQHelpExamples() {
}
testUIWebView()
testWebView()
testWKWebView()
testWKUserContentController()
testJSContext()
testJSEvaluateScript()
testQHelpExamples()