From cbfa7e7252ce4462610b4dfe8d4e041bfa21da12 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:53:23 +0000 Subject: [PATCH] Swift: Move query logic into .qlls. --- .../swift/security/SqlInjectionQuery.qll | 142 ++++++++++++++++++ .../swift/security/UnsafeJsEvalQuery.qll | 129 ++++++++++++++++ .../security/UnsafeWebViewFetchQuery.qll | 70 +++++++++ .../Security/CWE-079/UnsafeWebViewFetch.ql | 64 +------- .../queries/Security/CWE-089/SqlInjection.ql | 135 +---------------- .../queries/Security/CWE-094/UnsafeJsEval.ql | 123 +-------------- 6 files changed, 344 insertions(+), 319 deletions(-) create mode 100644 swift/ql/lib/codeql/swift/security/SqlInjectionQuery.qll create mode 100644 swift/ql/lib/codeql/swift/security/UnsafeJsEvalQuery.qll create mode 100644 swift/ql/lib/codeql/swift/security/UnsafeWebViewFetchQuery.qll diff --git a/swift/ql/lib/codeql/swift/security/SqlInjectionQuery.qll b/swift/ql/lib/codeql/swift/security/SqlInjectionQuery.qll new file mode 100644 index 00000000000..09cc6828a3c --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/SqlInjectionQuery.qll @@ -0,0 +1,142 @@ +/** + * Provides a taint-tracking configuration for reasoning about database + * queries built from user-controlled sources (that is, SQL injection + * vulnerabilities). + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources + +/** + * A `DataFlow::Node` that is a sink for a SQL string to be executed. + */ +abstract class SqlSink extends DataFlow::Node { } + +/** + * A sink for the sqlite3 C API. + */ +class CApiSqlSink extends SqlSink { + CApiSqlSink() { + // `sqlite3_exec` and variants of `sqlite3_prepare`. + exists(CallExpr call | + call.getStaticTarget() + .(FreeFunctionDecl) + .hasName([ + "sqlite3_exec(_:_:_:_:_:)", "sqlite3_prepare(_:_:_:_:_:)", + "sqlite3_prepare_v2(_:_:_:_:_:)", "sqlite3_prepare_v3(_:_:_:_:_:_:)", + "sqlite3_prepare16(_:_:_:_:_:)", "sqlite3_prepare16_v2(_:_:_:_:_:)", + "sqlite3_prepare16_v3(_:_:_:_:_:_:)" + ]) and + call.getArgument(1).getExpr() = this.asExpr() + ) + } +} + +/** + * A sink for the SQLite.swift library. + */ +class SQLiteSwiftSqlSink extends SqlSink { + SQLiteSwiftSqlSink() { + // Variants of `Connection.execute`, `connection.prepare` and `connection.scalar`. + exists(CallExpr call | + call.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("Connection", + ["execute(_:)", "prepare(_:_:)", "run(_:_:)", "scalar(_:_:)"]) and + call.getArgument(0).getExpr() = this.asExpr() + ) + or + // String argument to the `Statement` constructor. + exists(CallExpr call | + call.getStaticTarget().(MethodDecl).hasQualifiedName("Statement", "init(_:_:)") and + call.getArgument(1).getExpr() = this.asExpr() + ) + } +} + +/** A sink for the GRDB library. */ +class GrdbSqlSink extends SqlSink { + GrdbSqlSink() { + exists(CallExpr call, MethodDecl method | + call.getStaticTarget() = method and + call.getArgument(0).getExpr() = this.asExpr() + | + method + .hasQualifiedName("Database", + [ + "allStatements(sql:arguments:)", "cachedStatement(sql:)", + "internalCachedStatement(sql:)", "execute(sql:arguments:)", "makeStatement(sql:)", + "makeStatement(sql:prepFlags:)" + ]) + or + method + .hasQualifiedName("SQLRequest", + [ + "init(stringLiteral:)", "init(unicodeScalarLiteral:)", + "init(extendedGraphemeClusterLiteral:)", "init(stringInterpolation:)", + "init(sql:arguments:adapter:cached:)" + ]) + or + method + .hasQualifiedName("SQL", + [ + "init(stringLiteral:)", "init(unicodeScalarLiteral:)", + "init(extendedGraphemeClusterLiteral:)", "init(stringInterpolation:)", + "init(sql:arguments:)", "append(sql:arguments:)" + ]) + or + method + .hasQualifiedName("TableDefinition", ["column(sql:)", "check(sql:)", "constraint(sql:)"]) + or + method.hasQualifiedName("TableAlteration", "addColumn(sql:)") + or + method + .hasQualifiedName("ColumnDefinition", + ["check(sql:)", "defaults(sql:)", "generatedAs(sql:_:)"]) + or + method + .hasQualifiedName("TableRecord", + [ + "select(sql:arguments:)", "select(sql:arguments:as:)", "filter(sql:arguments:)", + "order(sql:arguments:)" + ]) + or + method.hasQualifiedName("StatementCache", "statement(_:)") + ) + or + exists(CallExpr call, MethodDecl method | + call.getStaticTarget() = method and + call.getArgument(1).getExpr() = this.asExpr() + | + method + .hasQualifiedName(["Row", "DatabaseValueConvertible"], + [ + "fetchCursor(_:sql:arguments:adapter:)", "fetchAll(_:sql:arguments:adapter:)", + "fetchSet(_:sql:arguments:adapter:)", "fetchOne(_:sql:arguments:adapter:)" + ]) + or + method.hasQualifiedName("SQLStatementCursor", "init(database:sql:arguments:prepFlags:)") + ) + or + exists(CallExpr call, MethodDecl method | + call.getStaticTarget() = method and + call.getArgument(3).getExpr() = this.asExpr() + | + method + .hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)") + ) + } +} + +/** + * A taint configuration for tainted data that reaches a SQL sink. + */ +class SqlInjectionConfig extends TaintTracking::Configuration { + SqlInjectionConfig() { this = "SqlInjectionConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + + override predicate isSink(DataFlow::Node node) { node instanceof SqlSink } +} diff --git a/swift/ql/lib/codeql/swift/security/UnsafeJsEvalQuery.qll b/swift/ql/lib/codeql/swift/security/UnsafeJsEvalQuery.qll new file mode 100644 index 00000000000..646745ec4ff --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/UnsafeJsEvalQuery.qll @@ -0,0 +1,129 @@ +/** + * Provides a taint-tracking configuration for reasoning about javascript + * evaluation vulnerabilities. + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources + +/** + * A source of untrusted, user-controlled data. + */ +class Source = FlowSource; + +/** + * A sink that evaluates a string of JavaScript code. + */ +abstract class Sink extends DataFlow::Node { } + +class WKWebView extends Sink { + WKWebView() { + any(CallExpr ce | + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("WKWebView", + [ + "evaluateJavaScript(_:)", "evaluateJavaScript(_:completionHandler:)", + "evaluateJavaScript(_:in:in:completionHandler:)", + "evaluateJavaScript(_:in:contentWorld:)", + "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", + "callAsyncJavaScript(_:arguments:in:contentWorld:)" + ]) + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class WKUserContentController extends Sink { + WKUserContentController() { + any(CallExpr ce | + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("WKUserContentController", "addUserScript(_:)") + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class UIWebView extends Sink { + UIWebView() { + any(CallExpr ce | + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName(["UIWebView", "WebView"], "stringByEvaluatingJavaScript(from:)") + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class JSContext extends Sink { + JSContext() { + any(CallExpr ce | + ce.getStaticTarget() + .(MethodDecl) + .hasQualifiedName("JSContext", ["evaluateScript(_:)", "evaluateScript(_:withSourceURL:)"]) + ).getArgument(0).getExpr() = this.asExpr() + } +} + +class JSEvaluateScript extends Sink { + JSEvaluateScript() { + any(CallExpr ce | + ce.getStaticTarget().(FreeFunctionDecl).hasName("JSEvaluateScript(_:_:_:_:_:_:)") + ).getArgument(1).getExpr() = this.asExpr() + } +} + +/** + * A taint configuration from taint sources to sinks for this query. + */ +class UnsafeJsEvalConfig extends TaintTracking::Configuration { + UnsafeJsEvalConfig() { this = "UnsafeJsEvalConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof Source } + + override predicate isSink(DataFlow::Node node) { node instanceof Sink } + + // TODO: convert to new taint flow models + override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(Argument arg | + arg = + any(CallExpr ce | + ce.getStaticTarget().(MethodDecl).hasQualifiedName("String", "init(decoding:as:)") + ).getArgument(0) + or + arg = + any(CallExpr ce | + ce.getStaticTarget() + .(FreeFunctionDecl) + .hasName([ + "JSStringCreateWithUTF8CString(_:)", "JSStringCreateWithCharacters(_:_:)", + "JSStringRetain(_:)" + ]) + ).getArgument(0) + | + nodeFrom.asExpr() = arg.getExpr() and + nodeTo.asExpr() = arg.getApplyExpr() + ) + or + exists(CallExpr ce, Expr self, AbstractClosureExpr closure | + ce.getStaticTarget() + .getName() + .matches(["withContiguousStorageIfAvailable(%)", "withUnsafeBufferPointer(%)"]) and + self = ce.getQualifier() and + ce.getArgument(0).getExpr() = closure + | + nodeFrom.asExpr() = self and + nodeTo.(DataFlow::ParameterNode).getParameter() = closure.getParam(0) + ) + or + exists(MemberRefExpr e, Expr self, VarDecl member | + self.getType().getName().matches(["Unsafe%Buffer%", "Unsafe%Pointer%"]) and + member.getName() = "baseAddress" + | + e.getBase() = self and + e.getMember() = member and + nodeFrom.asExpr() = self and + nodeTo.asExpr() = e + ) + } +} diff --git a/swift/ql/lib/codeql/swift/security/UnsafeWebViewFetchQuery.qll b/swift/ql/lib/codeql/swift/security/UnsafeWebViewFetchQuery.qll new file mode 100644 index 00000000000..4382e2c7e39 --- /dev/null +++ b/swift/ql/lib/codeql/swift/security/UnsafeWebViewFetchQuery.qll @@ -0,0 +1,70 @@ +/** + * Provides a taint-tracking configuration for reasoning about unsafe + * webview fetch vulnerabilities. + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources + +/** + * A sink that is a candidate result for this query, such as certain arguments + * to `UIWebView.loadHTMLString`. + */ +class Sink extends DataFlow::Node { + Expr baseUrl; + + Sink() { + exists( + MethodDecl funcDecl, CallExpr call, string className, string funcName, int arg, int baseArg + | + // arguments to method calls... + ( + // `loadHTMLString` + className = ["UIWebView", "WKWebView"] and + funcName = "loadHTMLString(_:baseURL:)" and + arg = 0 and + baseArg = 1 + or + // `UIWebView.load` + className = "UIWebView" and + funcName = "load(_:mimeType:textEncodingName:baseURL:)" and + arg = 0 and + baseArg = 3 + or + // `WKWebView.load` + className = "WKWebView" and + funcName = "load(_:mimeType:characterEncodingName:baseURL:)" and + arg = 0 and + baseArg = 3 + ) and + call.getStaticTarget() = funcDecl and + // match up `funcName`, `paramName`, `arg`, `node`. + funcDecl.hasQualifiedName(className, funcName) and + call.getArgument(arg).getExpr() = this.asExpr() and + // match up `baseURLArg` + call.getArgument(baseArg).getExpr() = baseUrl + ) + } + + /** + * Gets the `baseURL` argument associated with this sink. + */ + Expr getBaseUrl() { result = baseUrl } +} + +/** + * A taint configuration from taint sources to sinks (and `baseURL` arguments) + * for this query. + */ +class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { + UnsafeWebViewFetchConfig() { this = "UnsafeWebViewFetchConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node node) { + node instanceof Sink or + node.asExpr() = any(Sink s).getBaseUrl() + } +} diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index 92da3eb2869..fb6d33ee163 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -14,71 +14,9 @@ import swift import codeql.swift.dataflow.DataFlow -import codeql.swift.dataflow.TaintTracking -import codeql.swift.dataflow.FlowSources +import codeql.swift.security.UnsafeWebViewFetchQuery import DataFlow::PathGraph -/** - * A sink that is a candidate result for this query, such as certain arguments - * to `UIWebView.loadHTMLString`. - */ -class Sink extends DataFlow::Node { - Expr baseUrl; - - Sink() { - exists( - MethodDecl funcDecl, CallExpr call, string className, string funcName, int arg, int baseArg - | - // arguments to method calls... - ( - // `loadHTMLString` - className = ["UIWebView", "WKWebView"] and - funcName = "loadHTMLString(_:baseURL:)" and - arg = 0 and - baseArg = 1 - or - // `UIWebView.load` - className = "UIWebView" and - funcName = "load(_:mimeType:textEncodingName:baseURL:)" and - arg = 0 and - baseArg = 3 - or - // `WKWebView.load` - className = "WKWebView" and - funcName = "load(_:mimeType:characterEncodingName:baseURL:)" and - arg = 0 and - baseArg = 3 - ) and - call.getStaticTarget() = funcDecl and - // match up `funcName`, `paramName`, `arg`, `node`. - funcDecl.hasQualifiedName(className, funcName) and - call.getArgument(arg).getExpr() = this.asExpr() and - // match up `baseURLArg` - call.getArgument(baseArg).getExpr() = baseUrl - ) - } - - /** - * Gets the `baseURL` argument associated with this sink. - */ - Expr getBaseUrl() { result = baseUrl } -} - -/** - * A taint configuration from taint sources to sinks (and `baseURL` arguments) - * for this query. - */ -class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { - UnsafeWebViewFetchConfig() { this = "UnsafeWebViewFetchConfig" } - - override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node node) { - node instanceof Sink or - node.asExpr() = any(Sink s).getBaseUrl() - } -} - from UnsafeWebViewFetchConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, Sink sink, string message diff --git a/swift/ql/src/queries/Security/CWE-089/SqlInjection.ql b/swift/ql/src/queries/Security/CWE-089/SqlInjection.ql index 3fc44bc333a..2787f237e9c 100644 --- a/swift/ql/src/queries/Security/CWE-089/SqlInjection.ql +++ b/swift/ql/src/queries/Security/CWE-089/SqlInjection.ql @@ -12,142 +12,9 @@ import swift import codeql.swift.dataflow.DataFlow -import codeql.swift.dataflow.TaintTracking -import codeql.swift.dataflow.FlowSources +import codeql.swift.security.SqlInjectionQuery import DataFlow::PathGraph -/** - * A `DataFlow::Node` that is a sink for a SQL string to be executed. - */ -abstract class SqlSink extends DataFlow::Node { } - -/** - * A sink for the sqlite3 C API. - */ -class CApiSqlSink extends SqlSink { - CApiSqlSink() { - // `sqlite3_exec` and variants of `sqlite3_prepare`. - exists(CallExpr call | - call.getStaticTarget() - .(FreeFunctionDecl) - .hasName([ - "sqlite3_exec(_:_:_:_:_:)", "sqlite3_prepare(_:_:_:_:_:)", - "sqlite3_prepare_v2(_:_:_:_:_:)", "sqlite3_prepare_v3(_:_:_:_:_:_:)", - "sqlite3_prepare16(_:_:_:_:_:)", "sqlite3_prepare16_v2(_:_:_:_:_:)", - "sqlite3_prepare16_v3(_:_:_:_:_:_:)" - ]) and - call.getArgument(1).getExpr() = this.asExpr() - ) - } -} - -/** - * A sink for the SQLite.swift library. - */ -class SQLiteSwiftSqlSink extends SqlSink { - SQLiteSwiftSqlSink() { - // Variants of `Connection.execute`, `connection.prepare` and `connection.scalar`. - exists(CallExpr call | - call.getStaticTarget() - .(MethodDecl) - .hasQualifiedName("Connection", - ["execute(_:)", "prepare(_:_:)", "run(_:_:)", "scalar(_:_:)"]) and - call.getArgument(0).getExpr() = this.asExpr() - ) - or - // String argument to the `Statement` constructor. - exists(CallExpr call | - call.getStaticTarget().(MethodDecl).hasQualifiedName("Statement", "init(_:_:)") and - call.getArgument(1).getExpr() = this.asExpr() - ) - } -} - -/** A sink for the GRDB library. */ -class GrdbSqlSink extends SqlSink { - GrdbSqlSink() { - exists(CallExpr call, MethodDecl method | - call.getStaticTarget() = method and - call.getArgument(0).getExpr() = this.asExpr() - | - method - .hasQualifiedName("Database", - [ - "allStatements(sql:arguments:)", "cachedStatement(sql:)", - "internalCachedStatement(sql:)", "execute(sql:arguments:)", "makeStatement(sql:)", - "makeStatement(sql:prepFlags:)" - ]) - or - method - .hasQualifiedName("SQLRequest", - [ - "init(stringLiteral:)", "init(unicodeScalarLiteral:)", - "init(extendedGraphemeClusterLiteral:)", "init(stringInterpolation:)", - "init(sql:arguments:adapter:cached:)" - ]) - or - method - .hasQualifiedName("SQL", - [ - "init(stringLiteral:)", "init(unicodeScalarLiteral:)", - "init(extendedGraphemeClusterLiteral:)", "init(stringInterpolation:)", - "init(sql:arguments:)", "append(sql:arguments:)" - ]) - or - method - .hasQualifiedName("TableDefinition", ["column(sql:)", "check(sql:)", "constraint(sql:)"]) - or - method.hasQualifiedName("TableAlteration", "addColumn(sql:)") - or - method - .hasQualifiedName("ColumnDefinition", - ["check(sql:)", "defaults(sql:)", "generatedAs(sql:_:)"]) - or - method - .hasQualifiedName("TableRecord", - [ - "select(sql:arguments:)", "select(sql:arguments:as:)", "filter(sql:arguments:)", - "order(sql:arguments:)" - ]) - or - method.hasQualifiedName("StatementCache", "statement(_:)") - ) - or - exists(CallExpr call, MethodDecl method | - call.getStaticTarget() = method and - call.getArgument(1).getExpr() = this.asExpr() - | - method - .hasQualifiedName(["Row", "DatabaseValueConvertible"], - [ - "fetchCursor(_:sql:arguments:adapter:)", "fetchAll(_:sql:arguments:adapter:)", - "fetchSet(_:sql:arguments:adapter:)", "fetchOne(_:sql:arguments:adapter:)" - ]) - or - method.hasQualifiedName("SQLStatementCursor", "init(database:sql:arguments:prepFlags:)") - ) - or - exists(CallExpr call, MethodDecl method | - call.getStaticTarget() = method and - call.getArgument(3).getExpr() = this.asExpr() - | - method - .hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)") - ) - } -} - -/** - * A taint configuration for tainted data that reaches a SQL sink. - */ -class SqlInjectionConfig extends TaintTracking::Configuration { - SqlInjectionConfig() { this = "SqlInjectionConfig" } - - override predicate isSource(DataFlow::Node node) { node instanceof FlowSource } - - override predicate isSink(DataFlow::Node node) { node instanceof SqlSink } -} - from SqlInjectionConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode where config.hasFlowPath(sourceNode, sinkNode) select sinkNode.getNode(), sourceNode, sinkNode, "This query depends on a $@.", diff --git a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql index 9219b4f5f55..ddddecd93b1 100644 --- a/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql +++ b/swift/ql/src/queries/Security/CWE-094/UnsafeJsEval.ql @@ -14,130 +14,9 @@ import swift import codeql.swift.dataflow.DataFlow -import codeql.swift.dataflow.TaintTracking -import codeql.swift.dataflow.FlowSources +import codeql.swift.security.UnsafeJsEvalQuery import DataFlow::PathGraph -/** - * A source of untrusted, user-controlled data. - */ -class Source = FlowSource; - -/** - * A sink that evaluates a string of JavaScript code. - */ -abstract class Sink extends DataFlow::Node { } - -class WKWebView extends Sink { - WKWebView() { - any(CallExpr ce | - ce.getStaticTarget() - .(MethodDecl) - .hasQualifiedName("WKWebView", - [ - "evaluateJavaScript(_:)", "evaluateJavaScript(_:completionHandler:)", - "evaluateJavaScript(_:in:in:completionHandler:)", - "evaluateJavaScript(_:in:contentWorld:)", - "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", - "callAsyncJavaScript(_:arguments:in:contentWorld:)" - ]) - ).getArgument(0).getExpr() = this.asExpr() - } -} - -class WKUserContentController extends Sink { - WKUserContentController() { - any(CallExpr ce | - ce.getStaticTarget() - .(MethodDecl) - .hasQualifiedName("WKUserContentController", "addUserScript(_:)") - ).getArgument(0).getExpr() = this.asExpr() - } -} - -class UIWebView extends Sink { - UIWebView() { - any(CallExpr ce | - ce.getStaticTarget() - .(MethodDecl) - .hasQualifiedName(["UIWebView", "WebView"], "stringByEvaluatingJavaScript(from:)") - ).getArgument(0).getExpr() = this.asExpr() - } -} - -class JSContext extends Sink { - JSContext() { - any(CallExpr ce | - ce.getStaticTarget() - .(MethodDecl) - .hasQualifiedName("JSContext", ["evaluateScript(_:)", "evaluateScript(_:withSourceURL:)"]) - ).getArgument(0).getExpr() = this.asExpr() - } -} - -class JSEvaluateScript extends Sink { - JSEvaluateScript() { - any(CallExpr ce | - ce.getStaticTarget().(FreeFunctionDecl).hasName("JSEvaluateScript(_:_:_:_:_:_:)") - ).getArgument(1).getExpr() = this.asExpr() - } -} - -/** - * A taint configuration from taint sources to sinks for this query. - */ -class UnsafeJsEvalConfig extends TaintTracking::Configuration { - UnsafeJsEvalConfig() { this = "UnsafeJsEvalConfig" } - - override predicate isSource(DataFlow::Node node) { node instanceof Source } - - override predicate isSink(DataFlow::Node node) { node instanceof Sink } - - // TODO: convert to new taint flow models - override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(Argument arg | - arg = - any(CallExpr ce | - ce.getStaticTarget().(MethodDecl).hasQualifiedName("String", "init(decoding:as:)") - ).getArgument(0) - or - arg = - any(CallExpr ce | - ce.getStaticTarget() - .(FreeFunctionDecl) - .hasName([ - "JSStringCreateWithUTF8CString(_:)", "JSStringCreateWithCharacters(_:_:)", - "JSStringRetain(_:)" - ]) - ).getArgument(0) - | - nodeFrom.asExpr() = arg.getExpr() and - nodeTo.asExpr() = arg.getApplyExpr() - ) - or - exists(CallExpr ce, Expr self, AbstractClosureExpr closure | - ce.getStaticTarget() - .getName() - .matches(["withContiguousStorageIfAvailable(%)", "withUnsafeBufferPointer(%)"]) and - self = ce.getQualifier() and - ce.getArgument(0).getExpr() = closure - | - nodeFrom.asExpr() = self and - nodeTo.(DataFlow::ParameterNode).getParameter() = closure.getParam(0) - ) - or - exists(MemberRefExpr e, Expr self, VarDecl member | - self.getType().getName().matches(["Unsafe%Buffer%", "Unsafe%Pointer%"]) and - member.getName() = "baseAddress" - | - e.getBase() = self and - e.getMember() = member and - nodeFrom.asExpr() = self and - nodeTo.asExpr() = e - ) - } -} - from UnsafeJsEvalConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, Sink sink where