Swift: Split off the Extensions.qll as well.

This commit is contained in:
Geoffrey White
2023-01-24 15:17:09 +00:00
parent cbfa7e7252
commit 78eff0dc60
6 changed files with 258 additions and 232 deletions

View File

@@ -0,0 +1,129 @@
/**
* Provides classes and predicates for reasoning about database
* queries built from user-controlled sources (that is, SQL injection
* vulnerabilities).
*/
import swift
import codeql.swift.dataflow.DataFlow
/**
* 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:)")
)
}
}

View File

@@ -8,127 +8,7 @@ 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:)")
)
}
}
import codeql.swift.security.SqlInjectionExtensions
/**
* A taint configuration for tainted data that reaches a SQL sink.

View File

@@ -0,0 +1,73 @@
/**
* Provides classes and predicates for reasoning about javascript
* evaluation vulnerabilities.
*/
import swift
import codeql.swift.dataflow.DataFlow
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()
}
}

View File

@@ -7,71 +7,7 @@ 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()
}
}
import codeql.swift.security.UnsafeJsEvalExtensions
/**
* A taint configuration from taint sources to sinks for this query.

View File

@@ -0,0 +1,53 @@
/**
* Provides classes and predicates for reasoning about unsafe
* webview fetch vulnerabilities.
*/
import swift
import codeql.swift.dataflow.DataFlow
/**
* 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 }
}

View File

@@ -7,52 +7,7 @@ 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 }
}
import codeql.swift.security.UnsafeWebViewFetchExtensions
/**
* A taint configuration from taint sources to sinks (and `baseURL` arguments)