mirror of
https://github.com/github/codeql.git
synced 2026-04-18 05:24:01 +02:00
Swift: Split off the Extensions.qll as well.
This commit is contained in:
129
swift/ql/lib/codeql/swift/security/SqlInjectionExtensions.qll
Normal file
129
swift/ql/lib/codeql/swift/security/SqlInjectionExtensions.qll
Normal 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:)")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user