From 5e9026df8c0a6608ef1895403dae01c7d18aeaaf Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:37:27 +0000 Subject: [PATCH 01/13] unified: Copy Swift SQL injection query and tests unchanged Establish a byte-for-byte baseline for the unified Swift port, including query help, examples and the original test expectations. API and reference adaptations follow separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../security/CWE-089/SqlInjection.qhelp | 38 ++ .../queries/security/CWE-089/SqlInjection.ql | 21 + .../security/CWE-089/SqlInjectionBad.swift | 12 + .../security/CWE-089/SqlInjectionGood.swift | 19 + .../security/CWE-089/SqlInjection/GRDB.swift | 360 +++++++++++++++++ .../CWE-089/SqlInjection/SQLite.swift | 133 +++++++ .../SqlInjection/SqlInjection.expected | 368 ++++++++++++++++++ .../CWE-089/SqlInjection/SqlInjection.qlref | 2 + .../security/CWE-089/SqlInjection/other.swift | 64 +++ .../CWE-089/SqlInjection/sqlite3_c_api.swift | 215 ++++++++++ 10 files changed, 1232 insertions(+) create mode 100644 unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp create mode 100644 unified/ql/src/queries/security/CWE-089/SqlInjection.ql create mode 100644 unified/ql/src/queries/security/CWE-089/SqlInjectionBad.swift create mode 100644 unified/ql/src/queries/security/CWE-089/SqlInjectionGood.swift create mode 100644 unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift create mode 100644 unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift create mode 100644 unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected create mode 100644 unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref create mode 100644 unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift create mode 100644 unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp b/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp new file mode 100644 index 000000000000..6975d95d48a1 --- /dev/null +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp @@ -0,0 +1,38 @@ + + + + +

+If a database query (such as a SQL query) is built from user-provided data without sufficient sanitization, a user may be able to run malicious database queries. An attacker can craft the part of the query they control to change the overall meaning of the query. +

+ +
+ + +

+Most database connector libraries offer a way to safely embed untrusted data into a query using query parameters or prepared statements. You should use these features to build queries, rather than string concatenation or similar methods. You can also escape (sanitize) user-controlled strings so that they can be included directly in an SQL command. A library function should be used for escaping, because this approach is only safe if the escaping function is robust against all possible inputs. +

+ +
+ + +

In the following examples, an SQL query is prepared using string interpolation to directly include a user-controlled value userControlledString in the query. An attacker could craft userControlledString to change the overall meaning of the SQL query. +

+ + + +

A better way to do this is with a prepared statement, binding userControlledString to a parameter of that statement. An attacker who controls userControlledString now cannot change the overall meaning of the query. +

+ + + +
+ + +
  • Wikipedia: SQL injection.
  • +
  • OWASP: SQL Injection Prevention Cheat Sheet.
  • + +
    +
    diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql new file mode 100644 index 000000000000..8c78cad7044b --- /dev/null +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -0,0 +1,21 @@ +/** + * @name Database query built from user-controlled sources + * @description Building a database query from user-controlled sources is vulnerable to insertion of malicious code by the user. + * @kind path-problem + * @problem.severity error + * @security-severity 8.8 + * @precision high + * @id swift/sql-injection + * @tags security + * external/cwe/cwe-089 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.security.SqlInjectionQuery +import SqlInjectionFlow::PathGraph + +from SqlInjectionFlow::PathNode sourceNode, SqlInjectionFlow::PathNode sinkNode +where SqlInjectionFlow::flowPath(sourceNode, sinkNode) +select sinkNode.getNode(), sourceNode, sinkNode, "This query depends on a $@.", + sourceNode.getNode(), "user-provided value" diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjectionBad.swift b/unified/ql/src/queries/security/CWE-089/SqlInjectionBad.swift new file mode 100644 index 000000000000..085b1b547983 --- /dev/null +++ b/unified/ql/src/queries/security/CWE-089/SqlInjectionBad.swift @@ -0,0 +1,12 @@ +// with SQLite.swift + +let unsafeQuery = "SELECT * FROM users WHERE username='\(userControlledString)'" + +try db.execute(unsafeQuery) // BAD + +let stmt = try db.prepare(unsafeQuery) // also BAD +try stmt.run() + +// with SQLite3 C API + +let result = sqlite3_exec(db, unsafeQuery, nil, nil, nil) // BAD diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjectionGood.swift b/unified/ql/src/queries/security/CWE-089/SqlInjectionGood.swift new file mode 100644 index 000000000000..8d4228fca50a --- /dev/null +++ b/unified/ql/src/queries/security/CWE-089/SqlInjectionGood.swift @@ -0,0 +1,19 @@ +// with SQLite.swift + +let safeQuery = "SELECT * FROM users WHERE username=?" + +let stmt = try db.prepare(safeQuery, userControlledString) // GOOD +try stmt.run() + +// with sqlite3 C API + +var stmt2: OpaquePointer? + +if (sqlite3_prepare_v2(db, safeQuery, -1, &stmt2, nil) == SQLITE_OK) { + if (sqlite3_bind_text(stmt2, 1, userControlledString, -1, SQLITE_TRANSIENT) == SQLITE_OK) { // GOOD + let result = sqlite3_step(stmt2) + + // ... + } + sqlite3_finalize(stmt2) +} diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift new file mode 100644 index 000000000000..3bdffaa272b4 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift @@ -0,0 +1,360 @@ +// --- stubs --- + +struct URL +{ + init?(string: String) {} +} + +extension String { + init(contentsOf: URL) throws { + let data = "" + + // ... + + self.init(data) + } +} + +struct StatementArguments {} +class Statement {} +protocol RowAdapter {} +class RowDecoder {} +enum GeneratedColumnQualification { case virtual } +struct QueryInterfaceRequest {} + +class Database { + func allStatements(sql: String, arguments: StatementArguments? = nil) -> SQLStatementCursor { return SQLStatementCursor(database: self, sql: "", arguments: nil) } + func cachedStatement(sql: String) -> Statement { return Statement() } + func internalCachedStatement(sql: String) -> Statement { return Statement() } + func execute(sql: String, arguments: StatementArguments = StatementArguments()) {} + func makeStatement(sql: String) -> Statement { return Statement() } + func makeStatement(sql: String, prepFlags: CUnsignedInt) -> Statement { return Statement() } +} + +struct SQLRequest { + init(stringLiteral: String) {} + init(unicodeScalarLiteral: String) {} + init(extendedGraphemeClusterLiteral: String) {} + init(stringInterpolation: String) {} + init(sql: String, arguments: StatementArguments = StatementArguments(), adapter: (any RowAdapter)? = nil, cached: Bool = false) {} +} + +struct SQL { + init(stringLiteral: String) {} + init(unicodeScalarLiteral: String) {} + init(extendedGraphemeClusterLiteral: String) {} + init(stringInterpolation: String) {} + init(sql: String, arguments: StatementArguments = StatementArguments()) {} + func append(sql: String, arguments: StatementArguments = StatementArguments()) {} +} + +class TableDefinition { + func column(sql: String) {} + func check(sql: String) {} + func constraint(sql: String) {} +} + +class TableAlteration { + func addColumn(sql: String) {} +} + +class ColumnDefinition { + func check(sql: String) -> Self { return self } + func defaults(sql: String) -> Self { return self } + func generatedAs(sql: String, _: GeneratedColumnQualification = .virtual) -> Self { return self } +} + +class TableRecord { + static func select(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest { QueryInterfaceRequest() } + static func select(sql: String, arguments: StatementArguments = StatementArguments(), as: RowDecoder.Type = RowDecoder.self) -> QueryInterfaceRequest{ QueryInterfaceRequest() } + static func filter(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest { QueryInterfaceRequest() } + static func order(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest { QueryInterfaceRequest() } +} + +struct StatementCache { + func statement(_: String) -> Statement { return Statement() } +} + +class Row { + func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} + func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} + func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} + func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} +} + +class DatabaseValueConvertible { + func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} + func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} + func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} + func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {} +} + +class SQLStatementCursor { + init(database: Database, sql: String, arguments: StatementArguments?, prepFlags: CUnsignedInt = 0) {} +} + +class CommonTableExpression { + init(recursive: Bool = false, named: String, columns: [String]? = nil, sql: String, arguments: StatementArguments = StatementArguments()) {} +} + +// --- tests --- + +func test(database: Database) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = database.allStatements(sql: remoteString) // $ Alert + let _ = database.allStatements(sql: localString) // GOOD + let _ = database.allStatements(sql: remoteString, arguments: nil) // $ Alert + let _ = database.allStatements(sql: localString, arguments: nil) // GOOD + + let _ = database.cachedStatement(sql: remoteString) // $ Alert + let _ = database.cachedStatement(sql: localString) // GOOD + + let _ = database.internalCachedStatement(sql: remoteString) // $ Alert + let _ = database.internalCachedStatement(sql: localString) // GOOD + + database.execute(sql: remoteString) // $ Alert + database.execute(sql: localString) // GOOD + database.execute(sql: remoteString, arguments: StatementArguments()) // $ Alert + database.execute(sql: localString, arguments: StatementArguments()) // GOOD + + let _ = database.makeStatement(sql: remoteString) // $ Alert + let _ = database.makeStatement(sql: localString) // GOOD + let _ = database.makeStatement(sql: remoteString, prepFlags: 0) // $ Alert + let _ = database.makeStatement(sql: localString, prepFlags: 0) // GOOD +} + +func testSqlRequest() throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = SQLRequest(stringLiteral: remoteString) // $ Alert + let _ = SQLRequest(stringLiteral: localString) // GOOD + + let _ = SQLRequest(unicodeScalarLiteral: remoteString) // $ Alert + let _ = SQLRequest(unicodeScalarLiteral: localString) // GOOD + + let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // $ Alert + let _ = SQLRequest(extendedGraphemeClusterLiteral: localString) // GOOD + + let _ = SQLRequest(stringInterpolation: remoteString) // $ Alert + let _ = SQLRequest(stringInterpolation: localString) // GOOD + + let _ = SQLRequest(sql: remoteString) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), cached: false) // $ Alert + let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil, cached: false) // $ Alert + let _ = SQLRequest(sql: remoteString, adapter: nil) // $ Alert + let _ = SQLRequest(sql: remoteString, adapter: nil, cached: false) // $ Alert + let _ = SQLRequest(sql: remoteString, cached: false) // $ Alert + let _ = SQLRequest(sql: localString) // GOOD + let _ = SQLRequest(sql: localString, arguments: StatementArguments()) // GOOD + let _ = SQLRequest(sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + let _ = SQLRequest(sql: localString, arguments: StatementArguments(), cached: false) // GOOD + let _ = SQLRequest(sql: localString, arguments: StatementArguments(), adapter: nil, cached: false) // GOOD + let _ = SQLRequest(sql: localString, adapter: nil) // GOOD + let _ = SQLRequest(sql: localString, adapter: nil, cached: false) // GOOD + let _ = SQLRequest(sql: localString, cached: false) // GOOD +} + +func testSql() throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = SQL(stringLiteral: remoteString) // $ Alert + let _ = SQL(unicodeScalarLiteral: remoteString) // $ Alert + let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // $ Alert + let _ = SQL(stringInterpolation: remoteString) // $ Alert + let _ = SQL(sql: remoteString) // $ Alert + let sql1 = SQL(stringLiteral: "") + sql1.append(sql: remoteString) // $ Alert + + let _ = SQL(stringLiteral: localString) // GOOD + let _ = SQL(unicodeScalarLiteral: localString) // GOOD + let _ = SQL(extendedGraphemeClusterLiteral: localString) // GOOD + let _ = SQL(stringInterpolation: localString) // GOOD + let _ = SQL(sql: localString) // GOOD + let sql2 = SQL(stringLiteral: "") + sql2.append(sql: localString) // GOOD +} + +func test(tableDefinition: TableDefinition) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + tableDefinition.column(sql: remoteString) // $ Alert + tableDefinition.column(sql: localString) // GOOD + + tableDefinition.check(sql: remoteString) // $ Alert + tableDefinition.check(sql: localString) // GOOD + + tableDefinition.constraint(sql: remoteString) // $ Alert + tableDefinition.constraint(sql: localString) // GOOD +} + +func test(tableAlteration: TableAlteration) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + tableAlteration.addColumn(sql: remoteString) // $ Alert + tableAlteration.addColumn(sql: localString) // GOOD +} + +func test(columnDefinition: ColumnDefinition) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = columnDefinition.check(sql: remoteString) // $ Alert + let _ = columnDefinition.defaults(sql: remoteString) // $ Alert + let _ = columnDefinition.generatedAs(sql: remoteString) // $ Alert + let _ = columnDefinition.generatedAs(sql: remoteString, .virtual) // $ Alert + + let _ = columnDefinition.check(sql: localString) // GOOD + let _ = columnDefinition.defaults(sql: localString) // GOOD + let _ = columnDefinition.generatedAs(sql: localString) // GOOD + let _ = columnDefinition.generatedAs(sql: localString, .virtual) // GOOD +} + +func testTableRecord() throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = TableRecord.select(sql: remoteString) // $ Alert + let _ = TableRecord.select(sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = TableRecord.select(sql: localString) // GOOD + let _ = TableRecord.select(sql: localString, arguments: StatementArguments()) // GOOD + + let _ = TableRecord.filter(sql: remoteString) // $ Alert + let _ = TableRecord.filter(sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = TableRecord.filter(sql: localString) // GOOD + let _ = TableRecord.filter(sql: localString, arguments: StatementArguments()) // GOOD + + let _ = TableRecord.order(sql: remoteString) // $ Alert + let _ = TableRecord.order(sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = TableRecord.order(sql: localString) // GOOD + let _ = TableRecord.order(sql: localString, arguments: StatementArguments()) // GOOD +} + +func test(statementCache: StatementCache) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = statementCache.statement(remoteString) // $ Alert + let _ = statementCache.statement(localString) // GOOD +} + +func test(row: Row, stmt: Statement) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + row.fetchCursor(stmt, sql: remoteString) // $ Alert + row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchCursor(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + row.fetchCursor(stmt, sql: localString) // GOOD + row.fetchCursor(stmt, sql: localString, arguments: StatementArguments()) // GOOD + row.fetchCursor(stmt, sql: localString, adapter: nil) // GOOD + row.fetchCursor(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + + row.fetchAll(stmt, sql: remoteString) // $ Alert + row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchAll(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + row.fetchAll(stmt, sql: localString) // GOOD + row.fetchAll(stmt, sql: localString, arguments: StatementArguments()) // GOOD + row.fetchAll(stmt, sql: localString, adapter: nil) // GOOD + row.fetchAll(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + + row.fetchOne(stmt, sql: remoteString) // $ Alert + row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchOne(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + row.fetchOne(stmt, sql: localString) // GOOD + row.fetchOne(stmt, sql: localString, arguments: StatementArguments()) // GOOD + row.fetchOne(stmt, sql: localString, adapter: nil) // GOOD + row.fetchOne(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + + row.fetchSet(stmt, sql: remoteString) // $ Alert + row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + row.fetchSet(stmt, sql: remoteString, adapter: nil) // $ Alert + row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + row.fetchSet(stmt, sql: localString) // GOOD + row.fetchSet(stmt, sql: localString, arguments: StatementArguments()) // GOOD + row.fetchSet(stmt, sql: localString, adapter: nil) // GOOD + row.fetchSet(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD +} + +func test(databaseValueConvertible: DatabaseValueConvertible, stmt: Statement) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + databaseValueConvertible.fetchCursor(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + databaseValueConvertible.fetchCursor(stmt, sql: localString) // GOOD + databaseValueConvertible.fetchCursor(stmt, sql: localString, arguments: StatementArguments()) // GOOD + databaseValueConvertible.fetchCursor(stmt, sql: localString, adapter: nil) // GOOD + databaseValueConvertible.fetchCursor(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + + databaseValueConvertible.fetchAll(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + databaseValueConvertible.fetchAll(stmt, sql: localString) // GOOD + databaseValueConvertible.fetchAll(stmt, sql: localString, arguments: StatementArguments()) // GOOD + databaseValueConvertible.fetchAll(stmt, sql: localString, adapter: nil) // GOOD + databaseValueConvertible.fetchAll(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + + databaseValueConvertible.fetchOne(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + databaseValueConvertible.fetchOne(stmt, sql: localString) // GOOD + databaseValueConvertible.fetchOne(stmt, sql: localString, arguments: StatementArguments()) // GOOD + databaseValueConvertible.fetchOne(stmt, sql: localString, adapter: nil) // GOOD + databaseValueConvertible.fetchOne(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD + + databaseValueConvertible.fetchSet(stmt, sql: remoteString) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: remoteString, adapter: nil) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // $ Alert + databaseValueConvertible.fetchSet(stmt, sql: localString) // GOOD + databaseValueConvertible.fetchSet(stmt, sql: localString, arguments: StatementArguments()) // GOOD + databaseValueConvertible.fetchSet(stmt, sql: localString, adapter: nil) // GOOD + databaseValueConvertible.fetchSet(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD +} + +func testSqlStatementCursor(database: Database) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments(), prepFlags: 0) // $ Alert + let _ = SQLStatementCursor(database: database, sql: localString, arguments: StatementArguments()) // GOOD + let _ = SQLStatementCursor(database: database, sql: localString, arguments: StatementArguments(), prepFlags: 0) // GOOD +} + +func testCommonTableExpression() throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + let _ = CommonTableExpression(named: "", sql: remoteString) // $ Alert + let _ = CommonTableExpression(named: "", sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString) // $ Alert + let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // $ Alert + let _ = CommonTableExpression(named: "", sql: localString) // GOOD + let _ = CommonTableExpression(named: "", sql: localString, arguments: StatementArguments()) // GOOD + let _ = CommonTableExpression(named: "", columns: [""], sql: localString) // GOOD + let _ = CommonTableExpression(named: "", columns: [""], sql: localString, arguments: StatementArguments()) // GOOD + let _ = CommonTableExpression(recursive: false, named: "", sql: localString) // GOOD + let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: localString) // GOOD + let _ = CommonTableExpression(recursive: false, named: "", sql: localString, arguments: StatementArguments()) // GOOD + let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: localString, arguments: StatementArguments()) // GOOD +} diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift new file mode 100644 index 000000000000..5973866fb250 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift @@ -0,0 +1,133 @@ + +// --- stubs --- + +struct URL +{ + init?(string: String) {} + init?(string: String, relativeTo: URL?) {} +} + +extension String { + init(contentsOf: URL) throws { + var data = "" + + // ... + + self.init(data) + } +} + +public protocol Binding {} + +extension String: Binding {} + +class Statement { + fileprivate let connection: Connection + + init(_ connection: Connection, _ SQL: String) throws { self.connection = connection} + + public func bind(_ values: Binding?...) -> Statement { return self } + public func bind(_ values: [Binding?]) -> Statement { return self } + public func bind(_ values: [String: Binding?]) -> Statement { return self } + + @discardableResult public func run(_ bindings: Binding?...) throws -> Statement { return self } + @discardableResult public func run(_ bindings: [Binding?]) throws -> Statement { return self } + @discardableResult public func run(_ bindings: [String: Binding?]) throws -> Statement { return self } + + public func scalar(_ bindings: Binding?...) throws -> Binding? { return nil } + public func scalar(_ bindings: [Binding?]) throws -> Binding? { return nil } + public func scalar(_ bindings: [String: Binding?]) throws -> Binding? { return nil } +} + +class Connection { + public func execute(_ SQL: String) throws { } + + public func prepare(_ statement: String, _ bindings: Binding?...) throws -> Statement { return try Statement(self, "") } + public func prepare(_ statement: String, _ bindings: [Binding?]) throws -> Statement { return try Statement(self, "") } + public func prepare(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement { return try Statement(self, "") } + + @discardableResult public func run(_ statement: String, _ bindings: Binding?...) throws -> Statement { return try Statement(self, "") } + @discardableResult public func run(_ statement: String, _ bindings: [Binding?]) throws -> Statement { return try Statement(self, "") } + @discardableResult public func run(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement { return try Statement(self, "") } + + public func scalar(_ statement: String, _ bindings: Binding?...) throws -> Binding? { return nil } + public func scalar(_ statement: String, _ bindings: [Binding?]) throws -> Binding? { return nil } + public func scalar(_ statement: String, _ bindings: [String: Binding?]) throws -> Binding? { return nil } +} + +// --- tests --- + +func test_sqlite_swift_api(db: Connection) throws { + let localString = "user" + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + let remoteNumber = Int(remoteString) ?? 0 + + let unsafeQuery1 = remoteString + let unsafeQuery2 = "SELECT * FROM users WHERE username='" + remoteString + "'" + let unsafeQuery3 = "SELECT * FROM users WHERE username='\(remoteString)'" + let safeQuery1 = "SELECT * FROM users WHERE username='\(localString)'" + let safeQuery2 = "SELECT * FROM users WHERE username='\(remoteNumber)'" + + // --- execute --- + + try db.execute(unsafeQuery1) // $ Alert + try db.execute(unsafeQuery2) // $ Alert + try db.execute(unsafeQuery3) // $ Alert + try db.execute(safeQuery1) // GOOD + try db.execute(safeQuery2) // GOOD + + // --- prepared statements --- + + let varQuery = "SELECT * FROM users WHERE username=?" + + let stmt1 = try db.prepare(unsafeQuery3) // $ Alert + try stmt1.run() + + let stmt2 = try db.prepare(varQuery, localString) // GOOD + try stmt2.run() + + let stmt3 = try db.prepare(varQuery, remoteString) // GOOD + try stmt3.run() + + let stmt4 = try Statement(db, localString) // GOOD + try stmt4.run() + + let stmt5 = try Statement(db, remoteString) // $ Alert + try stmt5.run() + + // --- more variants --- + + let stmt6 = try db.prepare(unsafeQuery1, "") // $ Alert + try stmt6.run() + + let stmt7 = try db.prepare(unsafeQuery1, [""]) // $ Alert + try stmt7.run() + + let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ Alert + try stmt8.run() + + try db.run(unsafeQuery1, "") // $ Alert + + try db.run(unsafeQuery1, [""]) // $ Alert + + try db.run(unsafeQuery1, ["username": ""]) // $ Alert + + try db.scalar(unsafeQuery1, "") // $ Alert + + try db.scalar(unsafeQuery1, [""]) // $ Alert + + try db.scalar(unsafeQuery1, ["username": ""]) // $ Alert + + let stmt9 = try db.prepare(varQuery) // GOOD + try stmt9.bind(remoteString) // GOOD + try stmt9.bind([remoteString]) // GOOD + try stmt9.bind(["username": remoteString]) // GOOD + try stmt9.run(remoteString) // GOOD + try stmt9.run([remoteString]) // GOOD + try stmt9.run(["username": remoteString]) // GOOD + try stmt9.scalar(remoteString) // GOOD + try stmt9.scalar([remoteString]) // GOOD + try stmt9.scalar(["username": remoteString]) // GOOD + + try Statement(db, remoteString).run() // $ Alert +} diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected new file mode 100644 index 000000000000..36ebcd04a6ee --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -0,0 +1,368 @@ +edges +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:106:41:106:41 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:108:41:108:41 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:111:43:111:43 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:114:51:114:51 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:117:27:117:27 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:119:27:119:27 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:122:41:122:41 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:124:41:124:41 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:132:39:132:39 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:135:46:135:46 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:138:56:138:56 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:141:45:141:45 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:144:29:144:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:145:29:145:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:146:29:146:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:147:29:147:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:148:29:148:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:149:29:149:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:150:29:150:29 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:151:29:151:29 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:166:32:166:32 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:167:39:167:39 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:168:49:168:49 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:169:38:169:38 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:170:22:170:22 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:172:22:172:22 | remoteString | provenance | | +| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:187:33:187:33 | remoteString | provenance | | +| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:190:32:190:32 | remoteString | provenance | | +| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:193:37:193:37 | remoteString | provenance | | +| GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | GRDB.swift:201:36:201:36 | remoteString | provenance | | +| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:209:41:209:41 | remoteString | provenance | | +| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:210:44:210:44 | remoteString | provenance | | +| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:211:47:211:47 | remoteString | provenance | | +| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:212:47:212:47 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:224:37:224:37 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:225:37:225:37 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:229:37:229:37 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:230:37:230:37 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:234:36:234:36 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:235:36:235:36 | remoteString | provenance | | +| GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | GRDB.swift:244:38:244:38 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:252:32:252:32 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:253:32:253:32 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:254:32:254:32 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:255:32:255:32 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:261:29:261:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:262:29:262:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:263:29:263:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:264:29:264:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:270:29:270:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:271:29:271:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:272:29:272:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:273:29:273:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:279:29:279:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:280:29:280:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:281:29:281:29 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:282:29:282:29 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:293:53:293:53 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:294:53:294:53 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:295:53:295:53 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:296:53:296:53 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:302:50:302:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:303:50:303:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:304:50:304:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:305:50:305:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:311:50:311:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:312:50:312:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:313:50:313:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:314:50:314:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:320:50:320:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:321:50:321:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:322:50:322:50 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:323:50:323:50 | remoteString | provenance | | +| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:334:57:334:57 | remoteString | provenance | | +| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:335:57:335:57 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:344:51:344:51 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:345:51:345:51 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:346:66:346:66 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:347:66:347:66 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:348:69:348:69 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:349:84:349:84 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:350:69:350:69 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:351:84:351:84 | remoteString | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:73:17:73:17 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:74:17:74:17 | unsafeQuery2 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:75:17:75:17 | unsafeQuery3 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:83:29:83:29 | unsafeQuery3 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:95:32:95:32 | remoteString | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:100:29:100:29 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:103:29:103:29 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:106:29:106:29 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:109:13:109:13 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:111:13:111:13 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:113:13:113:13 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:115:16:115:16 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:117:16:117:16 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:119:16:119:16 | unsafeQuery1 | provenance | | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:132:20:132:20 | remoteString | provenance | | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:50:22:50:22 | remoteString | provenance | | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:52:14:52:14 | remoteString | provenance | | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:53:14:53:14 | remoteString | provenance | | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:54:31:54:31 | remoteString | provenance | | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:55:14:55:14 | remoteString | provenance | | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:57:16:57:16 | remoteString | provenance | | +| other.swift:54:31:54:31 | remoteString | other.swift:54:14:54:43 | call to NSString.init(string:) | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | provenance | | +| sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | sqlite3_c_api.swift:190:2:190:2 | data | provenance | | +| sqlite3_c_api.swift:190:2:190:2 | data | sqlite3_c_api.swift:190:21:190:21 | [post] buffer | provenance | | +| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:194:28:194:28 | buffer | provenance | | +| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:202:31:202:31 | buffer | provenance | | +| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:210:31:210:31 | buffer | provenance | | +nodes +| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:106:41:106:41 | remoteString | semmle.label | remoteString | +| GRDB.swift:108:41:108:41 | remoteString | semmle.label | remoteString | +| GRDB.swift:111:43:111:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:114:51:114:51 | remoteString | semmle.label | remoteString | +| GRDB.swift:117:27:117:27 | remoteString | semmle.label | remoteString | +| GRDB.swift:119:27:119:27 | remoteString | semmle.label | remoteString | +| GRDB.swift:122:41:122:41 | remoteString | semmle.label | remoteString | +| GRDB.swift:124:41:124:41 | remoteString | semmle.label | remoteString | +| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:132:39:132:39 | remoteString | semmle.label | remoteString | +| GRDB.swift:135:46:135:46 | remoteString | semmle.label | remoteString | +| GRDB.swift:138:56:138:56 | remoteString | semmle.label | remoteString | +| GRDB.swift:141:45:141:45 | remoteString | semmle.label | remoteString | +| GRDB.swift:144:29:144:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:145:29:145:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:146:29:146:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:147:29:147:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:148:29:148:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:149:29:149:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:150:29:150:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:151:29:151:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:166:32:166:32 | remoteString | semmle.label | remoteString | +| GRDB.swift:167:39:167:39 | remoteString | semmle.label | remoteString | +| GRDB.swift:168:49:168:49 | remoteString | semmle.label | remoteString | +| GRDB.swift:169:38:169:38 | remoteString | semmle.label | remoteString | +| GRDB.swift:170:22:170:22 | remoteString | semmle.label | remoteString | +| GRDB.swift:172:22:172:22 | remoteString | semmle.label | remoteString | +| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:187:33:187:33 | remoteString | semmle.label | remoteString | +| GRDB.swift:190:32:190:32 | remoteString | semmle.label | remoteString | +| GRDB.swift:193:37:193:37 | remoteString | semmle.label | remoteString | +| GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:201:36:201:36 | remoteString | semmle.label | remoteString | +| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:209:41:209:41 | remoteString | semmle.label | remoteString | +| GRDB.swift:210:44:210:44 | remoteString | semmle.label | remoteString | +| GRDB.swift:211:47:211:47 | remoteString | semmle.label | remoteString | +| GRDB.swift:212:47:212:47 | remoteString | semmle.label | remoteString | +| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:224:37:224:37 | remoteString | semmle.label | remoteString | +| GRDB.swift:225:37:225:37 | remoteString | semmle.label | remoteString | +| GRDB.swift:229:37:229:37 | remoteString | semmle.label | remoteString | +| GRDB.swift:230:37:230:37 | remoteString | semmle.label | remoteString | +| GRDB.swift:234:36:234:36 | remoteString | semmle.label | remoteString | +| GRDB.swift:235:36:235:36 | remoteString | semmle.label | remoteString | +| GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:244:38:244:38 | remoteString | semmle.label | remoteString | +| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:252:32:252:32 | remoteString | semmle.label | remoteString | +| GRDB.swift:253:32:253:32 | remoteString | semmle.label | remoteString | +| GRDB.swift:254:32:254:32 | remoteString | semmle.label | remoteString | +| GRDB.swift:255:32:255:32 | remoteString | semmle.label | remoteString | +| GRDB.swift:261:29:261:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:262:29:262:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:263:29:263:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:264:29:264:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:270:29:270:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:271:29:271:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:272:29:272:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:273:29:273:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:279:29:279:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:280:29:280:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:281:29:281:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:282:29:282:29 | remoteString | semmle.label | remoteString | +| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:293:53:293:53 | remoteString | semmle.label | remoteString | +| GRDB.swift:294:53:294:53 | remoteString | semmle.label | remoteString | +| GRDB.swift:295:53:295:53 | remoteString | semmle.label | remoteString | +| GRDB.swift:296:53:296:53 | remoteString | semmle.label | remoteString | +| GRDB.swift:302:50:302:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:303:50:303:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:304:50:304:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:305:50:305:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:311:50:311:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:312:50:312:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:313:50:313:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:314:50:314:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:320:50:320:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:321:50:321:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:322:50:322:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:323:50:323:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:334:57:334:57 | remoteString | semmle.label | remoteString | +| GRDB.swift:335:57:335:57 | remoteString | semmle.label | remoteString | +| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| GRDB.swift:344:51:344:51 | remoteString | semmle.label | remoteString | +| GRDB.swift:345:51:345:51 | remoteString | semmle.label | remoteString | +| GRDB.swift:346:66:346:66 | remoteString | semmle.label | remoteString | +| GRDB.swift:347:66:347:66 | remoteString | semmle.label | remoteString | +| GRDB.swift:348:69:348:69 | remoteString | semmle.label | remoteString | +| GRDB.swift:349:84:349:84 | remoteString | semmle.label | remoteString | +| GRDB.swift:350:69:350:69 | remoteString | semmle.label | remoteString | +| GRDB.swift:351:84:351:84 | remoteString | semmle.label | remoteString | +| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| SQLite.swift:73:17:73:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:74:17:74:17 | unsafeQuery2 | semmle.label | unsafeQuery2 | +| SQLite.swift:75:17:75:17 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| SQLite.swift:83:29:83:29 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| SQLite.swift:95:32:95:32 | remoteString | semmle.label | remoteString | +| SQLite.swift:100:29:100:29 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:103:29:103:29 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:106:29:106:29 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:109:13:109:13 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:111:13:111:13 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:113:13:113:13 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:115:16:115:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:117:16:117:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:119:16:119:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:132:20:132:20 | remoteString | semmle.label | remoteString | +| other.swift:46:25:46:79 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| other.swift:50:22:50:22 | remoteString | semmle.label | remoteString | +| other.swift:52:14:52:14 | remoteString | semmle.label | remoteString | +| other.swift:53:14:53:14 | remoteString | semmle.label | remoteString | +| other.swift:54:14:54:43 | call to NSString.init(string:) | semmle.label | call to NSString.init(string:) | +| other.swift:54:31:54:31 | remoteString | semmle.label | remoteString | +| other.swift:55:14:55:14 | remoteString | semmle.label | remoteString | +| other.swift:57:16:57:16 | remoteString | semmle.label | remoteString | +| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | +| sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | semmle.label | unsafeQuery2 | +| sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | semmle.label | call to data(using:allowLossyConversion:) | +| sqlite3_c_api.swift:190:2:190:2 | data | semmle.label | data | +| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | semmle.label | [post] buffer | +| sqlite3_c_api.swift:194:28:194:28 | buffer | semmle.label | buffer | +| sqlite3_c_api.swift:202:31:202:31 | buffer | semmle.label | buffer | +| sqlite3_c_api.swift:210:31:210:31 | buffer | semmle.label | buffer | +subpaths +#select +| GRDB.swift:106:41:106:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:106:41:106:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:108:41:108:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:108:41:108:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:111:43:111:43 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:111:43:111:43 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:114:51:114:51 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:114:51:114:51 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:117:27:117:27 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:117:27:117:27 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:119:27:119:27 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:119:27:119:27 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:122:41:122:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:122:41:122:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:124:41:124:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:124:41:124:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:132:39:132:39 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:132:39:132:39 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:135:46:135:46 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:135:46:135:46 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:138:56:138:56 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:138:56:138:56 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:141:45:141:45 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:141:45:141:45 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:144:29:144:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:144:29:144:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:145:29:145:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:145:29:145:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:146:29:146:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:146:29:146:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:147:29:147:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:147:29:147:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:148:29:148:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:148:29:148:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:149:29:149:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:149:29:149:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:150:29:150:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:150:29:150:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:151:29:151:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:151:29:151:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:166:32:166:32 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:166:32:166:32 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:167:39:167:39 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:167:39:167:39 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:168:49:168:49 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:168:49:168:49 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:169:38:169:38 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:169:38:169:38 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:170:22:170:22 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:170:22:170:22 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:172:22:172:22 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:172:22:172:22 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:187:33:187:33 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:187:33:187:33 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:190:32:190:32 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:190:32:190:32 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:193:37:193:37 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:193:37:193:37 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:201:36:201:36 | remoteString | GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | GRDB.swift:201:36:201:36 | remoteString | This query depends on a $@. | GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:209:41:209:41 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:209:41:209:41 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:210:44:210:44 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:210:44:210:44 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:211:47:211:47 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:211:47:211:47 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:212:47:212:47 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:212:47:212:47 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:224:37:224:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:224:37:224:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:225:37:225:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:225:37:225:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:229:37:229:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:229:37:229:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:230:37:230:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:230:37:230:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:234:36:234:36 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:234:36:234:36 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:235:36:235:36 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:235:36:235:36 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:244:38:244:38 | remoteString | GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | GRDB.swift:244:38:244:38 | remoteString | This query depends on a $@. | GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:252:32:252:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:252:32:252:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:253:32:253:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:253:32:253:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:254:32:254:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:254:32:254:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:255:32:255:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:255:32:255:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:261:29:261:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:261:29:261:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:262:29:262:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:262:29:262:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:263:29:263:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:263:29:263:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:264:29:264:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:264:29:264:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:270:29:270:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:270:29:270:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:271:29:271:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:271:29:271:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:272:29:272:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:272:29:272:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:273:29:273:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:273:29:273:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:279:29:279:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:279:29:279:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:280:29:280:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:280:29:280:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:281:29:281:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:281:29:281:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:282:29:282:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:282:29:282:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:293:53:293:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:293:53:293:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:294:53:294:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:294:53:294:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:295:53:295:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:295:53:295:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:296:53:296:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:296:53:296:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:302:50:302:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:302:50:302:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:303:50:303:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:303:50:303:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:304:50:304:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:304:50:304:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:305:50:305:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:305:50:305:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:311:50:311:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:311:50:311:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:312:50:312:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:312:50:312:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:313:50:313:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:313:50:313:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:314:50:314:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:314:50:314:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:320:50:320:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:320:50:320:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:321:50:321:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:321:50:321:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:322:50:322:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:322:50:322:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:323:50:323:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:323:50:323:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:334:57:334:57 | remoteString | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:334:57:334:57 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:335:57:335:57 | remoteString | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:335:57:335:57 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:344:51:344:51 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:344:51:344:51 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:345:51:345:51 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:345:51:345:51 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:346:66:346:66 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:346:66:346:66 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:347:66:347:66 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:347:66:347:66 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:348:69:348:69 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:348:69:348:69 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:349:84:349:84 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:349:84:349:84 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:350:69:350:69 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:350:69:350:69 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| GRDB.swift:351:84:351:84 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:351:84:351:84 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:73:17:73:17 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:73:17:73:17 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:74:17:74:17 | unsafeQuery2 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:74:17:74:17 | unsafeQuery2 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:75:17:75:17 | unsafeQuery3 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:75:17:75:17 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:83:29:83:29 | unsafeQuery3 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:83:29:83:29 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:95:32:95:32 | remoteString | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:95:32:95:32 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:100:29:100:29 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:100:29:100:29 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:103:29:103:29 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:103:29:103:29 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:106:29:106:29 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:106:29:106:29 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:109:13:109:13 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:109:13:109:13 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:111:13:111:13 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:111:13:111:13 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:113:13:113:13 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:113:13:113:13 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:115:16:115:16 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:115:16:115:16 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:117:16:117:16 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:117:16:117:16 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:119:16:119:16 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:119:16:119:16 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| SQLite.swift:132:20:132:20 | remoteString | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:132:20:132:20 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | +| other.swift:50:22:50:22 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:50:22:50:22 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | +| other.swift:52:14:52:14 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:52:14:52:14 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | +| other.swift:53:14:53:14 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:53:14:53:14 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | +| other.swift:54:14:54:43 | call to NSString.init(string:) | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:54:14:54:43 | call to NSString.init(string:) | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | +| other.swift:55:14:55:14 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:55:14:55:14 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | +| other.swift:57:16:57:16 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:57:16:57:16 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:194:28:194:28 | buffer | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:194:28:194:28 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:202:31:202:31 | buffer | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:202:31:202:31 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | +| sqlite3_c_api.swift:210:31:210:31 | buffer | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:210:31:210:31 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref new file mode 100644 index 000000000000..654631d8a094 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref @@ -0,0 +1,2 @@ +query: queries/Security/CWE-089/SqlInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift new file mode 100644 index 000000000000..0974d03937e9 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift @@ -0,0 +1,64 @@ + +// --- stubs --- + +struct URL +{ + init?(string: String) {} +} + +extension String { + init(contentsOf: URL) throws { + self.init("") + } +} + +class NSObject { +} + +class NSString : NSObject { + init(string: String) { } +} + +class Sql { +} + +class MyDatabase { + init(sql code: String? = nil) { } + + func execute1(_ sql: String) { } + func execute2(_ sql: String?) { } + func execute3(_ sql: NSString) { } + func execute4(_ sql: Sql) { } + + func query(sql: String) { } + func query(sqlLiteral: String) { } + func query(sqlStatement: String) { } + func query(sqliteStatement: String) { } + + // non-examples + func doSomething(sqlIndex: Int) { } + func doSomething(sqliteContext: Sql) { } +} + +// --- tests --- + +func test_heuristic(db: MyDatabase) throws { + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + + _ = MyDatabase() // GOOD + _ = MyDatabase(sql: "some_fixed_sql") // GOOD + _ = MyDatabase(sql: remoteString) // $ Alert + + db.execute1(remoteString) // $ Alert + db.execute2(remoteString) // $ Alert + db.execute3(NSString(string: remoteString)) // $ Alert + db.execute4(remoteString as! Sql) // $ Alert + + db.query(sql: remoteString) // $ Alert + db.query(sqlLiteral: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] + db.query(sqlStatement: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] + db.query(sqliteStatement: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] + + db.doSomething(sqlIndex: Int(remoteString) ?? 0) // GOOD + db.doSomething(sqliteContext: remoteString as! Sql) // GOOD +} diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift new file mode 100644 index 000000000000..b4e7451b9163 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift @@ -0,0 +1,215 @@ + +// --- stubs --- + +struct URL +{ + init?(string: String) {} + init?(string: String, relativeTo: URL?) {} +} +protocol DataProtocol { } +struct Data : DataProtocol { + init(_ elements: S) { count = 0 } + + var count: Int + + func copyBytes(to pointer: UnsafeMutablePointer, count: Int) {} +} + +extension String { + init(contentsOf: URL) throws { + var data = "" + + // ... + + self.init(data) + } + + struct Encoding { + var rawValue: UInt + + init(rawValue: UInt) { + self.rawValue = rawValue + } + + static let utf16 = Encoding(rawValue: 1) + } + + func data(using encoding: String.Encoding, allowLossyConversion: Bool = false) -> Data? { return nil } +} + +var SQLITE_OK : Int32 = 0 +var SQLITE_TRANSIENT : (@convention(c) (UnsafeMutableRawPointer?) -> Void)? + +func sqlite3_exec( + _ _: OpaquePointer?, + _ sql: UnsafePointer?, + _ callback: (@convention(c) (UnsafeMutableRawPointer?, Int32, UnsafeMutablePointer?>?, UnsafeMutablePointer?>?) -> Int32)?, + _ _: UnsafeMutableRawPointer?, + _ errmsg: UnsafeMutablePointer?>? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_prepare( + _ db: OpaquePointer?, + _ zSql: UnsafePointer?, + _ nByte: Int32, + _ ppStmt: UnsafeMutablePointer?, + _ pzTail: UnsafeMutablePointer?>? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_prepare_v2( + _ db: OpaquePointer?, + _ zSql: UnsafePointer?, + _ nByte: Int32, + _ ppStmt: UnsafeMutablePointer?, + _ pzTail: UnsafeMutablePointer?>? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_prepare_v3( + _ db: OpaquePointer?, + _ zSql: UnsafePointer?, + _ nByte: Int32, + _ prepFlags: UInt32, + _ ppStmt: UnsafeMutablePointer?, + _ pzTail: UnsafeMutablePointer?>? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_prepare16( + _ db: OpaquePointer?, + _ zSql: UnsafeRawPointer?, + _ nByte: Int32, + _ ppStmt: UnsafeMutablePointer?, + _ pzTail: UnsafeMutablePointer? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_prepare16_v2( + _ db: OpaquePointer?, + _ zSql: UnsafeRawPointer?, + _ nByte: Int32, + _ ppStmt: UnsafeMutablePointer?, + _ pzTail: UnsafeMutablePointer? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_prepare16_v3( + _ db: OpaquePointer?, + _ zSql: UnsafeRawPointer?, + _ nByte: Int32, + _ prepFlags: UInt32, + _ ppStmt: UnsafeMutablePointer?, + _ pzTail: UnsafeMutablePointer? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_bind_text( + _ _: OpaquePointer?, + _ _: Int32, + _ _: UnsafePointer?, + _ _: Int32, + _ callback: (@convention(c) (UnsafeMutableRawPointer?) -> Void)? + ) -> Int32 { return SQLITE_OK } +// (many other `sqlite3_bind_*` functions exist) + +func sqlite3_step( + _ _: OpaquePointer? + ) -> Int32 { return SQLITE_OK } + +func sqlite3_finalize( + _ pStmt: OpaquePointer? + ) -> Int32 { return SQLITE_OK } + +// --- tests --- + +func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) { + let localString = "user" + let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) // $ Source + let remoteNumber = Int(remoteString) ?? 0 + + let unsafeQuery1 = remoteString + let unsafeQuery2 = "SELECT * FROM users WHERE username='" + remoteString + "'" + let unsafeQuery3 = "SELECT * FROM users WHERE username='\(remoteString)'" + let safeQuery1 = "SELECT * FROM users WHERE username='\(localString)'" + let safeQuery2 = "SELECT * FROM users WHERE username='\(remoteNumber)'" + + // --- exec --- + + let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // $ Alert + let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // $ Alert + let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // $ Alert + let result4 = sqlite3_exec(db, safeQuery1, nil, nil, nil) // GOOD + let result5 = sqlite3_exec(db, safeQuery2, nil, nil, nil) // GOOD + + // --- prepared statements --- + + let varQuery = "SELECT * FROM users WHERE username=?" + + var stmt1: OpaquePointer? + + if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // $ Alert + let result = sqlite3_step(stmt1) + // ... + } + sqlite3_finalize(stmt1) + + var stmt2: OpaquePointer? + + if (sqlite3_prepare(db, varQuery, -1, &stmt2, nil) == SQLITE_OK) { // GOOD + if (sqlite3_bind_text(stmt2, 1, localString, -1, SQLITE_TRANSIENT) == SQLITE_OK) { // GOOD + let result = sqlite3_step(stmt2) + // ... + } + } + sqlite3_finalize(stmt2) + + var stmt3: OpaquePointer? + + if (sqlite3_prepare(db, varQuery, -1, &stmt3, nil) == SQLITE_OK) { // GOOD + if (sqlite3_bind_text(stmt3, 1, remoteString, -1, SQLITE_TRANSIENT) == SQLITE_OK) { // GOOD + let result = sqlite3_step(stmt3) + // ... + } + } + sqlite3_finalize(stmt3) + + // --- variant 'prepare' functions --- + + var stmt4: OpaquePointer? + + if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // $ Alert + let result = sqlite3_step(stmt4) + // ... + } + sqlite3_finalize(stmt4) + + var stmt5: OpaquePointer? + + if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // $ Alert + let result = sqlite3_step(stmt5) + // ... + } + sqlite3_finalize(stmt5) + + let data = unsafeQuery3.data(using:String.Encoding.utf16)! + data.copyBytes(to: buffer, count: data.count) + + var stmt6: OpaquePointer? + + if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // $ Alert + let result = sqlite3_step(stmt6) + // ... + } + sqlite3_finalize(stmt6) + + var stmt7: OpaquePointer? + + if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // $ Alert + let result = sqlite3_step(stmt7) + // ... + } + sqlite3_finalize(stmt7) + + var stmt8: OpaquePointer? + + if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // $ Alert + let result = sqlite3_step(stmt8) + // ... + } + sqlite3_finalize(stmt8) +} From 14f7a14333b1bfbaabea9163911c15507b24d988 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:37:54 +0000 Subject: [PATCH 02/13] unified: Collect original Swift SQL injection configuration Copy the original data flow configuration and query-specific models into the query, preserving declarations and provenance before adapting unified interfaces. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../queries/security/CWE-089/SqlInjection.ql | 228 +++++++++++++++++- 1 file changed, 227 insertions(+), 1 deletion(-) diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql index 8c78cad7044b..1d0c146840c0 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -12,7 +12,233 @@ import swift import codeql.swift.dataflow.DataFlow -import codeql.swift.security.SqlInjectionQuery +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import codeql.swift.dataflow.ExternalFlow + +// Copied from swift/ql/lib/codeql/swift/security/SqlInjectionExtensions.qll. +/** + * Provides classes and predicates for reasoning about database + * queries built from user-controlled sources (that is, SQL injection + * vulnerabilities). + */ + +/** + * A dataflow sink for SQL injection vulnerabilities. + */ +abstract class SqlInjectionSink extends DataFlow::Node { } + +/** + * A barrier for SQL injection vulnerabilities. + */ +abstract class SqlInjectionBarrier extends DataFlow::Node { } + +/** + * A unit class for adding additional flow steps. + */ +class SqlInjectionAdditionalFlowStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a flow + * step for paths related to SQL injection vulnerabilities. + */ + abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); +} + +/** + * A default SQL injection sink for the sqlite3 C API. + */ +private class CApiDefaultSqlInjectionSink extends SqlInjectionSink { + CApiDefaultSqlInjectionSink() { + // `sqlite3_exec` and variants of `sqlite3_prepare`. + exists(CallExpr call | + call.getStaticTarget() + .(FreeFunction) + .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 default SQL injection sink for the `SQLite.swift` library. + */ +private class SQLiteSwiftDefaultSqlInjectionSink extends SqlInjectionSink { + SQLiteSwiftDefaultSqlInjectionSink() { + // Variants of `Connection.execute`, `connection.prepare` and `connection.scalar`. + exists(CallExpr call | + call.getStaticTarget() + .(Method) + .hasQualifiedName("Connection", + ["execute(_:)", "prepare(_:_:)", "run(_:_:)", "scalar(_:_:)"]) and + call.getArgument(0).getExpr() = this.asExpr() + ) + or + // String argument to the `Statement` initializer. + exists(CallExpr call | + call.getStaticTarget().(Method).hasQualifiedName("Statement", "init(_:_:)") and + call.getArgument(1).getExpr() = this.asExpr() + ) + } +} + +/** + * A default SQL injection sink for the GRDB library. + */ +private class GrdbDefaultSqlInjectionSink extends SqlInjectionSink { + GrdbDefaultSqlInjectionSink() { + exists(CallExpr call, Method 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, Method 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, Method method | + call.getStaticTarget() = method and + call.getArgument(3).getExpr() = this.asExpr() + | + method + .hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)") + ) + } +} + +/** + * Holds if `f`, `ix` describe `pd` and `pd` is a parameter that might be + * executed as SQL. + */ +pragma[noinline] +predicate sqlLikeHeuristic(Callable f, int ix, ParamDecl pd) { + pd.getName() = "sql" and + pd = f.getParam(ix) +} + +/** + * An SQL injection sink that is determined by imprecise methods. + */ +private class HeuristicSqlInjectionSink extends SqlInjectionSink { + HeuristicSqlInjectionSink() { + // by parameter name + exists(CallExpr ce, Callable f, int ix | + sqlLikeHeuristic(f, ix, _) and + f = ce.getStaticTarget() and + this.asExpr() = ce.getArgument(ix).getExpr() + ) + or + // by argument name + exists(Argument a | + a.getLabel() = "sql" and + this.asExpr() = a.getExpr() + ) + } +} + +/** + * A sink defined in a CSV model. + */ +private class DefaultSqlInjectionSink extends SqlInjectionSink { + DefaultSqlInjectionSink() { sinkNode(this, "sql-injection") } +} + +/** + * A barrier for SQL injection. + */ +private class SqlInjectionDefaultBarrier extends SqlInjectionBarrier { + SqlInjectionDefaultBarrier() { + // any numeric type + this.asExpr().getType().getUnderlyingType().getABaseType*().getName() = + ["Numeric", "SignedInteger", "UnsignedInteger"] + } +} + +// Copied from swift/ql/lib/codeql/swift/security/SqlInjectionQuery.qll. +/** + * Provides a taint-tracking configuration for reasoning about database + * queries built from user-controlled sources (that is, SQL injection + * vulnerabilities). + */ + +/** + * A taint configuration for tainted data that reaches a SQL sink. + */ +module SqlInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof FlowSource } + + predicate isSink(DataFlow::Node node) { node instanceof SqlInjectionSink } + + predicate isBarrier(DataFlow::Node barrier) { barrier instanceof SqlInjectionBarrier } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + any(SqlInjectionAdditionalFlowStep s).step(nodeFrom, nodeTo) + } + + predicate observeDiffInformedIncrementalMode() { any() } +} + +/** + * Detect taint flow of tainted data that reaches a SQL sink. + */ +module SqlInjectionFlow = TaintTracking::Global; + import SqlInjectionFlow::PathGraph from SqlInjectionFlow::PathNode sourceNode, SqlInjectionFlow::PathNode sinkNode From 53152a364c1061fa3a989e3db1b27280870361de Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:39:17 +0000 Subject: [PATCH 03/13] unified: Port SQL injection query with annotated test baseline Use unified taint tracking, model-backed sources and sinks, and the sql argument-label heuristic. Retain all original cases and mark unsupported detections as missing. Parameter-name heuristics, SQL sink models and numeric barriers remain follow-up work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../queries/security/CWE-089/SqlInjection.ql | 229 +------ .../security/CWE-089/SqlInjection/GRDB.swift | 20 +- .../CWE-089/SqlInjection/SQLite.swift | 32 +- .../SqlInjection/SqlInjection.expected | 630 ++++++++---------- .../CWE-089/SqlInjection/SqlInjection.qlref | 5 +- .../security/CWE-089/SqlInjection/other.swift | 8 +- .../CWE-089/SqlInjection/sqlite3_c_api.swift | 20 +- 7 files changed, 322 insertions(+), 622 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql index 1d0c146840c0..da771f313621 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -5,238 +5,37 @@ * @problem.severity error * @security-severity 8.8 * @precision high - * @id swift/sql-injection + * @id unified/swift/sql-injection * @tags security * external/cwe/cwe-089 */ -import swift -import codeql.swift.dataflow.DataFlow -import codeql.swift.dataflow.TaintTracking -import codeql.swift.dataflow.FlowSources -import codeql.swift.dataflow.ExternalFlow +import unified -// Copied from swift/ql/lib/codeql/swift/security/SqlInjectionExtensions.qll. /** - * Provides classes and predicates for reasoning about database - * queries built from user-controlled sources (that is, SQL injection - * vulnerabilities). - */ - -/** - * A dataflow sink for SQL injection vulnerabilities. - */ -abstract class SqlInjectionSink extends DataFlow::Node { } - -/** - * A barrier for SQL injection vulnerabilities. - */ -abstract class SqlInjectionBarrier extends DataFlow::Node { } - -/** - * A unit class for adding additional flow steps. - */ -class SqlInjectionAdditionalFlowStep extends Unit { - /** - * Holds if the step from `node1` to `node2` should be considered a flow - * step for paths related to SQL injection vulnerabilities. - */ - abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo); -} - -/** - * A default SQL injection sink for the sqlite3 C API. - */ -private class CApiDefaultSqlInjectionSink extends SqlInjectionSink { - CApiDefaultSqlInjectionSink() { - // `sqlite3_exec` and variants of `sqlite3_prepare`. - exists(CallExpr call | - call.getStaticTarget() - .(FreeFunction) - .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 default SQL injection sink for the `SQLite.swift` library. - */ -private class SQLiteSwiftDefaultSqlInjectionSink extends SqlInjectionSink { - SQLiteSwiftDefaultSqlInjectionSink() { - // Variants of `Connection.execute`, `connection.prepare` and `connection.scalar`. - exists(CallExpr call | - call.getStaticTarget() - .(Method) - .hasQualifiedName("Connection", - ["execute(_:)", "prepare(_:_:)", "run(_:_:)", "scalar(_:_:)"]) and - call.getArgument(0).getExpr() = this.asExpr() - ) - or - // String argument to the `Statement` initializer. - exists(CallExpr call | - call.getStaticTarget().(Method).hasQualifiedName("Statement", "init(_:_:)") and - call.getArgument(1).getExpr() = this.asExpr() - ) - } -} - -/** - * A default SQL injection sink for the GRDB library. - */ -private class GrdbDefaultSqlInjectionSink extends SqlInjectionSink { - GrdbDefaultSqlInjectionSink() { - exists(CallExpr call, Method 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, Method 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, Method method | - call.getStaticTarget() = method and - call.getArgument(3).getExpr() = this.asExpr() - | - method - .hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)") - ) - } -} - -/** - * Holds if `f`, `ix` describe `pd` and `pd` is a parameter that might be - * executed as SQL. + * A taint configuration for tainted data that reaches a SQL sink. */ -pragma[noinline] -predicate sqlLikeHeuristic(Callable f, int ix, ParamDecl pd) { - pd.getName() = "sql" and - pd = f.getParam(ix) -} +module SqlInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { Models::isSource(node, _) } -/** - * An SQL injection sink that is determined by imprecise methods. - */ -private class HeuristicSqlInjectionSink extends SqlInjectionSink { - HeuristicSqlInjectionSink() { - // by parameter name - exists(CallExpr ce, Callable f, int ix | - sqlLikeHeuristic(f, ix, _) and - f = ce.getStaticTarget() and - this.asExpr() = ce.getArgument(ix).getExpr() - ) + predicate isSink(DataFlow::Node node) { + Models::isSink(node, "sql-injection") or - // by argument name - exists(Argument a | - a.getLabel() = "sql" and - this.asExpr() = a.getExpr() + exists(Argument arg | + arg.getName() = "sql" and + node.asExpr() = arg.getValue() ) + // TODO: Port the parameter-name heuristic from original Swift's SqlInjectionExtensions.qll. } -} - -/** - * A sink defined in a CSV model. - */ -private class DefaultSqlInjectionSink extends SqlInjectionSink { - DefaultSqlInjectionSink() { sinkNode(this, "sql-injection") } -} - -/** - * A barrier for SQL injection. - */ -private class SqlInjectionDefaultBarrier extends SqlInjectionBarrier { - SqlInjectionDefaultBarrier() { - // any numeric type - this.asExpr().getType().getUnderlyingType().getABaseType*().getName() = - ["Numeric", "SignedInteger", "UnsignedInteger"] - } -} - -// Copied from swift/ql/lib/codeql/swift/security/SqlInjectionQuery.qll. -/** - * Provides a taint-tracking configuration for reasoning about database - * queries built from user-controlled sources (that is, SQL injection - * vulnerabilities). - */ - -/** - * A taint configuration for tainted data that reaches a SQL sink. - */ -module SqlInjectionConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node node) { node instanceof FlowSource } - predicate isSink(DataFlow::Node node) { node instanceof SqlInjectionSink } - - predicate isBarrier(DataFlow::Node barrier) { barrier instanceof SqlInjectionBarrier } - - predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - any(SqlInjectionAdditionalFlowStep s).step(nodeFrom, nodeTo) + predicate isBarrier(DataFlow::Node node) { + // TODO: Port the numeric-type barrier from original Swift's SqlInjectionExtensions.qll. + none() } predicate observeDiffInformedIncrementalMode() { any() } } -/** - * Detect taint flow of tainted data that reaches a SQL sink. - */ module SqlInjectionFlow = TaintTracking::Global; import SqlInjectionFlow::PathGraph diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift index 3bdffaa272b4..0ae182a61e0a 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift @@ -129,16 +129,16 @@ func testSqlRequest() throws { let localString = "user" let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQLRequest(stringLiteral: remoteString) // $ Alert + let _ = SQLRequest(stringLiteral: remoteString) // $ MISSING: Alert let _ = SQLRequest(stringLiteral: localString) // GOOD - let _ = SQLRequest(unicodeScalarLiteral: remoteString) // $ Alert + let _ = SQLRequest(unicodeScalarLiteral: remoteString) // $ MISSING: Alert let _ = SQLRequest(unicodeScalarLiteral: localString) // GOOD - let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // $ Alert + let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // $ MISSING: Alert let _ = SQLRequest(extendedGraphemeClusterLiteral: localString) // GOOD - let _ = SQLRequest(stringInterpolation: remoteString) // $ Alert + let _ = SQLRequest(stringInterpolation: remoteString) // $ MISSING: Alert let _ = SQLRequest(stringInterpolation: localString) // GOOD let _ = SQLRequest(sql: remoteString) // $ Alert @@ -163,10 +163,10 @@ func testSql() throws { let localString = "user" let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQL(stringLiteral: remoteString) // $ Alert - let _ = SQL(unicodeScalarLiteral: remoteString) // $ Alert - let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // $ Alert - let _ = SQL(stringInterpolation: remoteString) // $ Alert + let _ = SQL(stringLiteral: remoteString) // $ MISSING: Alert + let _ = SQL(unicodeScalarLiteral: remoteString) // $ MISSING: Alert + let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // $ MISSING: Alert + let _ = SQL(stringInterpolation: remoteString) // $ MISSING: Alert let _ = SQL(sql: remoteString) // $ Alert let sql1 = SQL(stringLiteral: "") sql1.append(sql: remoteString) // $ Alert @@ -239,9 +239,9 @@ func testTableRecord() throws { func test(statementCache: StatementCache) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ MISSING: Source - let _ = statementCache.statement(remoteString) // $ Alert + let _ = statementCache.statement(remoteString) // $ MISSING: Alert let _ = statementCache.statement(localString) // GOOD } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift index 5973866fb250..378f2b2e2edf 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift @@ -59,7 +59,7 @@ class Connection { func test_sqlite_swift_api(db: Connection) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ MISSING: Source let remoteNumber = Int(remoteString) ?? 0 let unsafeQuery1 = remoteString @@ -70,9 +70,9 @@ func test_sqlite_swift_api(db: Connection) throws { // --- execute --- - try db.execute(unsafeQuery1) // $ Alert - try db.execute(unsafeQuery2) // $ Alert - try db.execute(unsafeQuery3) // $ Alert + try db.execute(unsafeQuery1) // $ MISSING: Alert + try db.execute(unsafeQuery2) // $ MISSING: Alert + try db.execute(unsafeQuery3) // $ MISSING: Alert try db.execute(safeQuery1) // GOOD try db.execute(safeQuery2) // GOOD @@ -80,7 +80,7 @@ func test_sqlite_swift_api(db: Connection) throws { let varQuery = "SELECT * FROM users WHERE username=?" - let stmt1 = try db.prepare(unsafeQuery3) // $ Alert + let stmt1 = try db.prepare(unsafeQuery3) // $ MISSING: Alert try stmt1.run() let stmt2 = try db.prepare(varQuery, localString) // GOOD @@ -92,31 +92,31 @@ func test_sqlite_swift_api(db: Connection) throws { let stmt4 = try Statement(db, localString) // GOOD try stmt4.run() - let stmt5 = try Statement(db, remoteString) // $ Alert + let stmt5 = try Statement(db, remoteString) // $ MISSING: Alert try stmt5.run() // --- more variants --- - let stmt6 = try db.prepare(unsafeQuery1, "") // $ Alert + let stmt6 = try db.prepare(unsafeQuery1, "") // $ MISSING: Alert try stmt6.run() - let stmt7 = try db.prepare(unsafeQuery1, [""]) // $ Alert + let stmt7 = try db.prepare(unsafeQuery1, [""]) // $ MISSING: Alert try stmt7.run() - let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ Alert + let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ MISSING: Alert try stmt8.run() - try db.run(unsafeQuery1, "") // $ Alert + try db.run(unsafeQuery1, "") // $ MISSING: Alert - try db.run(unsafeQuery1, [""]) // $ Alert + try db.run(unsafeQuery1, [""]) // $ MISSING: Alert - try db.run(unsafeQuery1, ["username": ""]) // $ Alert + try db.run(unsafeQuery1, ["username": ""]) // $ MISSING: Alert - try db.scalar(unsafeQuery1, "") // $ Alert + try db.scalar(unsafeQuery1, "") // $ MISSING: Alert - try db.scalar(unsafeQuery1, [""]) // $ Alert + try db.scalar(unsafeQuery1, [""]) // $ MISSING: Alert - try db.scalar(unsafeQuery1, ["username": ""]) // $ Alert + try db.scalar(unsafeQuery1, ["username": ""]) // $ MISSING: Alert let stmt9 = try db.prepare(varQuery) // GOOD try stmt9.bind(remoteString) // GOOD @@ -129,5 +129,5 @@ func test_sqlite_swift_api(db: Connection) throws { try stmt9.scalar([remoteString]) // GOOD try stmt9.scalar(["username": remoteString]) // GOOD - try Statement(db, remoteString).run() // $ Alert + try Statement(db, remoteString).run() // $ MISSING: Alert } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index 36ebcd04a6ee..2b8a68800729 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -1,368 +1,268 @@ +#select +| GRDB.swift:106:41:106:52 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:106:41:106:52 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:108:41:108:52 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:108:41:108:52 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:111:43:111:54 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:111:43:111:54 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:114:51:114:62 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:114:51:114:62 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:117:27:117:38 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:117:27:117:38 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:119:27:119:38 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:119:27:119:38 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:122:41:122:52 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:122:41:122:52 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:124:41:124:52 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:124:41:124:52 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:144:29:144:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:144:29:144:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:145:29:145:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:145:29:145:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:146:29:146:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:146:29:146:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:147:29:147:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:147:29:147:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:148:29:148:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:148:29:148:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:149:29:149:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:149:29:149:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:150:29:150:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:150:29:150:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:151:29:151:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:151:29:151:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:170:22:170:33 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:170:22:170:33 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | +| GRDB.swift:172:22:172:33 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:172:22:172:33 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | +| GRDB.swift:187:33:187:44 | remoteString | GRDB.swift:185:26:185:80 | String(...) | GRDB.swift:187:33:187:44 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | String(...) | user-provided value | +| GRDB.swift:190:32:190:43 | remoteString | GRDB.swift:185:26:185:80 | String(...) | GRDB.swift:190:32:190:43 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | String(...) | user-provided value | +| GRDB.swift:193:37:193:48 | remoteString | GRDB.swift:185:26:185:80 | String(...) | GRDB.swift:193:37:193:48 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | String(...) | user-provided value | +| GRDB.swift:201:36:201:47 | remoteString | GRDB.swift:199:26:199:80 | String(...) | GRDB.swift:201:36:201:47 | remoteString | This query depends on a $@. | GRDB.swift:199:26:199:80 | String(...) | user-provided value | +| GRDB.swift:209:41:209:52 | remoteString | GRDB.swift:207:26:207:80 | String(...) | GRDB.swift:209:41:209:52 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | String(...) | user-provided value | +| GRDB.swift:210:44:210:55 | remoteString | GRDB.swift:207:26:207:80 | String(...) | GRDB.swift:210:44:210:55 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | String(...) | user-provided value | +| GRDB.swift:211:47:211:58 | remoteString | GRDB.swift:207:26:207:80 | String(...) | GRDB.swift:211:47:211:58 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | String(...) | user-provided value | +| GRDB.swift:212:47:212:58 | remoteString | GRDB.swift:207:26:207:80 | String(...) | GRDB.swift:212:47:212:58 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | String(...) | user-provided value | +| GRDB.swift:224:37:224:48 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:224:37:224:48 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:225:37:225:48 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:225:37:225:48 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:229:37:229:48 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:229:37:229:48 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:230:37:230:48 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:230:37:230:48 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:234:36:234:47 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:234:36:234:47 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:235:36:235:47 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:235:36:235:47 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:252:32:252:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:252:32:252:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:253:32:253:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:253:32:253:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:254:32:254:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:254:32:254:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:255:32:255:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:255:32:255:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:261:29:261:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:261:29:261:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:262:29:262:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:262:29:262:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:263:29:263:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:263:29:263:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:264:29:264:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:264:29:264:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:270:29:270:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:270:29:270:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:271:29:271:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:271:29:271:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:272:29:272:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:272:29:272:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:273:29:273:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:273:29:273:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:279:29:279:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:279:29:279:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:280:29:280:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:280:29:280:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:281:29:281:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:281:29:281:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:282:29:282:40 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:282:29:282:40 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | +| GRDB.swift:293:53:293:64 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:293:53:293:64 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:294:53:294:64 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:294:53:294:64 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:295:53:295:64 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:295:53:295:64 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:296:53:296:64 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:296:53:296:64 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:302:50:302:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:302:50:302:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:303:50:303:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:303:50:303:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:304:50:304:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:304:50:304:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:305:50:305:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:305:50:305:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:311:50:311:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:311:50:311:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:312:50:312:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:312:50:312:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:313:50:313:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:313:50:313:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:314:50:314:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:314:50:314:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:320:50:320:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:320:50:320:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:321:50:321:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:321:50:321:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:322:50:322:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:322:50:322:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:323:50:323:61 | remoteString | GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:323:50:323:61 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | String(...) | user-provided value | +| GRDB.swift:334:57:334:68 | remoteString | GRDB.swift:332:26:332:80 | String(...) | GRDB.swift:334:57:334:68 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | String(...) | user-provided value | +| GRDB.swift:335:57:335:68 | remoteString | GRDB.swift:332:26:332:80 | String(...) | GRDB.swift:335:57:335:68 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | String(...) | user-provided value | +| GRDB.swift:344:51:344:62 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:344:51:344:62 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:345:51:345:62 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:345:51:345:62 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:346:66:346:77 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:346:66:346:77 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:347:66:347:77 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:347:66:347:77 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:348:69:348:80 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:348:69:348:80 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:349:84:349:95 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:349:84:349:95 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:350:69:350:80 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:350:69:350:80 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| GRDB.swift:351:84:351:95 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:351:84:351:95 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| other.swift:50:22:50:33 | remoteString | other.swift:46:25:46:79 | String(...) | other.swift:50:22:50:33 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | String(...) | user-provided value | +| other.swift:57:16:57:27 | remoteString | other.swift:46:25:46:79 | String(...) | other.swift:57:16:57:27 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | String(...) | user-provided value | edges -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:106:41:106:41 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:108:41:108:41 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:111:43:111:43 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:114:51:114:51 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:117:27:117:27 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:119:27:119:27 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:122:41:122:41 | remoteString | provenance | | -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:124:41:124:41 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:132:39:132:39 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:135:46:135:46 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:138:56:138:56 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:141:45:141:45 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:144:29:144:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:145:29:145:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:146:29:146:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:147:29:147:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:148:29:148:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:149:29:149:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:150:29:150:29 | remoteString | provenance | | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:151:29:151:29 | remoteString | provenance | | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:166:32:166:32 | remoteString | provenance | | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:167:39:167:39 | remoteString | provenance | | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:168:49:168:49 | remoteString | provenance | | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:169:38:169:38 | remoteString | provenance | | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:170:22:170:22 | remoteString | provenance | | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:172:22:172:22 | remoteString | provenance | | -| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:187:33:187:33 | remoteString | provenance | | -| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:190:32:190:32 | remoteString | provenance | | -| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:193:37:193:37 | remoteString | provenance | | -| GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | GRDB.swift:201:36:201:36 | remoteString | provenance | | -| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:209:41:209:41 | remoteString | provenance | | -| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:210:44:210:44 | remoteString | provenance | | -| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:211:47:211:47 | remoteString | provenance | | -| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:212:47:212:47 | remoteString | provenance | | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:224:37:224:37 | remoteString | provenance | | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:225:37:225:37 | remoteString | provenance | | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:229:37:229:37 | remoteString | provenance | | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:230:37:230:37 | remoteString | provenance | | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:234:36:234:36 | remoteString | provenance | | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:235:36:235:36 | remoteString | provenance | | -| GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | GRDB.swift:244:38:244:38 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:252:32:252:32 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:253:32:253:32 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:254:32:254:32 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:255:32:255:32 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:261:29:261:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:262:29:262:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:263:29:263:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:264:29:264:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:270:29:270:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:271:29:271:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:272:29:272:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:273:29:273:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:279:29:279:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:280:29:280:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:281:29:281:29 | remoteString | provenance | | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:282:29:282:29 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:293:53:293:53 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:294:53:294:53 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:295:53:295:53 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:296:53:296:53 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:302:50:302:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:303:50:303:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:304:50:304:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:305:50:305:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:311:50:311:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:312:50:312:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:313:50:313:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:314:50:314:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:320:50:320:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:321:50:321:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:322:50:322:50 | remoteString | provenance | | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:323:50:323:50 | remoteString | provenance | | -| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:334:57:334:57 | remoteString | provenance | | -| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:335:57:335:57 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:344:51:344:51 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:345:51:345:51 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:346:66:346:66 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:347:66:347:66 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:348:69:348:69 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:349:84:349:84 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:350:69:350:69 | remoteString | provenance | | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:351:84:351:84 | remoteString | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:73:17:73:17 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:74:17:74:17 | unsafeQuery2 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:75:17:75:17 | unsafeQuery3 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:83:29:83:29 | unsafeQuery3 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:95:32:95:32 | remoteString | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:100:29:100:29 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:103:29:103:29 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:106:29:106:29 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:109:13:109:13 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:111:13:111:13 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:113:13:113:13 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:115:16:115:16 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:117:16:117:16 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:119:16:119:16 | unsafeQuery1 | provenance | | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:132:20:132:20 | remoteString | provenance | | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:50:22:50:22 | remoteString | provenance | | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:52:14:52:14 | remoteString | provenance | | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:53:14:53:14 | remoteString | provenance | | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:54:31:54:31 | remoteString | provenance | | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:55:14:55:14 | remoteString | provenance | | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:57:16:57:16 | remoteString | provenance | | -| other.swift:54:31:54:31 | remoteString | other.swift:54:14:54:43 | call to NSString.init(string:) | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | provenance | | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | provenance | | -| sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | provenance | | -| sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | sqlite3_c_api.swift:190:2:190:2 | data | provenance | | -| sqlite3_c_api.swift:190:2:190:2 | data | sqlite3_c_api.swift:190:21:190:21 | [post] buffer | provenance | | -| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:194:28:194:28 | buffer | provenance | | -| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:202:31:202:31 | buffer | provenance | | -| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:210:31:210:31 | buffer | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:106:41:106:52 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:108:41:108:52 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:111:43:111:54 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:114:51:114:62 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:117:27:117:38 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:119:27:119:38 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:122:41:122:52 | remoteString | provenance | | +| GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:124:41:124:52 | remoteString | provenance | | +| GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:104:6:104:17 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:144:29:144:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:145:29:145:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:146:29:146:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:147:29:147:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:148:29:148:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:149:29:149:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:150:29:150:40 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:151:29:151:40 | remoteString | provenance | | +| GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:130:6:130:17 | remoteString | provenance | | +| GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:170:22:170:33 | remoteString | provenance | | +| GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:172:22:172:33 | remoteString | provenance | | +| GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:164:6:164:17 | remoteString | provenance | | +| GRDB.swift:185:6:185:17 | remoteString | GRDB.swift:187:33:187:44 | remoteString | provenance | | +| GRDB.swift:185:6:185:17 | remoteString | GRDB.swift:190:32:190:43 | remoteString | provenance | | +| GRDB.swift:185:6:185:17 | remoteString | GRDB.swift:193:37:193:48 | remoteString | provenance | | +| GRDB.swift:185:26:185:80 | String(...) | GRDB.swift:185:6:185:17 | remoteString | provenance | | +| GRDB.swift:199:6:199:17 | remoteString | GRDB.swift:201:36:201:47 | remoteString | provenance | | +| GRDB.swift:199:26:199:80 | String(...) | GRDB.swift:199:6:199:17 | remoteString | provenance | | +| GRDB.swift:207:6:207:17 | remoteString | GRDB.swift:209:41:209:52 | remoteString | provenance | | +| GRDB.swift:207:6:207:17 | remoteString | GRDB.swift:210:44:210:55 | remoteString | provenance | | +| GRDB.swift:207:6:207:17 | remoteString | GRDB.swift:211:47:211:58 | remoteString | provenance | | +| GRDB.swift:207:6:207:17 | remoteString | GRDB.swift:212:47:212:58 | remoteString | provenance | | +| GRDB.swift:207:26:207:80 | String(...) | GRDB.swift:207:6:207:17 | remoteString | provenance | | +| GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:224:37:224:48 | remoteString | provenance | | +| GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:225:37:225:48 | remoteString | provenance | | +| GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:229:37:229:48 | remoteString | provenance | | +| GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:230:37:230:48 | remoteString | provenance | | +| GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:234:36:234:47 | remoteString | provenance | | +| GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:235:36:235:47 | remoteString | provenance | | +| GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:222:6:222:17 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:252:32:252:43 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:253:32:253:43 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:254:32:254:43 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:255:32:255:43 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:261:29:261:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:262:29:262:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:263:29:263:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:264:29:264:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:270:29:270:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:271:29:271:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:272:29:272:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:273:29:273:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:279:29:279:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:280:29:280:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:281:29:281:40 | remoteString | provenance | | +| GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:282:29:282:40 | remoteString | provenance | | +| GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:250:6:250:17 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:293:53:293:64 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:294:53:294:64 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:295:53:295:64 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:296:53:296:64 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:302:50:302:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:303:50:303:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:304:50:304:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:305:50:305:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:311:50:311:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:312:50:312:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:313:50:313:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:314:50:314:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:320:50:320:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:321:50:321:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:322:50:322:61 | remoteString | provenance | | +| GRDB.swift:291:6:291:17 | remoteString | GRDB.swift:323:50:323:61 | remoteString | provenance | | +| GRDB.swift:291:26:291:80 | String(...) | GRDB.swift:291:6:291:17 | remoteString | provenance | | +| GRDB.swift:332:6:332:17 | remoteString | GRDB.swift:334:57:334:68 | remoteString | provenance | | +| GRDB.swift:332:6:332:17 | remoteString | GRDB.swift:335:57:335:68 | remoteString | provenance | | +| GRDB.swift:332:26:332:80 | String(...) | GRDB.swift:332:6:332:17 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:344:51:344:62 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:345:51:345:62 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:346:66:346:77 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:347:66:347:77 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:348:69:348:80 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:349:84:349:95 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:350:69:350:80 | remoteString | provenance | | +| GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:351:84:351:95 | remoteString | provenance | | +| GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:342:6:342:17 | remoteString | provenance | | +| other.swift:46:6:46:17 | remoteString | other.swift:50:22:50:33 | remoteString | provenance | | +| other.swift:46:6:46:17 | remoteString | other.swift:57:16:57:27 | remoteString | provenance | | +| other.swift:46:25:46:79 | String(...) | other.swift:46:6:46:17 | remoteString | provenance | | nodes -| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:106:41:106:41 | remoteString | semmle.label | remoteString | -| GRDB.swift:108:41:108:41 | remoteString | semmle.label | remoteString | -| GRDB.swift:111:43:111:43 | remoteString | semmle.label | remoteString | -| GRDB.swift:114:51:114:51 | remoteString | semmle.label | remoteString | -| GRDB.swift:117:27:117:27 | remoteString | semmle.label | remoteString | -| GRDB.swift:119:27:119:27 | remoteString | semmle.label | remoteString | -| GRDB.swift:122:41:122:41 | remoteString | semmle.label | remoteString | -| GRDB.swift:124:41:124:41 | remoteString | semmle.label | remoteString | -| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:132:39:132:39 | remoteString | semmle.label | remoteString | -| GRDB.swift:135:46:135:46 | remoteString | semmle.label | remoteString | -| GRDB.swift:138:56:138:56 | remoteString | semmle.label | remoteString | -| GRDB.swift:141:45:141:45 | remoteString | semmle.label | remoteString | -| GRDB.swift:144:29:144:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:145:29:145:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:146:29:146:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:147:29:147:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:148:29:148:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:149:29:149:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:150:29:150:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:151:29:151:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:166:32:166:32 | remoteString | semmle.label | remoteString | -| GRDB.swift:167:39:167:39 | remoteString | semmle.label | remoteString | -| GRDB.swift:168:49:168:49 | remoteString | semmle.label | remoteString | -| GRDB.swift:169:38:169:38 | remoteString | semmle.label | remoteString | -| GRDB.swift:170:22:170:22 | remoteString | semmle.label | remoteString | -| GRDB.swift:172:22:172:22 | remoteString | semmle.label | remoteString | -| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:187:33:187:33 | remoteString | semmle.label | remoteString | -| GRDB.swift:190:32:190:32 | remoteString | semmle.label | remoteString | -| GRDB.swift:193:37:193:37 | remoteString | semmle.label | remoteString | -| GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:201:36:201:36 | remoteString | semmle.label | remoteString | -| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:209:41:209:41 | remoteString | semmle.label | remoteString | -| GRDB.swift:210:44:210:44 | remoteString | semmle.label | remoteString | -| GRDB.swift:211:47:211:47 | remoteString | semmle.label | remoteString | -| GRDB.swift:212:47:212:47 | remoteString | semmle.label | remoteString | -| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:224:37:224:37 | remoteString | semmle.label | remoteString | -| GRDB.swift:225:37:225:37 | remoteString | semmle.label | remoteString | -| GRDB.swift:229:37:229:37 | remoteString | semmle.label | remoteString | -| GRDB.swift:230:37:230:37 | remoteString | semmle.label | remoteString | -| GRDB.swift:234:36:234:36 | remoteString | semmle.label | remoteString | -| GRDB.swift:235:36:235:36 | remoteString | semmle.label | remoteString | -| GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:244:38:244:38 | remoteString | semmle.label | remoteString | -| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:252:32:252:32 | remoteString | semmle.label | remoteString | -| GRDB.swift:253:32:253:32 | remoteString | semmle.label | remoteString | -| GRDB.swift:254:32:254:32 | remoteString | semmle.label | remoteString | -| GRDB.swift:255:32:255:32 | remoteString | semmle.label | remoteString | -| GRDB.swift:261:29:261:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:262:29:262:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:263:29:263:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:264:29:264:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:270:29:270:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:271:29:271:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:272:29:272:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:273:29:273:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:279:29:279:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:280:29:280:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:281:29:281:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:282:29:282:29 | remoteString | semmle.label | remoteString | -| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:293:53:293:53 | remoteString | semmle.label | remoteString | -| GRDB.swift:294:53:294:53 | remoteString | semmle.label | remoteString | -| GRDB.swift:295:53:295:53 | remoteString | semmle.label | remoteString | -| GRDB.swift:296:53:296:53 | remoteString | semmle.label | remoteString | -| GRDB.swift:302:50:302:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:303:50:303:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:304:50:304:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:305:50:305:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:311:50:311:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:312:50:312:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:313:50:313:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:314:50:314:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:320:50:320:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:321:50:321:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:322:50:322:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:323:50:323:50 | remoteString | semmle.label | remoteString | -| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:334:57:334:57 | remoteString | semmle.label | remoteString | -| GRDB.swift:335:57:335:57 | remoteString | semmle.label | remoteString | -| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| GRDB.swift:344:51:344:51 | remoteString | semmle.label | remoteString | -| GRDB.swift:345:51:345:51 | remoteString | semmle.label | remoteString | -| GRDB.swift:346:66:346:66 | remoteString | semmle.label | remoteString | -| GRDB.swift:347:66:347:66 | remoteString | semmle.label | remoteString | -| GRDB.swift:348:69:348:69 | remoteString | semmle.label | remoteString | -| GRDB.swift:349:84:349:84 | remoteString | semmle.label | remoteString | -| GRDB.swift:350:69:350:69 | remoteString | semmle.label | remoteString | -| GRDB.swift:351:84:351:84 | remoteString | semmle.label | remoteString | -| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| SQLite.swift:73:17:73:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:74:17:74:17 | unsafeQuery2 | semmle.label | unsafeQuery2 | -| SQLite.swift:75:17:75:17 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| SQLite.swift:83:29:83:29 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| SQLite.swift:95:32:95:32 | remoteString | semmle.label | remoteString | -| SQLite.swift:100:29:100:29 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:103:29:103:29 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:106:29:106:29 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:109:13:109:13 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:111:13:111:13 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:113:13:113:13 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:115:16:115:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:117:16:117:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:119:16:119:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:132:20:132:20 | remoteString | semmle.label | remoteString | -| other.swift:46:25:46:79 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| other.swift:50:22:50:22 | remoteString | semmle.label | remoteString | -| other.swift:52:14:52:14 | remoteString | semmle.label | remoteString | -| other.swift:53:14:53:14 | remoteString | semmle.label | remoteString | -| other.swift:54:14:54:43 | call to NSString.init(string:) | semmle.label | call to NSString.init(string:) | -| other.swift:54:31:54:31 | remoteString | semmle.label | remoteString | -| other.swift:55:14:55:14 | remoteString | semmle.label | remoteString | -| other.swift:57:16:57:16 | remoteString | semmle.label | remoteString | -| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | -| sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | semmle.label | unsafeQuery2 | -| sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | semmle.label | unsafeQuery3 | -| sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | semmle.label | call to data(using:allowLossyConversion:) | -| sqlite3_c_api.swift:190:2:190:2 | data | semmle.label | data | -| sqlite3_c_api.swift:190:21:190:21 | [post] buffer | semmle.label | [post] buffer | -| sqlite3_c_api.swift:194:28:194:28 | buffer | semmle.label | buffer | -| sqlite3_c_api.swift:202:31:202:31 | buffer | semmle.label | buffer | -| sqlite3_c_api.swift:210:31:210:31 | buffer | semmle.label | buffer | +| GRDB.swift:104:6:104:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:104:25:104:79 | String(...) | semmle.label | String(...) | +| GRDB.swift:106:41:106:52 | remoteString | semmle.label | remoteString | +| GRDB.swift:108:41:108:52 | remoteString | semmle.label | remoteString | +| GRDB.swift:111:43:111:54 | remoteString | semmle.label | remoteString | +| GRDB.swift:114:51:114:62 | remoteString | semmle.label | remoteString | +| GRDB.swift:117:27:117:38 | remoteString | semmle.label | remoteString | +| GRDB.swift:119:27:119:38 | remoteString | semmle.label | remoteString | +| GRDB.swift:122:41:122:52 | remoteString | semmle.label | remoteString | +| GRDB.swift:124:41:124:52 | remoteString | semmle.label | remoteString | +| GRDB.swift:130:6:130:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:130:26:130:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:144:29:144:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:145:29:145:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:146:29:146:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:147:29:147:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:148:29:148:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:149:29:149:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:150:29:150:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:151:29:151:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:164:6:164:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:164:26:164:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:170:22:170:33 | remoteString | semmle.label | remoteString | +| GRDB.swift:172:22:172:33 | remoteString | semmle.label | remoteString | +| GRDB.swift:185:6:185:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:185:26:185:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:187:33:187:44 | remoteString | semmle.label | remoteString | +| GRDB.swift:190:32:190:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:193:37:193:48 | remoteString | semmle.label | remoteString | +| GRDB.swift:199:6:199:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:199:26:199:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:201:36:201:47 | remoteString | semmle.label | remoteString | +| GRDB.swift:207:6:207:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:207:26:207:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:209:41:209:52 | remoteString | semmle.label | remoteString | +| GRDB.swift:210:44:210:55 | remoteString | semmle.label | remoteString | +| GRDB.swift:211:47:211:58 | remoteString | semmle.label | remoteString | +| GRDB.swift:212:47:212:58 | remoteString | semmle.label | remoteString | +| GRDB.swift:222:6:222:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:222:26:222:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:224:37:224:48 | remoteString | semmle.label | remoteString | +| GRDB.swift:225:37:225:48 | remoteString | semmle.label | remoteString | +| GRDB.swift:229:37:229:48 | remoteString | semmle.label | remoteString | +| GRDB.swift:230:37:230:48 | remoteString | semmle.label | remoteString | +| GRDB.swift:234:36:234:47 | remoteString | semmle.label | remoteString | +| GRDB.swift:235:36:235:47 | remoteString | semmle.label | remoteString | +| GRDB.swift:250:6:250:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:250:26:250:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:252:32:252:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:253:32:253:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:254:32:254:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:255:32:255:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:261:29:261:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:262:29:262:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:263:29:263:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:264:29:264:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:270:29:270:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:271:29:271:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:272:29:272:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:273:29:273:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:279:29:279:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:280:29:280:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:281:29:281:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:282:29:282:40 | remoteString | semmle.label | remoteString | +| GRDB.swift:291:6:291:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:291:26:291:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:293:53:293:64 | remoteString | semmle.label | remoteString | +| GRDB.swift:294:53:294:64 | remoteString | semmle.label | remoteString | +| GRDB.swift:295:53:295:64 | remoteString | semmle.label | remoteString | +| GRDB.swift:296:53:296:64 | remoteString | semmle.label | remoteString | +| GRDB.swift:302:50:302:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:303:50:303:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:304:50:304:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:305:50:305:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:311:50:311:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:312:50:312:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:313:50:313:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:314:50:314:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:320:50:320:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:321:50:321:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:322:50:322:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:323:50:323:61 | remoteString | semmle.label | remoteString | +| GRDB.swift:332:6:332:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:332:26:332:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:334:57:334:68 | remoteString | semmle.label | remoteString | +| GRDB.swift:335:57:335:68 | remoteString | semmle.label | remoteString | +| GRDB.swift:342:6:342:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:342:26:342:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:344:51:344:62 | remoteString | semmle.label | remoteString | +| GRDB.swift:345:51:345:62 | remoteString | semmle.label | remoteString | +| GRDB.swift:346:66:346:77 | remoteString | semmle.label | remoteString | +| GRDB.swift:347:66:347:77 | remoteString | semmle.label | remoteString | +| GRDB.swift:348:69:348:80 | remoteString | semmle.label | remoteString | +| GRDB.swift:349:84:349:95 | remoteString | semmle.label | remoteString | +| GRDB.swift:350:69:350:80 | remoteString | semmle.label | remoteString | +| GRDB.swift:351:84:351:95 | remoteString | semmle.label | remoteString | +| other.swift:46:6:46:17 | remoteString | semmle.label | remoteString | +| other.swift:46:25:46:79 | String(...) | semmle.label | String(...) | +| other.swift:50:22:50:33 | remoteString | semmle.label | remoteString | +| other.swift:57:16:57:27 | remoteString | semmle.label | remoteString | subpaths -#select -| GRDB.swift:106:41:106:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:106:41:106:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:108:41:108:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:108:41:108:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:111:43:111:43 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:111:43:111:43 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:114:51:114:51 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:114:51:114:51 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:117:27:117:27 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:117:27:117:27 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:119:27:119:27 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:119:27:119:27 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:122:41:122:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:122:41:122:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:124:41:124:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:124:41:124:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:132:39:132:39 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:132:39:132:39 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:135:46:135:46 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:135:46:135:46 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:138:56:138:56 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:138:56:138:56 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:141:45:141:45 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:141:45:141:45 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:144:29:144:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:144:29:144:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:145:29:145:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:145:29:145:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:146:29:146:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:146:29:146:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:147:29:147:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:147:29:147:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:148:29:148:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:148:29:148:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:149:29:149:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:149:29:149:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:150:29:150:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:150:29:150:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:151:29:151:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | GRDB.swift:151:29:151:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:166:32:166:32 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:166:32:166:32 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:167:39:167:39 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:167:39:167:39 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:168:49:168:49 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:168:49:168:49 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:169:38:169:38 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:169:38:169:38 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:170:22:170:22 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:170:22:170:22 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:172:22:172:22 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | GRDB.swift:172:22:172:22 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:187:33:187:33 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:187:33:187:33 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:190:32:190:32 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:190:32:190:32 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:193:37:193:37 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | GRDB.swift:193:37:193:37 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:201:36:201:36 | remoteString | GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | GRDB.swift:201:36:201:36 | remoteString | This query depends on a $@. | GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:209:41:209:41 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:209:41:209:41 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:210:44:210:44 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:210:44:210:44 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:211:47:211:47 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:211:47:211:47 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:212:47:212:47 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | GRDB.swift:212:47:212:47 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:224:37:224:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:224:37:224:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:225:37:225:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:225:37:225:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:229:37:229:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:229:37:229:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:230:37:230:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:230:37:230:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:234:36:234:36 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:234:36:234:36 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:235:36:235:36 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | GRDB.swift:235:36:235:36 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:244:38:244:38 | remoteString | GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | GRDB.swift:244:38:244:38 | remoteString | This query depends on a $@. | GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:252:32:252:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:252:32:252:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:253:32:253:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:253:32:253:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:254:32:254:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:254:32:254:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:255:32:255:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:255:32:255:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:261:29:261:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:261:29:261:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:262:29:262:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:262:29:262:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:263:29:263:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:263:29:263:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:264:29:264:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:264:29:264:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:270:29:270:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:270:29:270:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:271:29:271:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:271:29:271:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:272:29:272:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:272:29:272:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:273:29:273:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:273:29:273:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:279:29:279:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:279:29:279:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:280:29:280:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:280:29:280:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:281:29:281:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:281:29:281:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:282:29:282:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | GRDB.swift:282:29:282:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:293:53:293:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:293:53:293:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:294:53:294:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:294:53:294:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:295:53:295:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:295:53:295:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:296:53:296:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:296:53:296:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:302:50:302:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:302:50:302:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:303:50:303:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:303:50:303:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:304:50:304:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:304:50:304:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:305:50:305:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:305:50:305:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:311:50:311:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:311:50:311:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:312:50:312:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:312:50:312:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:313:50:313:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:313:50:313:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:314:50:314:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:314:50:314:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:320:50:320:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:320:50:320:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:321:50:321:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:321:50:321:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:322:50:322:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:322:50:322:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:323:50:323:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | GRDB.swift:323:50:323:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:334:57:334:57 | remoteString | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:334:57:334:57 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:335:57:335:57 | remoteString | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | GRDB.swift:335:57:335:57 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:344:51:344:51 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:344:51:344:51 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:345:51:345:51 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:345:51:345:51 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:346:66:346:66 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:346:66:346:66 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:347:66:347:66 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:347:66:347:66 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:348:69:348:69 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:348:69:348:69 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:349:84:349:84 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:349:84:349:84 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:350:69:350:69 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:350:69:350:69 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| GRDB.swift:351:84:351:84 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | GRDB.swift:351:84:351:84 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:73:17:73:17 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:73:17:73:17 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:74:17:74:17 | unsafeQuery2 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:74:17:74:17 | unsafeQuery2 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:75:17:75:17 | unsafeQuery3 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:75:17:75:17 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:83:29:83:29 | unsafeQuery3 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:83:29:83:29 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:95:32:95:32 | remoteString | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:95:32:95:32 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:100:29:100:29 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:100:29:100:29 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:103:29:103:29 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:103:29:103:29 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:106:29:106:29 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:106:29:106:29 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:109:13:109:13 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:109:13:109:13 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:111:13:111:13 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:111:13:111:13 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:113:13:113:13 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:113:13:113:13 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:115:16:115:16 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:115:16:115:16 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:117:16:117:16 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:117:16:117:16 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:119:16:119:16 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:119:16:119:16 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| SQLite.swift:132:20:132:20 | remoteString | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:132:20:132:20 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value | -| other.swift:50:22:50:22 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:50:22:50:22 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | -| other.swift:52:14:52:14 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:52:14:52:14 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | -| other.swift:53:14:53:14 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:53:14:53:14 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | -| other.swift:54:14:54:43 | call to NSString.init(string:) | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:54:14:54:43 | call to NSString.init(string:) | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | -| other.swift:55:14:55:14 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:55:14:55:14 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | -| other.swift:57:16:57:16 | remoteString | other.swift:46:25:46:79 | call to String.init(contentsOf:) | other.swift:57:16:57:16 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:145:26:145:26 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:194:28:194:28 | buffer | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:194:28:194:28 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:202:31:202:31 | buffer | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:202:31:202:31 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | -| sqlite3_c_api.swift:210:31:210:31 | buffer | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:210:31:210:31 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | user-provided value | diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref index 654631d8a094..dcf21a24fc27 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.qlref @@ -1,2 +1,3 @@ -query: queries/Security/CWE-089/SqlInjection.ql -postprocess: utils/test/InlineExpectationsTestQuery.ql +query: queries/security/CWE-089/SqlInjection.ql +postprocess: + - utils/test/InlineExpectationsTestQuery.ql diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift index 0974d03937e9..3e4d6634b800 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift @@ -49,10 +49,10 @@ func test_heuristic(db: MyDatabase) throws { _ = MyDatabase(sql: "some_fixed_sql") // GOOD _ = MyDatabase(sql: remoteString) // $ Alert - db.execute1(remoteString) // $ Alert - db.execute2(remoteString) // $ Alert - db.execute3(NSString(string: remoteString)) // $ Alert - db.execute4(remoteString as! Sql) // $ Alert + db.execute1(remoteString) // $ MISSING: Alert + db.execute2(remoteString) // $ MISSING: Alert + db.execute3(NSString(string: remoteString)) // $ MISSING: Alert + db.execute4(remoteString as! Sql) // $ MISSING: Alert db.query(sql: remoteString) // $ Alert db.query(sqlLiteral: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift index b4e7451b9163..024734e9367c 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift @@ -119,7 +119,7 @@ func sqlite3_finalize( func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) { let localString = "user" - let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) // $ Source + let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) // $ MISSING: Source let remoteNumber = Int(remoteString) ?? 0 let unsafeQuery1 = remoteString @@ -130,9 +130,9 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) // --- exec --- - let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // $ Alert - let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // $ Alert - let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // $ Alert + let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // $ MISSING: Alert + let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // $ MISSING: Alert + let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // $ MISSING: Alert let result4 = sqlite3_exec(db, safeQuery1, nil, nil, nil) // GOOD let result5 = sqlite3_exec(db, safeQuery2, nil, nil, nil) // GOOD @@ -142,7 +142,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt1: OpaquePointer? - if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // $ Alert + if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // $ MISSING: Alert let result = sqlite3_step(stmt1) // ... } @@ -172,7 +172,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt4: OpaquePointer? - if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // $ Alert + if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // $ MISSING: Alert let result = sqlite3_step(stmt4) // ... } @@ -180,7 +180,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt5: OpaquePointer? - if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // $ Alert + if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // $ MISSING: Alert let result = sqlite3_step(stmt5) // ... } @@ -191,7 +191,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt6: OpaquePointer? - if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // $ Alert + if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // $ MISSING: Alert let result = sqlite3_step(stmt6) // ... } @@ -199,7 +199,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt7: OpaquePointer? - if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // $ Alert + if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // $ MISSING: Alert let result = sqlite3_step(stmt7) // ... } @@ -207,7 +207,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt8: OpaquePointer? - if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // $ Alert + if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // $ MISSING: Alert let result = sqlite3_step(stmt8) // ... } From 8ce85ea7aba8fafcc6986d674f3e6bfc8d92d75f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:42:06 +0000 Subject: [PATCH 04/13] unified: Model unambiguous Swift SQL injection sinks Add SQLite C API, SQLite.swift and GRDB sinks not already covered by sql argument labels. Cover omitted variadic bindings while retaining safe prepared-statement cases. Leave Connection.run/scalar missing because legacy selectors cannot distinguish safe Statement binding methods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- unified/ql/lib/ext/legacy-swift.model.yml | 21 +++ .../security/CWE-089/SqlInjection/GRDB.swift | 20 +-- .../CWE-089/SqlInjection/SQLite.swift | 25 ++-- .../SqlInjection/SqlInjection.expected | 124 ++++++++++++++++++ .../CWE-089/SqlInjection/sqlite3_c_api.swift | 20 +-- 5 files changed, 180 insertions(+), 30 deletions(-) diff --git a/unified/ql/lib/ext/legacy-swift.model.yml b/unified/ql/lib/ext/legacy-swift.model.yml index d262fadcec1d..8bf90325cd63 100644 --- a/unified/ql/lib/ext/legacy-swift.model.yml +++ b/unified/ql/lib/ext/legacy-swift.model.yml @@ -118,6 +118,13 @@ extensions: - ["", "", false, "sqlite3_prepare16_v3(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "", false, "sqlite3_prepare_v2(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "", false, "sqlite3_prepare_v3(_:_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] + - ["", "", false, "sqlite3_exec(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "", false, "sqlite3_prepare(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "", false, "sqlite3_prepare_v2(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "", false, "sqlite3_prepare_v3(_:_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "", false, "sqlite3_prepare16(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "", false, "sqlite3_prepare16_v2(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "", false, "sqlite3_prepare16_v3(_:_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] - ["", "", false, "sqlite3_database_file_object(_:)", "", "", "Argument[0]", "path-injection", "manual"] - ["", "", false, "sqlite3_filename_database(_:)", "", "", "Argument[0]", "path-injection", "manual"] - ["", "", false, "sqlite3_filename_journal(_:)", "", "", "Argument[0]", "path-injection", "manual"] @@ -237,6 +244,20 @@ extensions: - ["", "Connection", true, "run(_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "Connection", true, "scalar(_:_:)", "", "", "Argument[0]", "database-store", "manual"] - ["", "Connection", true, "scalar(_:_:)", "", "", "Argument[1]", "database-store", "manual"] + - ["", "Connection", true, "execute(_:)", "", "", "Argument[0]", "sql-injection", "manual"] + - ["", "Connection", true, "prepare(_:)", "", "", "Argument[0]", "sql-injection", "manual"] + - ["", "Connection", true, "prepare(_:_:)", "", "", "Argument[0]", "sql-injection", "manual"] + # TODO: Model Connection.run/scalar once receiver types distinguish them from safe Statement bindings. + - ["", "Statement", true, "init(_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] + - ["", "SQLRequest", true, "init(stringLiteral:)", "", "", "Argument[stringLiteral:]", "sql-injection", "manual"] + - ["", "SQLRequest", true, "init(unicodeScalarLiteral:)", "", "", "Argument[unicodeScalarLiteral:]", "sql-injection", "manual"] + - ["", "SQLRequest", true, "init(extendedGraphemeClusterLiteral:)", "", "", "Argument[extendedGraphemeClusterLiteral:]", "sql-injection", "manual"] + - ["", "SQLRequest", true, "init(stringInterpolation:)", "", "", "Argument[stringInterpolation:]", "sql-injection", "manual"] + - ["", "SQL", true, "init(stringLiteral:)", "", "", "Argument[stringLiteral:]", "sql-injection", "manual"] + - ["", "SQL", true, "init(unicodeScalarLiteral:)", "", "", "Argument[unicodeScalarLiteral:]", "sql-injection", "manual"] + - ["", "SQL", true, "init(extendedGraphemeClusterLiteral:)", "", "", "Argument[extendedGraphemeClusterLiteral:]", "sql-injection", "manual"] + - ["", "SQL", true, "init(stringInterpolation:)", "", "", "Argument[stringInterpolation:]", "sql-injection", "manual"] + - ["", "StatementCache", true, "statement(_:)", "", "", "Argument[0]", "sql-injection", "manual"] - ["", "Connection", true, "init(_:readonly:)", "", "", "Argument[0]", "path-injection", "manual"] - ["", "Connection.Location", true, "uri(_:parameters:)", "", "", "Argument[0]", "path-injection", "manual"] - ["", "Database", true, "changePassphrase(_:)", "", "", "Argument[0]", "encryption-key", "manual"] diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift index 0ae182a61e0a..3bdffaa272b4 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/GRDB.swift @@ -129,16 +129,16 @@ func testSqlRequest() throws { let localString = "user" let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQLRequest(stringLiteral: remoteString) // $ MISSING: Alert + let _ = SQLRequest(stringLiteral: remoteString) // $ Alert let _ = SQLRequest(stringLiteral: localString) // GOOD - let _ = SQLRequest(unicodeScalarLiteral: remoteString) // $ MISSING: Alert + let _ = SQLRequest(unicodeScalarLiteral: remoteString) // $ Alert let _ = SQLRequest(unicodeScalarLiteral: localString) // GOOD - let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // $ MISSING: Alert + let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // $ Alert let _ = SQLRequest(extendedGraphemeClusterLiteral: localString) // GOOD - let _ = SQLRequest(stringInterpolation: remoteString) // $ MISSING: Alert + let _ = SQLRequest(stringInterpolation: remoteString) // $ Alert let _ = SQLRequest(stringInterpolation: localString) // GOOD let _ = SQLRequest(sql: remoteString) // $ Alert @@ -163,10 +163,10 @@ func testSql() throws { let localString = "user" let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = SQL(stringLiteral: remoteString) // $ MISSING: Alert - let _ = SQL(unicodeScalarLiteral: remoteString) // $ MISSING: Alert - let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // $ MISSING: Alert - let _ = SQL(stringInterpolation: remoteString) // $ MISSING: Alert + let _ = SQL(stringLiteral: remoteString) // $ Alert + let _ = SQL(unicodeScalarLiteral: remoteString) // $ Alert + let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // $ Alert + let _ = SQL(stringInterpolation: remoteString) // $ Alert let _ = SQL(sql: remoteString) // $ Alert let sql1 = SQL(stringLiteral: "") sql1.append(sql: remoteString) // $ Alert @@ -239,9 +239,9 @@ func testTableRecord() throws { func test(statementCache: StatementCache) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ MISSING: Source + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source - let _ = statementCache.statement(remoteString) // $ MISSING: Alert + let _ = statementCache.statement(remoteString) // $ Alert let _ = statementCache.statement(localString) // GOOD } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift index 378f2b2e2edf..e3c8347700c6 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift @@ -59,7 +59,7 @@ class Connection { func test_sqlite_swift_api(db: Connection) throws { let localString = "user" - let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ MISSING: Source + let remoteString = try String(contentsOf: URL(string: "http://example.com/")!) // $ Source let remoteNumber = Int(remoteString) ?? 0 let unsafeQuery1 = remoteString @@ -70,9 +70,9 @@ func test_sqlite_swift_api(db: Connection) throws { // --- execute --- - try db.execute(unsafeQuery1) // $ MISSING: Alert - try db.execute(unsafeQuery2) // $ MISSING: Alert - try db.execute(unsafeQuery3) // $ MISSING: Alert + try db.execute(unsafeQuery1) // $ Alert + try db.execute(unsafeQuery2) // $ Alert + try db.execute(unsafeQuery3) // $ Alert try db.execute(safeQuery1) // GOOD try db.execute(safeQuery2) // GOOD @@ -80,7 +80,7 @@ func test_sqlite_swift_api(db: Connection) throws { let varQuery = "SELECT * FROM users WHERE username=?" - let stmt1 = try db.prepare(unsafeQuery3) // $ MISSING: Alert + let stmt1 = try db.prepare(unsafeQuery3) // $ Alert try stmt1.run() let stmt2 = try db.prepare(varQuery, localString) // GOOD @@ -92,18 +92,18 @@ func test_sqlite_swift_api(db: Connection) throws { let stmt4 = try Statement(db, localString) // GOOD try stmt4.run() - let stmt5 = try Statement(db, remoteString) // $ MISSING: Alert + let stmt5 = try Statement(db, remoteString) // $ Alert try stmt5.run() // --- more variants --- - let stmt6 = try db.prepare(unsafeQuery1, "") // $ MISSING: Alert + let stmt6 = try db.prepare(unsafeQuery1, "") // $ Alert try stmt6.run() - let stmt7 = try db.prepare(unsafeQuery1, [""]) // $ MISSING: Alert + let stmt7 = try db.prepare(unsafeQuery1, [""]) // $ Alert try stmt7.run() - let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ MISSING: Alert + let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ Alert try stmt8.run() try db.run(unsafeQuery1, "") // $ MISSING: Alert @@ -129,5 +129,10 @@ func test_sqlite_swift_api(db: Connection) throws { try stmt9.scalar([remoteString]) // GOOD try stmt9.scalar(["username": remoteString]) // GOOD - try Statement(db, remoteString).run() // $ MISSING: Alert + try Statement(db, remoteString).run() // $ Alert + + try db.run(unsafeQuery1) // $ MISSING: Alert + try db.run(safeQuery1) // GOOD + try db.scalar(unsafeQuery1) // $ MISSING: Alert + try db.scalar(safeQuery1) // GOOD } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index 2b8a68800729..87ea5bb2d593 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -7,6 +7,10 @@ | GRDB.swift:119:27:119:38 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:119:27:119:38 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | | GRDB.swift:122:41:122:52 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:122:41:122:52 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | | GRDB.swift:124:41:124:52 | remoteString | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:124:41:124:52 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | String(...) | user-provided value | +| GRDB.swift:132:39:132:50 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:132:39:132:50 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:135:46:135:57 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:135:46:135:57 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:138:56:138:67 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:138:56:138:67 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:141:45:141:56 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:141:45:141:56 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | | GRDB.swift:144:29:144:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:144:29:144:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | | GRDB.swift:145:29:145:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:145:29:145:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | | GRDB.swift:146:29:146:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:146:29:146:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | @@ -15,6 +19,10 @@ | GRDB.swift:149:29:149:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:149:29:149:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | | GRDB.swift:150:29:150:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:150:29:150:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | | GRDB.swift:151:29:151:40 | remoteString | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:151:29:151:40 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | String(...) | user-provided value | +| GRDB.swift:166:32:166:43 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:166:32:166:43 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | +| GRDB.swift:167:39:167:50 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:167:39:167:50 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | +| GRDB.swift:168:49:168:60 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:168:49:168:60 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | +| GRDB.swift:169:38:169:49 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:169:38:169:49 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | | GRDB.swift:170:22:170:33 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:170:22:170:33 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | | GRDB.swift:172:22:172:33 | remoteString | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:172:22:172:33 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | String(...) | user-provided value | | GRDB.swift:187:33:187:44 | remoteString | GRDB.swift:185:26:185:80 | String(...) | GRDB.swift:187:33:187:44 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | String(...) | user-provided value | @@ -31,6 +39,7 @@ | GRDB.swift:230:37:230:48 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:230:37:230:48 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | | GRDB.swift:234:36:234:47 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:234:36:234:47 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | | GRDB.swift:235:36:235:47 | remoteString | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:235:36:235:47 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | String(...) | user-provided value | +| GRDB.swift:244:38:244:49 | remoteString | GRDB.swift:242:26:242:80 | String(...) | GRDB.swift:244:38:244:49 | remoteString | This query depends on a $@. | GRDB.swift:242:26:242:80 | String(...) | user-provided value | | GRDB.swift:252:32:252:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:252:32:252:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | | GRDB.swift:253:32:253:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:253:32:253:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | | GRDB.swift:254:32:254:43 | remoteString | GRDB.swift:250:26:250:80 | String(...) | GRDB.swift:254:32:254:43 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | String(...) | user-provided value | @@ -73,8 +82,26 @@ | GRDB.swift:349:84:349:95 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:349:84:349:95 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | | GRDB.swift:350:69:350:80 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:350:69:350:80 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | | GRDB.swift:351:84:351:95 | remoteString | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:351:84:351:95 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | String(...) | user-provided value | +| SQLite.swift:73:17:73:28 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:73:17:73:28 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:74:17:74:28 | unsafeQuery2 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:74:17:74:28 | unsafeQuery2 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:75:17:75:28 | unsafeQuery3 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:75:17:75:28 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:83:29:83:40 | unsafeQuery3 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:83:29:83:40 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:95:32:95:43 | remoteString | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:95:32:95:43 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:100:29:100:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:100:29:100:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:103:29:103:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:103:29:103:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:106:29:106:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:106:29:106:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:132:20:132:31 | remoteString | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:132:20:132:31 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | other.swift:50:22:50:33 | remoteString | other.swift:46:25:46:79 | String(...) | other.swift:50:22:50:33 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | String(...) | user-provided value | | other.swift:57:16:57:27 | remoteString | other.swift:46:25:46:79 | String(...) | other.swift:57:16:57:27 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | String(...) | user-provided value | +| sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:145:26:145:37 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:145:26:145:37 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:175:29:175:40 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:175:29:175:40 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:183:29:183:40 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:183:29:183:40 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:194:28:194:33 | buffer | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:194:28:194:33 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:202:31:202:36 | buffer | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:202:31:202:36 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | +| sqlite3_c_api.swift:210:31:210:36 | buffer | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:210:31:210:36 | buffer | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | edges | GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:106:41:106:52 | remoteString | provenance | | | GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:108:41:108:52 | remoteString | provenance | | @@ -85,6 +112,10 @@ edges | GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:122:41:122:52 | remoteString | provenance | | | GRDB.swift:104:6:104:17 | remoteString | GRDB.swift:124:41:124:52 | remoteString | provenance | | | GRDB.swift:104:25:104:79 | String(...) | GRDB.swift:104:6:104:17 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:132:39:132:50 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:135:46:135:57 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:138:56:138:67 | remoteString | provenance | | +| GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:141:45:141:56 | remoteString | provenance | | | GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:144:29:144:40 | remoteString | provenance | | | GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:145:29:145:40 | remoteString | provenance | | | GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:146:29:146:40 | remoteString | provenance | | @@ -94,6 +125,10 @@ edges | GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:150:29:150:40 | remoteString | provenance | | | GRDB.swift:130:6:130:17 | remoteString | GRDB.swift:151:29:151:40 | remoteString | provenance | | | GRDB.swift:130:26:130:80 | String(...) | GRDB.swift:130:6:130:17 | remoteString | provenance | | +| GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:166:32:166:43 | remoteString | provenance | | +| GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:167:39:167:50 | remoteString | provenance | | +| GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:168:49:168:60 | remoteString | provenance | | +| GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:169:38:169:49 | remoteString | provenance | | | GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:170:22:170:33 | remoteString | provenance | | | GRDB.swift:164:6:164:17 | remoteString | GRDB.swift:172:22:172:33 | remoteString | provenance | | | GRDB.swift:164:26:164:80 | String(...) | GRDB.swift:164:6:164:17 | remoteString | provenance | | @@ -115,6 +150,8 @@ edges | GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:234:36:234:47 | remoteString | provenance | | | GRDB.swift:222:6:222:17 | remoteString | GRDB.swift:235:36:235:47 | remoteString | provenance | | | GRDB.swift:222:26:222:80 | String(...) | GRDB.swift:222:6:222:17 | remoteString | provenance | | +| GRDB.swift:242:6:242:17 | remoteString | GRDB.swift:244:38:244:49 | remoteString | provenance | | +| GRDB.swift:242:26:242:80 | String(...) | GRDB.swift:242:6:242:17 | remoteString | provenance | | | GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:252:32:252:43 | remoteString | provenance | | | GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:253:32:253:43 | remoteString | provenance | | | GRDB.swift:250:6:250:17 | remoteString | GRDB.swift:254:32:254:43 | remoteString | provenance | | @@ -161,9 +198,46 @@ edges | GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:350:69:350:80 | remoteString | provenance | | | GRDB.swift:342:6:342:17 | remoteString | GRDB.swift:351:84:351:95 | remoteString | provenance | | | GRDB.swift:342:26:342:80 | String(...) | GRDB.swift:342:6:342:17 | remoteString | provenance | | +| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:65:21:65:32 | remoteString | provenance | | +| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:66:62:66:73 | remoteString | provenance | | +| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:67:60:67:71 | remoteString | provenance | | +| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:95:32:95:43 | remoteString | provenance | | +| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:132:20:132:31 | remoteString | provenance | | +| SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:62:6:62:17 | remoteString | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:73:17:73:28 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:100:29:100:40 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:103:29:103:40 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:106:29:106:40 | unsafeQuery1 | provenance | | +| SQLite.swift:65:21:65:32 | remoteString | SQLite.swift:65:6:65:17 | unsafeQuery1 | provenance | | +| SQLite.swift:66:6:66:17 | unsafeQuery2 | SQLite.swift:74:17:74:28 | unsafeQuery2 | provenance | | +| SQLite.swift:66:62:66:73 | remoteString | SQLite.swift:66:6:66:17 | unsafeQuery2 | provenance | | +| SQLite.swift:67:6:67:17 | unsafeQuery3 | SQLite.swift:75:17:75:28 | unsafeQuery3 | provenance | | +| SQLite.swift:67:6:67:17 | unsafeQuery3 | SQLite.swift:83:29:83:40 | unsafeQuery3 | provenance | | +| SQLite.swift:67:60:67:71 | remoteString | SQLite.swift:67:6:67:17 | unsafeQuery3 | provenance | | | other.swift:46:6:46:17 | remoteString | other.swift:50:22:50:33 | remoteString | provenance | | | other.swift:46:6:46:17 | remoteString | other.swift:57:16:57:27 | remoteString | provenance | | | other.swift:46:25:46:79 | String(...) | other.swift:46:6:46:17 | remoteString | provenance | | +| sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:125:21:125:32 | remoteString | provenance | | +| sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:126:62:126:73 | remoteString | provenance | | +| sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:127:60:127:71 | remoteString | provenance | | +| sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:122:6:122:17 | remoteString | provenance | | +| sqlite3_c_api.swift:125:6:125:17 | unsafeQuery1 | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | provenance | | +| sqlite3_c_api.swift:125:21:125:32 | remoteString | sqlite3_c_api.swift:125:6:125:17 | unsafeQuery1 | provenance | | +| sqlite3_c_api.swift:126:6:126:17 | unsafeQuery2 | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | provenance | | +| sqlite3_c_api.swift:126:62:126:73 | remoteString | sqlite3_c_api.swift:126:6:126:17 | unsafeQuery2 | provenance | | +| sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | sqlite3_c_api.swift:145:26:145:37 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | sqlite3_c_api.swift:175:29:175:40 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | sqlite3_c_api.swift:183:29:183:40 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | sqlite3_c_api.swift:189:13:189:24 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:127:60:127:71 | remoteString | sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | provenance | | +| sqlite3_c_api.swift:189:6:189:9 | data | sqlite3_c_api.swift:190:2:190:5 | data | provenance | | +| sqlite3_c_api.swift:189:13:189:24 | unsafeQuery3 | sqlite3_c_api.swift:189:13:189:58 | ... .data(...) | provenance | | +| sqlite3_c_api.swift:189:13:189:58 | ... .data(...) | sqlite3_c_api.swift:189:6:189:9 | data | provenance | | +| sqlite3_c_api.swift:190:2:190:5 | data | sqlite3_c_api.swift:190:21:190:26 | [post] buffer | provenance | | +| sqlite3_c_api.swift:190:21:190:26 | [post] buffer | sqlite3_c_api.swift:194:28:194:33 | buffer | provenance | | +| sqlite3_c_api.swift:190:21:190:26 | [post] buffer | sqlite3_c_api.swift:202:31:202:36 | buffer | provenance | | +| sqlite3_c_api.swift:190:21:190:26 | [post] buffer | sqlite3_c_api.swift:210:31:210:36 | buffer | provenance | | nodes | GRDB.swift:104:6:104:17 | remoteString | semmle.label | remoteString | | GRDB.swift:104:25:104:79 | String(...) | semmle.label | String(...) | @@ -177,6 +251,10 @@ nodes | GRDB.swift:124:41:124:52 | remoteString | semmle.label | remoteString | | GRDB.swift:130:6:130:17 | remoteString | semmle.label | remoteString | | GRDB.swift:130:26:130:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:132:39:132:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:135:46:135:57 | remoteString | semmle.label | remoteString | +| GRDB.swift:138:56:138:67 | remoteString | semmle.label | remoteString | +| GRDB.swift:141:45:141:56 | remoteString | semmle.label | remoteString | | GRDB.swift:144:29:144:40 | remoteString | semmle.label | remoteString | | GRDB.swift:145:29:145:40 | remoteString | semmle.label | remoteString | | GRDB.swift:146:29:146:40 | remoteString | semmle.label | remoteString | @@ -187,6 +265,10 @@ nodes | GRDB.swift:151:29:151:40 | remoteString | semmle.label | remoteString | | GRDB.swift:164:6:164:17 | remoteString | semmle.label | remoteString | | GRDB.swift:164:26:164:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:166:32:166:43 | remoteString | semmle.label | remoteString | +| GRDB.swift:167:39:167:50 | remoteString | semmle.label | remoteString | +| GRDB.swift:168:49:168:60 | remoteString | semmle.label | remoteString | +| GRDB.swift:169:38:169:49 | remoteString | semmle.label | remoteString | | GRDB.swift:170:22:170:33 | remoteString | semmle.label | remoteString | | GRDB.swift:172:22:172:33 | remoteString | semmle.label | remoteString | | GRDB.swift:185:6:185:17 | remoteString | semmle.label | remoteString | @@ -211,6 +293,9 @@ nodes | GRDB.swift:230:37:230:48 | remoteString | semmle.label | remoteString | | GRDB.swift:234:36:234:47 | remoteString | semmle.label | remoteString | | GRDB.swift:235:36:235:47 | remoteString | semmle.label | remoteString | +| GRDB.swift:242:6:242:17 | remoteString | semmle.label | remoteString | +| GRDB.swift:242:26:242:80 | String(...) | semmle.label | String(...) | +| GRDB.swift:244:38:244:49 | remoteString | semmle.label | remoteString | | GRDB.swift:250:6:250:17 | remoteString | semmle.label | remoteString | | GRDB.swift:250:26:250:80 | String(...) | semmle.label | String(...) | | GRDB.swift:252:32:252:43 | remoteString | semmle.label | remoteString | @@ -261,8 +346,47 @@ nodes | GRDB.swift:349:84:349:95 | remoteString | semmle.label | remoteString | | GRDB.swift:350:69:350:80 | remoteString | semmle.label | remoteString | | GRDB.swift:351:84:351:95 | remoteString | semmle.label | remoteString | +| SQLite.swift:62:6:62:17 | remoteString | semmle.label | remoteString | +| SQLite.swift:62:25:62:79 | String(...) | semmle.label | String(...) | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:65:21:65:32 | remoteString | semmle.label | remoteString | +| SQLite.swift:66:6:66:17 | unsafeQuery2 | semmle.label | unsafeQuery2 | +| SQLite.swift:66:62:66:73 | remoteString | semmle.label | remoteString | +| SQLite.swift:67:6:67:17 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| SQLite.swift:67:60:67:71 | remoteString | semmle.label | remoteString | +| SQLite.swift:73:17:73:28 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:74:17:74:28 | unsafeQuery2 | semmle.label | unsafeQuery2 | +| SQLite.swift:75:17:75:28 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| SQLite.swift:83:29:83:40 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| SQLite.swift:95:32:95:43 | remoteString | semmle.label | remoteString | +| SQLite.swift:100:29:100:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:103:29:103:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:106:29:106:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:132:20:132:31 | remoteString | semmle.label | remoteString | | other.swift:46:6:46:17 | remoteString | semmle.label | remoteString | | other.swift:46:25:46:79 | String(...) | semmle.label | String(...) | | other.swift:50:22:50:33 | remoteString | semmle.label | remoteString | | other.swift:57:16:57:27 | remoteString | semmle.label | remoteString | +| sqlite3_c_api.swift:122:6:122:17 | remoteString | semmle.label | remoteString | +| sqlite3_c_api.swift:122:26:122:80 | String(...) | semmle.label | String(...) | +| sqlite3_c_api.swift:125:6:125:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| sqlite3_c_api.swift:125:21:125:32 | remoteString | semmle.label | remoteString | +| sqlite3_c_api.swift:126:6:126:17 | unsafeQuery2 | semmle.label | unsafeQuery2 | +| sqlite3_c_api.swift:126:62:126:73 | remoteString | semmle.label | remoteString | +| sqlite3_c_api.swift:127:6:127:17 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:127:60:127:71 | remoteString | semmle.label | remoteString | +| sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | semmle.label | unsafeQuery2 | +| sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:145:26:145:37 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:175:29:175:40 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:183:29:183:40 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:189:6:189:9 | data | semmle.label | data | +| sqlite3_c_api.swift:189:13:189:24 | unsafeQuery3 | semmle.label | unsafeQuery3 | +| sqlite3_c_api.swift:189:13:189:58 | ... .data(...) | semmle.label | ... .data(...) | +| sqlite3_c_api.swift:190:2:190:5 | data | semmle.label | data | +| sqlite3_c_api.swift:190:21:190:26 | [post] buffer | semmle.label | [post] buffer | +| sqlite3_c_api.swift:194:28:194:33 | buffer | semmle.label | buffer | +| sqlite3_c_api.swift:202:31:202:36 | buffer | semmle.label | buffer | +| sqlite3_c_api.swift:210:31:210:36 | buffer | semmle.label | buffer | subpaths diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift index 024734e9367c..b4e7451b9163 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/sqlite3_c_api.swift @@ -119,7 +119,7 @@ func sqlite3_finalize( func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) { let localString = "user" - let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) // $ MISSING: Source + let remoteString = try! String(contentsOf: URL(string: "http://example.com/")!) // $ Source let remoteNumber = Int(remoteString) ?? 0 let unsafeQuery1 = remoteString @@ -130,9 +130,9 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) // --- exec --- - let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // $ MISSING: Alert - let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // $ MISSING: Alert - let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // $ MISSING: Alert + let result1 = sqlite3_exec(db, unsafeQuery1, nil, nil, nil) // $ Alert + let result2 = sqlite3_exec(db, unsafeQuery2, nil, nil, nil) // $ Alert + let result3 = sqlite3_exec(db, unsafeQuery3, nil, nil, nil) // $ Alert let result4 = sqlite3_exec(db, safeQuery1, nil, nil, nil) // GOOD let result5 = sqlite3_exec(db, safeQuery2, nil, nil, nil) // GOOD @@ -142,7 +142,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt1: OpaquePointer? - if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // $ MISSING: Alert + if (sqlite3_prepare(db, unsafeQuery3, -1, &stmt1, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt1) // ... } @@ -172,7 +172,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt4: OpaquePointer? - if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // $ MISSING: Alert + if (sqlite3_prepare_v2(db, unsafeQuery3, -1, &stmt4, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt4) // ... } @@ -180,7 +180,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt5: OpaquePointer? - if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // $ MISSING: Alert + if (sqlite3_prepare_v3(db, unsafeQuery3, -1, 0, &stmt5, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt5) // ... } @@ -191,7 +191,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt6: OpaquePointer? - if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // $ MISSING: Alert + if (sqlite3_prepare16(db, buffer, Int32(data.count), &stmt6, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt6) // ... } @@ -199,7 +199,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt7: OpaquePointer? - if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // $ MISSING: Alert + if (sqlite3_prepare16_v2(db, buffer, Int32(data.count), &stmt7, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt7) // ... } @@ -207,7 +207,7 @@ func test_sqlite3_c_api(db: OpaquePointer?, buffer: UnsafeMutablePointer) var stmt8: OpaquePointer? - if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // $ MISSING: Alert + if (sqlite3_prepare16_v3(db, buffer, Int32(data.count), 0, &stmt8, nil) == SQLITE_OK) { // $ Alert let result = sqlite3_step(stmt8) // ... } From 9f930b0d9692075b644ec5d0e0ff8feac1f7c893 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:51:31 +0000 Subject: [PATCH 05/13] unified: Recognize SQL sinks by resolved parameter names Match sql parameters using unified call resolution, accounting for positional and external argument names. Cover tainted non-SQL arguments as negative cases and preserve all previous alert paths. All four unified security query tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../queries/security/CWE-089/SqlInjection.ql | 27 +++++++++--- .../SqlInjection/SqlInjection.expected | 43 +++++++++++++++---- .../security/CWE-089/SqlInjection/other.swift | 18 ++++++-- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql index da771f313621..66011b49214f 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -11,6 +11,27 @@ */ import unified +private import codeql.unified.internal.typeinference.TypeInference as TypeInference + +private predicate heuristicSink(DataFlow::Node node) { + exists(Argument arg | + node.asExpr() = arg.getValue() and + ( + arg.getName() = "sql" + or + exists(CallExpr call, Parameter param | + arg = call.getAnArgument() and + param = TypeInference::resolveCallTarget(call).getAParameter() and + param.getPattern().(Identifier).getValue() = "sql" and + ( + arg.getPositionalIndex() = param.getPositionalIndex() + or + arg.getName() = param.getExternalName() + ) + ) + ) + ) +} /** * A taint configuration for tainted data that reaches a SQL sink. @@ -21,11 +42,7 @@ module SqlInjectionConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node node) { Models::isSink(node, "sql-injection") or - exists(Argument arg | - arg.getName() = "sql" and - node.asExpr() = arg.getValue() - ) - // TODO: Port the parameter-name heuristic from original Swift's SqlInjectionExtensions.qll. + heuristicSink(node) } predicate isBarrier(DataFlow::Node node) { diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index 87ea5bb2d593..e086ab4eb118 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -91,8 +91,15 @@ | SQLite.swift:103:29:103:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:103:29:103:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:106:29:106:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:106:29:106:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:132:20:132:31 | remoteString | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:132:20:132:31 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| other.swift:50:22:50:33 | remoteString | other.swift:46:25:46:79 | String(...) | other.swift:50:22:50:33 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | String(...) | user-provided value | -| other.swift:57:16:57:27 | remoteString | other.swift:46:25:46:79 | String(...) | other.swift:57:16:57:27 | remoteString | This query depends on a $@. | other.swift:46:25:46:79 | String(...) | user-provided value | +| other.swift:53:22:53:33 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:53:22:53:33 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:55:14:55:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:55:14:55:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:56:14:56:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:56:14:56:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:57:14:57:43 | NSString(...) | other.swift:49:25:49:79 | String(...) | other.swift:57:14:57:43 | NSString(...) | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:58:14:58:33 | TypeCastExpr | other.swift:49:25:49:79 | String(...) | other.swift:58:14:58:33 | TypeCastExpr | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:60:25:60:36 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:60:25:60:36 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:62:34:62:45 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:62:34:62:45 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:64:21:64:32 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:64:21:64:32 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:67:16:67:27 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:67:16:67:27 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | @@ -214,9 +221,18 @@ edges | SQLite.swift:67:6:67:17 | unsafeQuery3 | SQLite.swift:75:17:75:28 | unsafeQuery3 | provenance | | | SQLite.swift:67:6:67:17 | unsafeQuery3 | SQLite.swift:83:29:83:40 | unsafeQuery3 | provenance | | | SQLite.swift:67:60:67:71 | remoteString | SQLite.swift:67:6:67:17 | unsafeQuery3 | provenance | | -| other.swift:46:6:46:17 | remoteString | other.swift:50:22:50:33 | remoteString | provenance | | -| other.swift:46:6:46:17 | remoteString | other.swift:57:16:57:27 | remoteString | provenance | | -| other.swift:46:25:46:79 | String(...) | other.swift:46:6:46:17 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:53:22:53:33 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:55:14:55:25 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:56:14:56:25 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:57:31:57:42 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:58:14:58:25 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:60:25:60:36 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:62:34:62:45 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:64:21:64:32 | remoteString | provenance | | +| other.swift:49:6:49:17 | remoteString | other.swift:67:16:67:27 | remoteString | provenance | | +| other.swift:49:25:49:79 | String(...) | other.swift:49:6:49:17 | remoteString | provenance | | +| other.swift:57:31:57:42 | remoteString | other.swift:57:14:57:43 | NSString(...) | provenance | | +| other.swift:58:14:58:25 | remoteString | other.swift:58:14:58:33 | TypeCastExpr | provenance | | | sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:125:21:125:32 | remoteString | provenance | | | sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:126:62:126:73 | remoteString | provenance | | | sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:127:60:127:71 | remoteString | provenance | | @@ -363,10 +379,19 @@ nodes | SQLite.swift:103:29:103:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:106:29:106:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:132:20:132:31 | remoteString | semmle.label | remoteString | -| other.swift:46:6:46:17 | remoteString | semmle.label | remoteString | -| other.swift:46:25:46:79 | String(...) | semmle.label | String(...) | -| other.swift:50:22:50:33 | remoteString | semmle.label | remoteString | -| other.swift:57:16:57:27 | remoteString | semmle.label | remoteString | +| other.swift:49:6:49:17 | remoteString | semmle.label | remoteString | +| other.swift:49:25:49:79 | String(...) | semmle.label | String(...) | +| other.swift:53:22:53:33 | remoteString | semmle.label | remoteString | +| other.swift:55:14:55:25 | remoteString | semmle.label | remoteString | +| other.swift:56:14:56:25 | remoteString | semmle.label | remoteString | +| other.swift:57:14:57:43 | NSString(...) | semmle.label | NSString(...) | +| other.swift:57:31:57:42 | remoteString | semmle.label | remoteString | +| other.swift:58:14:58:25 | remoteString | semmle.label | remoteString | +| other.swift:58:14:58:33 | TypeCastExpr | semmle.label | TypeCastExpr | +| other.swift:60:25:60:36 | remoteString | semmle.label | remoteString | +| other.swift:62:34:62:45 | remoteString | semmle.label | remoteString | +| other.swift:64:21:64:32 | remoteString | semmle.label | remoteString | +| other.swift:67:16:67:27 | remoteString | semmle.label | remoteString | | sqlite3_c_api.swift:122:6:122:17 | remoteString | semmle.label | remoteString | | sqlite3_c_api.swift:122:26:122:80 | String(...) | semmle.label | String(...) | | sqlite3_c_api.swift:125:6:125:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift index 3e4d6634b800..a74ac0873fb4 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift @@ -29,6 +29,9 @@ class MyDatabase { func execute2(_ sql: String?) { } func execute3(_ sql: NSString) { } func execute4(_ sql: Sql) { } + func execute5(_ context: String, _ sql: String) { } + func execute6(context: String, _ sql: String) { } + func queryCode(code sql: String) { } func query(sql: String) { } func query(sqlLiteral: String) { } @@ -49,10 +52,17 @@ func test_heuristic(db: MyDatabase) throws { _ = MyDatabase(sql: "some_fixed_sql") // GOOD _ = MyDatabase(sql: remoteString) // $ Alert - db.execute1(remoteString) // $ MISSING: Alert - db.execute2(remoteString) // $ MISSING: Alert - db.execute3(NSString(string: remoteString)) // $ MISSING: Alert - db.execute4(remoteString as! Sql) // $ MISSING: Alert + db.execute1(remoteString) // $ Alert + db.execute2(remoteString) // $ Alert + db.execute3(NSString(string: remoteString)) // $ Alert + db.execute4(remoteString as! Sql) // $ Alert + + db.execute5("context", remoteString) // $ Alert + db.execute5(remoteString, "SELECT 1") // GOOD + db.execute6(context: "context", remoteString) // $ Alert + db.execute6(context: remoteString, "SELECT 1") // GOOD + db.queryCode(code: remoteString) // $ Alert + db.queryCode(code: "SELECT 1") // GOOD db.query(sql: remoteString) // $ Alert db.query(sqlLiteral: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] From 9a3c558aa21abd4647d87b1bf112d3c65d29f22d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:53:05 +0000 Subject: [PATCH 06/13] unified: Resolve SQLite run and scalar SQL sinks precisely Use resolved Connection targets until legacy models support receiver checks, avoiding false positives on Statement bindings. Cover omitted and multiple variadic bindings with unsafe SQL and safe parameterized comparisons. Preserve all preceding detections; security and model tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- unified/ql/lib/ext/legacy-swift.model.yml | 2 +- .../queries/security/CWE-089/SqlInjection.ql | 12 ++++++++ .../CWE-089/SqlInjection/SQLite.swift | 23 +++++++++----- .../SqlInjection/SqlInjection.expected | 30 +++++++++++++++++++ 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/unified/ql/lib/ext/legacy-swift.model.yml b/unified/ql/lib/ext/legacy-swift.model.yml index 8bf90325cd63..875fef3a0a25 100644 --- a/unified/ql/lib/ext/legacy-swift.model.yml +++ b/unified/ql/lib/ext/legacy-swift.model.yml @@ -247,7 +247,7 @@ extensions: - ["", "Connection", true, "execute(_:)", "", "", "Argument[0]", "sql-injection", "manual"] - ["", "Connection", true, "prepare(_:)", "", "", "Argument[0]", "sql-injection", "manual"] - ["", "Connection", true, "prepare(_:_:)", "", "", "Argument[0]", "sql-injection", "manual"] - # TODO: Model Connection.run/scalar once receiver types distinguish them from safe Statement bindings. + # Connection.run/scalar need receiver checks; see SqlInjection.ql until legacy selectors support them. - ["", "Statement", true, "init(_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] - ["", "SQLRequest", true, "init(stringLiteral:)", "", "", "Argument[stringLiteral:]", "sql-injection", "manual"] - ["", "SQLRequest", true, "init(unicodeScalarLiteral:)", "", "", "Argument[unicodeScalarLiteral:]", "sql-injection", "manual"] diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql index 66011b49214f..eba913cf02d8 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -13,6 +13,16 @@ import unified private import codeql.unified.internal.typeinference.TypeInference as TypeInference +private predicate sqliteSwiftSink(DataFlow::Node node) { + // TODO: Use MaD once legacy selectors distinguish Connection methods from Statement bindings. + exists(CallExpr call, FunctionDeclaration target | + target = TypeInference::resolveCallTarget(call) and + target.getEnclosingClass().getName() = "Connection" and + target.getName() = ["run", "scalar"] and + node.asExpr() = call.getArgument(0).getValue() + ) +} + private predicate heuristicSink(DataFlow::Node node) { exists(Argument arg | node.asExpr() = arg.getValue() and @@ -42,6 +52,8 @@ module SqlInjectionConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node node) { Models::isSink(node, "sql-injection") or + sqliteSwiftSink(node) + or heuristicSink(node) } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift index e3c8347700c6..2d70990dfa63 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift @@ -106,17 +106,17 @@ func test_sqlite_swift_api(db: Connection) throws { let stmt8 = try db.prepare(unsafeQuery1, ["username": ""]) // $ Alert try stmt8.run() - try db.run(unsafeQuery1, "") // $ MISSING: Alert + try db.run(unsafeQuery1, "") // $ Alert - try db.run(unsafeQuery1, [""]) // $ MISSING: Alert + try db.run(unsafeQuery1, [""]) // $ Alert - try db.run(unsafeQuery1, ["username": ""]) // $ MISSING: Alert + try db.run(unsafeQuery1, ["username": ""]) // $ Alert - try db.scalar(unsafeQuery1, "") // $ MISSING: Alert + try db.scalar(unsafeQuery1, "") // $ Alert - try db.scalar(unsafeQuery1, [""]) // $ MISSING: Alert + try db.scalar(unsafeQuery1, [""]) // $ Alert - try db.scalar(unsafeQuery1, ["username": ""]) // $ MISSING: Alert + try db.scalar(unsafeQuery1, ["username": ""]) // $ Alert let stmt9 = try db.prepare(varQuery) // GOOD try stmt9.bind(remoteString) // GOOD @@ -131,8 +131,15 @@ func test_sqlite_swift_api(db: Connection) throws { try Statement(db, remoteString).run() // $ Alert - try db.run(unsafeQuery1) // $ MISSING: Alert + try db.run(unsafeQuery1) // $ Alert try db.run(safeQuery1) // GOOD - try db.scalar(unsafeQuery1) // $ MISSING: Alert + try db.scalar(unsafeQuery1) // $ Alert try db.scalar(safeQuery1) // GOOD + + try db.run(unsafeQuery1, localString, remoteString) // $ Alert + try db.run(varQuery, localString, remoteString) // GOOD + try db.scalar(unsafeQuery1, localString, remoteString) // $ Alert + try db.scalar(varQuery, localString, remoteString) // GOOD + try stmt9.run(localString, remoteString) // GOOD + try stmt9.scalar(localString, remoteString) // GOOD } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index e086ab4eb118..f4e7f7db9493 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -90,7 +90,17 @@ | SQLite.swift:100:29:100:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:100:29:100:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:103:29:103:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:103:29:103:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:106:29:106:40 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:106:29:106:40 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:109:13:109:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:109:13:109:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:111:13:111:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:111:13:111:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:113:13:113:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:113:13:113:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:115:16:115:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:115:16:115:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:117:16:117:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:117:16:117:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:119:16:119:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:119:16:119:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:132:20:132:31 | remoteString | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:132:20:132:31 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:134:13:134:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:134:13:134:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:136:16:136:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:136:16:136:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:139:13:139:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:139:13:139:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:141:16:141:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:141:16:141:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | other.swift:53:22:53:33 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:53:22:53:33 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | | other.swift:55:14:55:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:55:14:55:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | | other.swift:56:14:56:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:56:14:56:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | @@ -215,6 +225,16 @@ edges | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:100:29:100:40 | unsafeQuery1 | provenance | | | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:103:29:103:40 | unsafeQuery1 | provenance | | | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:106:29:106:40 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:109:13:109:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:111:13:111:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:113:13:113:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:115:16:115:27 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:117:16:117:27 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:119:16:119:27 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:134:13:134:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:136:16:136:27 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:139:13:139:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:141:16:141:27 | unsafeQuery1 | provenance | | | SQLite.swift:65:21:65:32 | remoteString | SQLite.swift:65:6:65:17 | unsafeQuery1 | provenance | | | SQLite.swift:66:6:66:17 | unsafeQuery2 | SQLite.swift:74:17:74:28 | unsafeQuery2 | provenance | | | SQLite.swift:66:62:66:73 | remoteString | SQLite.swift:66:6:66:17 | unsafeQuery2 | provenance | | @@ -378,7 +398,17 @@ nodes | SQLite.swift:100:29:100:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:103:29:103:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:106:29:106:40 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:109:13:109:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:111:13:111:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:113:13:113:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:115:16:115:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:117:16:117:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:119:16:119:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:132:20:132:31 | remoteString | semmle.label | remoteString | +| SQLite.swift:134:13:134:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:136:16:136:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:139:13:139:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:141:16:141:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | | other.swift:49:6:49:17 | remoteString | semmle.label | remoteString | | other.swift:49:25:49:79 | String(...) | semmle.label | String(...) | | other.swift:53:22:53:33 | remoteString | semmle.label | remoteString | From 92ebcc079a29c1411559deacf0e696c0284e326b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:57:12 +0000 Subject: [PATCH 07/13] unified: Clarify SQL injection prevention guidance Explain that preparing interpolated SQL does not prevent injection and prefer parameter binding over fragile escaping. Verify both external references, XML parsing and referenced Swift samples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp b/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp index 6975d95d48a1..fb076cde163e 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp @@ -12,7 +12,7 @@ If a database query (such as a SQL query) is built from user-provided data witho

    -Most database connector libraries offer a way to safely embed untrusted data into a query using query parameters or prepared statements. You should use these features to build queries, rather than string concatenation or similar methods. You can also escape (sanitize) user-controlled strings so that they can be included directly in an SQL command. A library function should be used for escaping, because this approach is only safe if the escaping function is robust against all possible inputs. +Most database connector libraries offer a way to safely embed untrusted data into a query using query parameters or prepared statements. Use these features to bind untrusted values separately from the SQL syntax, rather than using string concatenation or interpolation. Preparing a statement after inserting untrusted data into its SQL text does not prevent injection. Escaping is error-prone and database-specific, so prefer parameter binding rather than implementing your own escaping routine.

    From 42158ff984ed8a89cfdb0357f2a2c9d6515d9c47 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 17:53:50 +0000 Subject: [PATCH 08/13] unified: move the new test cases into organization of existing test cases. --- .../CWE-089/SqlInjection/SQLite.swift | 9 +++--- .../SqlInjection/SqlInjection.expected | 30 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift index 2d70990dfa63..b58aa304e143 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SQLite.swift @@ -125,21 +125,20 @@ func test_sqlite_swift_api(db: Connection) throws { try stmt9.run(remoteString) // GOOD try stmt9.run([remoteString]) // GOOD try stmt9.run(["username": remoteString]) // GOOD + try stmt9.run(localString, remoteString) // GOOD try stmt9.scalar(remoteString) // GOOD try stmt9.scalar([remoteString]) // GOOD try stmt9.scalar(["username": remoteString]) // GOOD + try stmt9.scalar(localString, remoteString) // GOOD try Statement(db, remoteString).run() // $ Alert try db.run(unsafeQuery1) // $ Alert try db.run(safeQuery1) // GOOD - try db.scalar(unsafeQuery1) // $ Alert - try db.scalar(safeQuery1) // GOOD - try db.run(unsafeQuery1, localString, remoteString) // $ Alert try db.run(varQuery, localString, remoteString) // GOOD + try db.scalar(unsafeQuery1) // $ Alert + try db.scalar(safeQuery1) // GOOD try db.scalar(unsafeQuery1, localString, remoteString) // $ Alert try db.scalar(varQuery, localString, remoteString) // GOOD - try stmt9.run(localString, remoteString) // GOOD - try stmt9.scalar(localString, remoteString) // GOOD } diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index f4e7f7db9493..8ef94dccd174 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -96,11 +96,11 @@ | SQLite.swift:115:16:115:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:115:16:115:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:117:16:117:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:117:16:117:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:119:16:119:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:119:16:119:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| SQLite.swift:132:20:132:31 | remoteString | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:132:20:132:31 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| SQLite.swift:134:13:134:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:134:13:134:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| SQLite.swift:136:16:136:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:136:16:136:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| SQLite.swift:139:13:139:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:139:13:139:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| SQLite.swift:141:16:141:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:141:16:141:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:134:20:134:31 | remoteString | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:134:20:134:31 | remoteString | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:136:13:136:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:136:13:136:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:138:13:138:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:138:13:138:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:140:16:140:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:140:16:140:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | +| SQLite.swift:142:16:142:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:142:16:142:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | other.swift:53:22:53:33 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:53:22:53:33 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | | other.swift:55:14:55:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:55:14:55:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | | other.swift:56:14:56:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:56:14:56:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | @@ -219,7 +219,7 @@ edges | SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:66:62:66:73 | remoteString | provenance | | | SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:67:60:67:71 | remoteString | provenance | | | SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:95:32:95:43 | remoteString | provenance | | -| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:132:20:132:31 | remoteString | provenance | | +| SQLite.swift:62:6:62:17 | remoteString | SQLite.swift:134:20:134:31 | remoteString | provenance | | | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:62:6:62:17 | remoteString | provenance | | | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:73:17:73:28 | unsafeQuery1 | provenance | | | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:100:29:100:40 | unsafeQuery1 | provenance | | @@ -231,10 +231,10 @@ edges | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:115:16:115:27 | unsafeQuery1 | provenance | | | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:117:16:117:27 | unsafeQuery1 | provenance | | | SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:119:16:119:27 | unsafeQuery1 | provenance | | -| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:134:13:134:24 | unsafeQuery1 | provenance | | -| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:136:16:136:27 | unsafeQuery1 | provenance | | -| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:139:13:139:24 | unsafeQuery1 | provenance | | -| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:141:16:141:27 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:136:13:136:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:138:13:138:24 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:140:16:140:27 | unsafeQuery1 | provenance | | +| SQLite.swift:65:6:65:17 | unsafeQuery1 | SQLite.swift:142:16:142:27 | unsafeQuery1 | provenance | | | SQLite.swift:65:21:65:32 | remoteString | SQLite.swift:65:6:65:17 | unsafeQuery1 | provenance | | | SQLite.swift:66:6:66:17 | unsafeQuery2 | SQLite.swift:74:17:74:28 | unsafeQuery2 | provenance | | | SQLite.swift:66:62:66:73 | remoteString | SQLite.swift:66:6:66:17 | unsafeQuery2 | provenance | | @@ -404,11 +404,11 @@ nodes | SQLite.swift:115:16:115:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:117:16:117:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:119:16:119:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:132:20:132:31 | remoteString | semmle.label | remoteString | -| SQLite.swift:134:13:134:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:136:16:136:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:139:13:139:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| SQLite.swift:141:16:141:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:134:20:134:31 | remoteString | semmle.label | remoteString | +| SQLite.swift:136:13:136:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:138:13:138:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:140:16:140:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | +| SQLite.swift:142:16:142:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | | other.swift:49:6:49:17 | remoteString | semmle.label | remoteString | | other.swift:49:25:49:79 | String(...) | semmle.label | String(...) | | other.swift:53:22:53:33 | remoteString | semmle.label | remoteString | From c0afa1f19f61eff26dca8a7a01602d8e8bacbfba Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 17:35:56 +0000 Subject: [PATCH 09/13] unified: Add / edit doc and comments. --- unified/ql/lib/ext/legacy-swift.model.yml | 1 - unified/ql/src/queries/security/CWE-089/SqlInjection.ql | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/unified/ql/lib/ext/legacy-swift.model.yml b/unified/ql/lib/ext/legacy-swift.model.yml index 875fef3a0a25..e2f0873faa72 100644 --- a/unified/ql/lib/ext/legacy-swift.model.yml +++ b/unified/ql/lib/ext/legacy-swift.model.yml @@ -247,7 +247,6 @@ extensions: - ["", "Connection", true, "execute(_:)", "", "", "Argument[0]", "sql-injection", "manual"] - ["", "Connection", true, "prepare(_:)", "", "", "Argument[0]", "sql-injection", "manual"] - ["", "Connection", true, "prepare(_:_:)", "", "", "Argument[0]", "sql-injection", "manual"] - # Connection.run/scalar need receiver checks; see SqlInjection.ql until legacy selectors support them. - ["", "Statement", true, "init(_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] - ["", "SQLRequest", true, "init(stringLiteral:)", "", "", "Argument[stringLiteral:]", "sql-injection", "manual"] - ["", "SQLRequest", true, "init(unicodeScalarLiteral:)", "", "", "Argument[unicodeScalarLiteral:]", "sql-injection", "manual"] diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql index eba913cf02d8..1109280d071b 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -23,12 +23,19 @@ private predicate sqliteSwiftSink(DataFlow::Node node) { ) } +/** + * An SQL injection sink that is determined by imprecise methods. + */ private predicate heuristicSink(DataFlow::Node node) { exists(Argument arg | node.asExpr() = arg.getValue() and ( + // by argument name arg.getName() = "sql" or + // by parameter name + // TODO: can we simplify this whole case since we have `getExternalName`? + // TODO: make a helper predicate `Argument.getParameter` for this? exists(CallExpr call, Parameter param | arg = call.getAnArgument() and param = TypeInference::resolveCallTarget(call).getAParameter() and From b1a43f79dfda74cec7e21e28cd75b9cc3c160221 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:33:24 +0000 Subject: [PATCH 10/13] unified: Add a few more test cases around parameter names and call target confusion. --- .../SqlInjection/SqlInjection.expected | 72 ++++++++++--------- .../security/CWE-089/SqlInjection/other.swift | 13 +++- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index 8ef94dccd174..ff7174db2123 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -101,15 +101,17 @@ | SQLite.swift:138:13:138:24 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:138:13:138:24 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:140:16:140:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:140:16:140:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | | SQLite.swift:142:16:142:27 | unsafeQuery1 | SQLite.swift:62:25:62:79 | String(...) | SQLite.swift:142:16:142:27 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | String(...) | user-provided value | -| other.swift:53:22:53:33 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:53:22:53:33 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:55:14:55:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:55:14:55:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:56:14:56:25 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:56:14:56:25 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:57:14:57:43 | NSString(...) | other.swift:49:25:49:79 | String(...) | other.swift:57:14:57:43 | NSString(...) | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:58:14:58:33 | TypeCastExpr | other.swift:49:25:49:79 | String(...) | other.swift:58:14:58:33 | TypeCastExpr | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:60:25:60:36 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:60:25:60:36 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:62:34:62:45 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:62:34:62:45 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:64:21:64:32 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:64:21:64:32 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | -| other.swift:67:16:67:27 | remoteString | other.swift:49:25:49:79 | String(...) | other.swift:67:16:67:27 | remoteString | This query depends on a $@. | other.swift:49:25:49:79 | String(...) | user-provided value | +| other.swift:55:22:55:33 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:55:22:55:33 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:57:14:57:25 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:57:14:57:25 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:58:14:58:25 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:58:14:58:25 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:59:14:59:43 | NSString(...) | other.swift:51:25:51:79 | String(...) | other.swift:59:14:59:43 | NSString(...) | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:60:14:60:33 | TypeCastExpr | other.swift:51:25:51:79 | String(...) | other.swift:60:14:60:33 | TypeCastExpr | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:62:25:62:36 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:62:25:62:36 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:64:34:64:45 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:64:34:64:45 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:67:21:67:32 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:67:21:67:32 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:69:21:69:32 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:69:21:69:32 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:72:16:72:27 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:72:16:72:27 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:76:34:76:45 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:76:34:76:45 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | @@ -241,18 +243,20 @@ edges | SQLite.swift:67:6:67:17 | unsafeQuery3 | SQLite.swift:75:17:75:28 | unsafeQuery3 | provenance | | | SQLite.swift:67:6:67:17 | unsafeQuery3 | SQLite.swift:83:29:83:40 | unsafeQuery3 | provenance | | | SQLite.swift:67:60:67:71 | remoteString | SQLite.swift:67:6:67:17 | unsafeQuery3 | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:53:22:53:33 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:55:14:55:25 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:56:14:56:25 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:57:31:57:42 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:58:14:58:25 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:60:25:60:36 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:62:34:62:45 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:64:21:64:32 | remoteString | provenance | | -| other.swift:49:6:49:17 | remoteString | other.swift:67:16:67:27 | remoteString | provenance | | -| other.swift:49:25:49:79 | String(...) | other.swift:49:6:49:17 | remoteString | provenance | | -| other.swift:57:31:57:42 | remoteString | other.swift:57:14:57:43 | NSString(...) | provenance | | -| other.swift:58:14:58:25 | remoteString | other.swift:58:14:58:33 | TypeCastExpr | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:55:22:55:33 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:57:14:57:25 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:58:14:58:25 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:59:31:59:42 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:60:14:60:25 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:62:25:62:36 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:64:34:64:45 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:67:21:67:32 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:69:21:69:32 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:72:16:72:27 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:76:34:76:45 | remoteString | provenance | | +| other.swift:51:25:51:79 | String(...) | other.swift:51:6:51:17 | remoteString | provenance | | +| other.swift:59:31:59:42 | remoteString | other.swift:59:14:59:43 | NSString(...) | provenance | | +| other.swift:60:14:60:25 | remoteString | other.swift:60:14:60:33 | TypeCastExpr | provenance | | | sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:125:21:125:32 | remoteString | provenance | | | sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:126:62:126:73 | remoteString | provenance | | | sqlite3_c_api.swift:122:6:122:17 | remoteString | sqlite3_c_api.swift:127:60:127:71 | remoteString | provenance | | @@ -409,19 +413,21 @@ nodes | SQLite.swift:138:13:138:24 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:140:16:140:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:142:16:142:27 | unsafeQuery1 | semmle.label | unsafeQuery1 | -| other.swift:49:6:49:17 | remoteString | semmle.label | remoteString | -| other.swift:49:25:49:79 | String(...) | semmle.label | String(...) | -| other.swift:53:22:53:33 | remoteString | semmle.label | remoteString | -| other.swift:55:14:55:25 | remoteString | semmle.label | remoteString | -| other.swift:56:14:56:25 | remoteString | semmle.label | remoteString | -| other.swift:57:14:57:43 | NSString(...) | semmle.label | NSString(...) | -| other.swift:57:31:57:42 | remoteString | semmle.label | remoteString | +| other.swift:51:6:51:17 | remoteString | semmle.label | remoteString | +| other.swift:51:25:51:79 | String(...) | semmle.label | String(...) | +| other.swift:55:22:55:33 | remoteString | semmle.label | remoteString | +| other.swift:57:14:57:25 | remoteString | semmle.label | remoteString | | other.swift:58:14:58:25 | remoteString | semmle.label | remoteString | -| other.swift:58:14:58:33 | TypeCastExpr | semmle.label | TypeCastExpr | -| other.swift:60:25:60:36 | remoteString | semmle.label | remoteString | -| other.swift:62:34:62:45 | remoteString | semmle.label | remoteString | -| other.swift:64:21:64:32 | remoteString | semmle.label | remoteString | -| other.swift:67:16:67:27 | remoteString | semmle.label | remoteString | +| other.swift:59:14:59:43 | NSString(...) | semmle.label | NSString(...) | +| other.swift:59:31:59:42 | remoteString | semmle.label | remoteString | +| other.swift:60:14:60:25 | remoteString | semmle.label | remoteString | +| other.swift:60:14:60:33 | TypeCastExpr | semmle.label | TypeCastExpr | +| other.swift:62:25:62:36 | remoteString | semmle.label | remoteString | +| other.swift:64:34:64:45 | remoteString | semmle.label | remoteString | +| other.swift:67:21:67:32 | remoteString | semmle.label | remoteString | +| other.swift:69:21:69:32 | remoteString | semmle.label | remoteString | +| other.swift:72:16:72:27 | remoteString | semmle.label | remoteString | +| other.swift:76:34:76:45 | remoteString | semmle.label | remoteString | | sqlite3_c_api.swift:122:6:122:17 | remoteString | semmle.label | remoteString | | sqlite3_c_api.swift:122:26:122:80 | String(...) | semmle.label | String(...) | | sqlite3_c_api.swift:125:6:125:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift index a74ac0873fb4..3d6fb2f02837 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift @@ -32,11 +32,13 @@ class MyDatabase { func execute5(_ context: String, _ sql: String) { } func execute6(context: String, _ sql: String) { } func queryCode(code sql: String) { } + func queryCode2(sql code: String) { } func query(sql: String) { } func query(sqlLiteral: String) { } func query(sqlStatement: String) { } func query(sqliteStatement: String) { } + func query(config: String, sql: String) { } // non-examples func doSomething(sqlIndex: Int) { } @@ -61,13 +63,18 @@ func test_heuristic(db: MyDatabase) throws { db.execute5(remoteString, "SELECT 1") // GOOD db.execute6(context: "context", remoteString) // $ Alert db.execute6(context: remoteString, "SELECT 1") // GOOD + db.queryCode(code: remoteString) // $ Alert db.queryCode(code: "SELECT 1") // GOOD + db.queryCode2(sql: remoteString) // $ Alert + db.queryCode2(sql: "SELECT 1") // GOOD db.query(sql: remoteString) // $ Alert - db.query(sqlLiteral: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] - db.query(sqlStatement: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] - db.query(sqliteStatement: remoteString) // $ MISSING: Alert // BAD [NOT DETECTED] + db.query(sqlLiteral: remoteString) // $ MISSING: Alert + db.query(sqlStatement: remoteString) // $ MISSING: Alert + db.query(sqliteStatement: remoteString) // $ MISSING: Alert + db.query(config: "config", sql: remoteString) // $ Alert + db.query(config: remoteString, sql: "SELECT 1") // GOOD db.doSomething(sqlIndex: Int(remoteString) ?? 0) // GOOD db.doSomething(sqliteContext: remoteString as! Sql) // GOOD From 2bcd3ab7c02908fee9f80c578bd1eaa2957de394 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:04:37 +0000 Subject: [PATCH 11/13] unified: Clean up heuristicSink predicate. --- .../src/queries/security/CWE-089/SqlInjection.ql | 14 ++++---------- .../CWE-089/SqlInjection/SqlInjection.expected | 12 ++++++++++++ .../security/CWE-089/SqlInjection/other.swift | 8 ++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql index 1109280d071b..62872779069d 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.ql +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.ql @@ -34,17 +34,11 @@ private predicate heuristicSink(DataFlow::Node node) { arg.getName() = "sql" or // by parameter name - // TODO: can we simplify this whole case since we have `getExternalName`? // TODO: make a helper predicate `Argument.getParameter` for this? - exists(CallExpr call, Parameter param | - arg = call.getAnArgument() and - param = TypeInference::resolveCallTarget(call).getAParameter() and - param.getPattern().(Identifier).getValue() = "sql" and - ( - arg.getPositionalIndex() = param.getPositionalIndex() - or - arg.getName() = param.getExternalName() - ) + exists(CallExpr call, int index, Parameter param | + arg = call.getArgument(index) and + param = TypeInference::resolveCallTarget(call).getParameter(index) and + param.getPattern().(Identifier).getValue() = "sql" ) ) ) diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected index ff7174db2123..57ba80442944 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/SqlInjection.expected @@ -111,7 +111,11 @@ | other.swift:67:21:67:32 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:67:21:67:32 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | | other.swift:69:21:69:32 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:69:21:69:32 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | | other.swift:72:16:72:27 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:72:16:72:27 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:73:23:73:34 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:73:23:73:34 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:74:25:74:36 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:74:25:74:36 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:75:28:75:39 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:75:28:75:39 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | | other.swift:76:34:76:45 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:76:34:76:45 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | +| other.swift:77:19:77:30 | remoteString | other.swift:51:25:51:79 | String(...) | other.swift:77:19:77:30 | remoteString | This query depends on a $@. | other.swift:51:25:51:79 | String(...) | user-provided value | | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:133:33:133:44 | unsafeQuery1 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:134:33:134:44 | unsafeQuery2 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | sqlite3_c_api.swift:122:26:122:80 | String(...) | sqlite3_c_api.swift:135:33:135:44 | unsafeQuery3 | This query depends on a $@. | sqlite3_c_api.swift:122:26:122:80 | String(...) | user-provided value | @@ -253,7 +257,11 @@ edges | other.swift:51:6:51:17 | remoteString | other.swift:67:21:67:32 | remoteString | provenance | | | other.swift:51:6:51:17 | remoteString | other.swift:69:21:69:32 | remoteString | provenance | | | other.swift:51:6:51:17 | remoteString | other.swift:72:16:72:27 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:73:23:73:34 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:74:25:74:36 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:75:28:75:39 | remoteString | provenance | | | other.swift:51:6:51:17 | remoteString | other.swift:76:34:76:45 | remoteString | provenance | | +| other.swift:51:6:51:17 | remoteString | other.swift:77:19:77:30 | remoteString | provenance | | | other.swift:51:25:51:79 | String(...) | other.swift:51:6:51:17 | remoteString | provenance | | | other.swift:59:31:59:42 | remoteString | other.swift:59:14:59:43 | NSString(...) | provenance | | | other.swift:60:14:60:25 | remoteString | other.swift:60:14:60:33 | TypeCastExpr | provenance | | @@ -427,7 +435,11 @@ nodes | other.swift:67:21:67:32 | remoteString | semmle.label | remoteString | | other.swift:69:21:69:32 | remoteString | semmle.label | remoteString | | other.swift:72:16:72:27 | remoteString | semmle.label | remoteString | +| other.swift:73:23:73:34 | remoteString | semmle.label | remoteString | +| other.swift:74:25:74:36 | remoteString | semmle.label | remoteString | +| other.swift:75:28:75:39 | remoteString | semmle.label | remoteString | | other.swift:76:34:76:45 | remoteString | semmle.label | remoteString | +| other.swift:77:19:77:30 | remoteString | semmle.label | remoteString | | sqlite3_c_api.swift:122:6:122:17 | remoteString | semmle.label | remoteString | | sqlite3_c_api.swift:122:26:122:80 | String(...) | semmle.label | String(...) | | sqlite3_c_api.swift:125:6:125:17 | unsafeQuery1 | semmle.label | unsafeQuery1 | diff --git a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift index 3d6fb2f02837..d4cb09877eb3 100644 --- a/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift +++ b/unified/ql/test/query-tests/security/CWE-089/SqlInjection/other.swift @@ -70,11 +70,11 @@ func test_heuristic(db: MyDatabase) throws { db.queryCode2(sql: "SELECT 1") // GOOD db.query(sql: remoteString) // $ Alert - db.query(sqlLiteral: remoteString) // $ MISSING: Alert - db.query(sqlStatement: remoteString) // $ MISSING: Alert - db.query(sqliteStatement: remoteString) // $ MISSING: Alert + db.query(sqlLiteral: remoteString) // $ Alert + db.query(sqlStatement: remoteString) // $ Alert + db.query(sqliteStatement: remoteString) // $ Alert db.query(config: "config", sql: remoteString) // $ Alert - db.query(config: remoteString, sql: "SELECT 1") // GOOD + db.query(config: remoteString, sql: "SELECT 1") // $ SPURIOUS: Alert db.doSomething(sqlIndex: Int(remoteString) ?? 0) // GOOD db.doSomething(sqliteContext: remoteString as! Sql) // GOOD From 5921954a4143c1616bdc192a3755501253039219 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:56:18 +0000 Subject: [PATCH 12/13] unified: Undo the .qhelp change. I'm not convinced it's meaningfully better. --- unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp b/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp index fb076cde163e..6975d95d48a1 100644 --- a/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp +++ b/unified/ql/src/queries/security/CWE-089/SqlInjection.qhelp @@ -12,7 +12,7 @@ If a database query (such as a SQL query) is built from user-provided data witho

    -Most database connector libraries offer a way to safely embed untrusted data into a query using query parameters or prepared statements. Use these features to bind untrusted values separately from the SQL syntax, rather than using string concatenation or interpolation. Preparing a statement after inserting untrusted data into its SQL text does not prevent injection. Escaping is error-prone and database-specific, so prefer parameter binding rather than implementing your own escaping routine. +Most database connector libraries offer a way to safely embed untrusted data into a query using query parameters or prepared statements. You should use these features to build queries, rather than string concatenation or similar methods. You can also escape (sanitize) user-controlled strings so that they can be included directly in an SQL command. A library function should be used for escaping, because this approach is only safe if the escaping function is robust against all possible inputs.

    From c38ed2ae7e58a76f6090f668072fececd6401153 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:31:17 +0000 Subject: [PATCH 13/13] unified: Remove database-store sinks that duplicate sql-sinks (the database storage query can just match both patterns, as the Rust version does; any potential SQL injection is input to a database). --- unified/ql/lib/ext/legacy-swift.model.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/unified/ql/lib/ext/legacy-swift.model.yml b/unified/ql/lib/ext/legacy-swift.model.yml index e2f0873faa72..2a9e33425f5e 100644 --- a/unified/ql/lib/ext/legacy-swift.model.yml +++ b/unified/ql/lib/ext/legacy-swift.model.yml @@ -111,13 +111,6 @@ extensions: - ["", "", false, "sqlite3_bind_text16(_:_:_:_:_:)", "", "", "Argument[2]", "database-store", "manual"] - ["", "", false, "sqlite3_bind_text64(_:_:_:_:_:_:)", "", "", "Argument[2]", "database-store", "manual"] - ["", "", false, "sqlite3_bind_value(_:_:_:)", "", "", "Argument[2]", "database-store", "manual"] - - ["", "", false, "sqlite3_exec(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - - ["", "", false, "sqlite3_prepare(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - - ["", "", false, "sqlite3_prepare16(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - - ["", "", false, "sqlite3_prepare16_v2(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - - ["", "", false, "sqlite3_prepare16_v3(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - - ["", "", false, "sqlite3_prepare_v2(_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - - ["", "", false, "sqlite3_prepare_v3(_:_:_:_:_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "", false, "sqlite3_exec(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] - ["", "", false, "sqlite3_prepare(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] - ["", "", false, "sqlite3_prepare_v2(_:_:_:_:_:)", "", "", "Argument[1]", "sql-injection", "manual"] @@ -237,9 +230,6 @@ extensions: - ["", "Connection", true, "keyAndMigrate(_:db:)", "", "", "Argument[0]", "encryption-key", "manual"] - ["", "Connection", true, "rekey(_:db:)", "", "", "Argument[0]", "encryption-key", "manual"] - ["", "Connection", true, "sqlcipher_export(_:key:)", "", "", "Argument[key:]", "encryption-key", "manual"] - - ["", "Connection", true, "execute(_:)", "", "", "Argument[0]", "database-store", "manual"] - - ["", "Connection", true, "prepare(_:_:)", "", "", "Argument[0]", "database-store", "manual"] - - ["", "Connection", true, "prepare(_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "Connection", true, "run(_:_:)", "", "", "Argument[0]", "database-store", "manual"] - ["", "Connection", true, "run(_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "Connection", true, "scalar(_:_:)", "", "", "Argument[0]", "database-store", "manual"] @@ -361,7 +351,6 @@ extensions: - ["", "SQL", true, "init(arguments:sql:)", "", "", "Argument[arguments:]", "database-store", "manual"] - ["", "SQLRequest", true, "init(adapter:arguments:cached:sql:)", "", "", "Argument[arguments:]", "database-store", "manual"] - ["", "SQLStatementCursor", true, "init(arguments:database:prepFlags:sql:)", "", "", "Argument[arguments:]", "database-store", "manual"] - - ["", "Statement", true, "init(_:_:)", "", "", "Argument[1]", "database-store", "manual"] - ["", "Statement", true, "scalar(_:)", "", "", "Argument[0]", "database-store", "manual"] - ["", "Statement", true, "bind(_:)", "", "", "Argument[0]", "database-store", "manual"] - ["", "Statement", true, "execute(arguments:)", "", "", "Argument[arguments:]", "database-store", "manual"]