mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
add database accesses as additional (heuristic) remote flow sources
This commit is contained in:
@@ -84,6 +84,21 @@ abstract class FileNameSource extends DataFlow::Node { }
|
||||
abstract class DatabaseAccess extends DataFlow::Node {
|
||||
/** Gets an argument to this database access that is interpreted as a query. */
|
||||
abstract DataFlow::Node getAQueryArgument();
|
||||
|
||||
/** Gets a node to which a result of the access may flow. */
|
||||
DataFlow::Node getAResult() {
|
||||
none() // Overridden in subclass
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the data returned can be a user-controlled object,
|
||||
* such as a JSON object parsed from user-controlled data.
|
||||
*/
|
||||
predicate returnsUserControlledObject() {
|
||||
// NB: Most data bases support JSON data (some via plugins),
|
||||
// which is why this has a default implementation.
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,17 +46,35 @@ module Knex {
|
||||
RawKnexSqlString() { this = any(RawKnexCall call).getArgument(0).asExpr() }
|
||||
}
|
||||
|
||||
/** A call that triggers a SQL query submission. */
|
||||
private class KnexDatabaseAccess extends DatabaseAccess {
|
||||
KnexDatabaseAccess() {
|
||||
this = knexObject().getMember(["then", "stream", "asCallback"]).getACall()
|
||||
/** A call that triggers a SQL query submission by calling then/stream/asCallback. */
|
||||
private class KnexDatabaseCallback extends DatabaseAccess, DataFlow::CallNode {
|
||||
string member;
|
||||
|
||||
KnexDatabaseCallback() {
|
||||
member = ["then", "stream", "asCallback"] and
|
||||
this = knexObject().getMember(member).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
member = "then" and
|
||||
result = this.getCallback(0).getParameter(0)
|
||||
or
|
||||
exists(AwaitExpr await |
|
||||
this = await.flow() and
|
||||
await.getOperand() = knexObject().getAUse().asExpr()
|
||||
)
|
||||
member = "asCallback" and
|
||||
result = this.getCallback(0).getParameter(1)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
|
||||
private class KnexDatabaseAwait extends DatabaseAccess, DataFlow::ValueNode {
|
||||
KnexDatabaseAwait() {
|
||||
exists(AwaitExpr enclosingAwait | this = enclosingAwait.flow() |
|
||||
enclosingAwait.getOperand() = knexObject().getAUse().asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() { result = this }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.Promises
|
||||
|
||||
module NoSQL {
|
||||
/** An expression that is interpreted as a NoSQL query. */
|
||||
@@ -65,6 +66,10 @@ private module MongoDB {
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(queryArgIdx) }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(this.getParameter(queryArgIdx))
|
||||
}
|
||||
@@ -537,12 +542,29 @@ private module Mongoose {
|
||||
// NB: the complete information is not easily accessible for deeply chained calls
|
||||
f.getQueryArgument().getARhs() = result
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(this.getNumArgument() - 1).getParameter(1)
|
||||
}
|
||||
}
|
||||
|
||||
class ExplicitQueryEvaluation extends DatabaseAccess {
|
||||
class ExplicitQueryEvaluation extends DatabaseAccess, DataFlow::CallNode {
|
||||
string member;
|
||||
|
||||
ExplicitQueryEvaluation() {
|
||||
// explicit execution using a Query method call
|
||||
Query::getAMongooseQuery().getMember(["exec", "then", "catch"]).getACall() = this
|
||||
member = ["exec", "then", "catch"] and
|
||||
Query::getAMongooseQuery().getMember(member).getACall() = this
|
||||
}
|
||||
|
||||
private int resultParamIndex() {
|
||||
member = "then" and result = 0
|
||||
or
|
||||
member = "exec" and result = 1
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(_).getParameter(this.resultParamIndex())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
@@ -588,6 +610,10 @@ private module Minimongo {
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(queryArgIdx) }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(this.getParameter(queryArgIdx))
|
||||
}
|
||||
@@ -609,7 +635,7 @@ private module Minimongo {
|
||||
* Provides classes modeling the MarsDB library.
|
||||
*/
|
||||
private module MarsDB {
|
||||
private class MarsDBAccess extends DatabaseAccess {
|
||||
private class MarsDBAccess extends DatabaseAccess, DataFlow::CallNode {
|
||||
string method;
|
||||
|
||||
MarsDBAccess() {
|
||||
@@ -623,21 +649,29 @@ private module MarsDB {
|
||||
|
||||
string getMethod() { result = method }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
|
||||
/** A call to a MarsDB query method. */
|
||||
private class QueryCall extends DatabaseAccess, API::CallNode {
|
||||
private class QueryCall extends MarsDBAccess, API::CallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m |
|
||||
this.(MarsDBAccess).getMethod() = m and
|
||||
this.getMethod() = m and
|
||||
// implements parts of the Minimongo interface
|
||||
Minimongo::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
@@ -744,9 +778,13 @@ private module Redis {
|
||||
/**
|
||||
* An access to a database through redis
|
||||
*/
|
||||
class RedisDatabaseAccess extends DatabaseAccess {
|
||||
class RedisDatabaseAccess extends DatabaseAccess, DataFlow::CallNode {
|
||||
RedisDatabaseAccess() { this = redis().getMember(_).getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
}
|
||||
@@ -768,9 +806,13 @@ private module IoRedis {
|
||||
/**
|
||||
* An access to a database through ioredis
|
||||
*/
|
||||
class IoRedisDatabaseAccess extends DatabaseAccess {
|
||||
class IoRedisDatabaseAccess extends DatabaseAccess, DataFlow::CallNode {
|
||||
IoRedisDatabaseAccess() { this = ioredis().getMember(_).getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.Promises
|
||||
|
||||
module SQL {
|
||||
/** A string-valued expression that is interpreted as a SQL command. */
|
||||
@@ -81,6 +82,8 @@ private module MySql {
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() { result = this.getCallback(_).getParameter(1) }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -178,6 +181,16 @@ private module Postgres {
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [client(), pool()].getMember("query").getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
this.getNumArgument() = 2 and
|
||||
result = this.getCallback(1).getParameter(1)
|
||||
or
|
||||
this.getNumArgument() = 1 and
|
||||
result = this.getAMethodCall("then").getCallback(0).getParameter(0)
|
||||
or
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -322,6 +335,10 @@ private module Postgres {
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = this.getADirectQueryArgument()
|
||||
or
|
||||
@@ -370,6 +387,11 @@ private module Sqlite {
|
||||
this = database().getMember("prepare").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(1).getParameter(1) or
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -420,6 +442,11 @@ private module MsSql {
|
||||
mssql().getMember("query").getAUse() = DataFlow::valueNode(astNode.getTag())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.(DataFlow::SourceNode).getALocalUse(), result,
|
||||
Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = DataFlow::valueNode(astNode.getTemplate().getAnElement())
|
||||
}
|
||||
@@ -429,6 +456,12 @@ private module MsSql {
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [mssql(), request()].getMember(["query", "batch"]).getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(1).getParameter(1)
|
||||
or
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -491,6 +524,31 @@ private module Sequelize {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets an import of the `sequelize` module or one that re-exports it. */
|
||||
API::Node sequelize() { result = API::moduleImport(["sequelize", "sequelize-typescript"]) }
|
||||
|
||||
/** Gets an expression that creates an instance of the `Sequelize` class. */
|
||||
API::Node instance() {
|
||||
result = [sequelize(), sequelize().getMember("Sequelize")].getInstance()
|
||||
or
|
||||
result = API::Node::ofType(["sequelize", "sequelize-typescript"], ["Sequelize", "default"])
|
||||
}
|
||||
|
||||
/** A call to `Sequelize.query`. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = instance().getMember("query").getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = this.getArgument(0)
|
||||
or
|
||||
result = this.getOptionArgument(0, "query")
|
||||
}
|
||||
}
|
||||
|
||||
class SequelizeSink extends ModelInput::SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
@@ -540,3 +598,145 @@ private module SpannerCsv {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Google Cloud Spanner library.
|
||||
*/
|
||||
private module Spanner {
|
||||
/**
|
||||
* Gets a node that refers to the `Spanner` class
|
||||
*/
|
||||
API::Node spanner() {
|
||||
// older versions
|
||||
result = API::moduleImport("@google-cloud/spanner")
|
||||
or
|
||||
// newer versions
|
||||
result = API::moduleImport("@google-cloud/spanner").getMember("Spanner")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to an instance of the `Database` class.
|
||||
*/
|
||||
API::Node database() {
|
||||
result =
|
||||
spanner().getReturn().getMember("instance").getReturn().getMember("database").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Database")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to an instance of the `v1.SpannerClient` class.
|
||||
*/
|
||||
API::Node v1SpannerClient() {
|
||||
result = spanner().getMember("v1").getMember("SpannerClient").getInstance()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "v1.SpannerClient")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to a transaction object.
|
||||
*/
|
||||
API::Node transaction() {
|
||||
result =
|
||||
database()
|
||||
.getMember(["runTransaction", "runTransactionAsync"])
|
||||
.getParameter([0, 1])
|
||||
.getParameter(1)
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Transaction")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that refers to a snapshot object.
|
||||
*/
|
||||
API::Node snapshot() {
|
||||
result = database().getMember("getSnapshot").getParameter([0, 1]).getParameter(1)
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "Snapshot")
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a `BatchTransaction` object. */
|
||||
API::Node batchTransaction() {
|
||||
result = database().getMember("batchTransaction").getReturn()
|
||||
or
|
||||
result = database().getMember("createBatchTransaction").getReturn().getPromised()
|
||||
or
|
||||
result = API::Node::ofType("@google-cloud/spanner", "BatchTransaction")
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a Spanner method that executes a SQL query.
|
||||
*/
|
||||
abstract class SqlExecution extends DatabaseAccess, DataFlow::InvokeNode { }
|
||||
|
||||
/**
|
||||
* A SQL execution that takes the input directly in the first argument or in the `sql` option.
|
||||
*/
|
||||
class SqlExecutionDirect extends SqlExecution {
|
||||
SqlExecutionDirect() {
|
||||
this = database().getMember(["run", "runPartitionedUpdate", "runStream"]).getACall()
|
||||
or
|
||||
this = transaction().getMember(["run", "runStream", "runUpdate"]).getACall()
|
||||
or
|
||||
this = batchTransaction().getMember("createQueryPartitions").getACall()
|
||||
or
|
||||
this = snapshot().getMember(["run", "runStream"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
or
|
||||
this = [database(), transaction(), snapshot()].getMember("run").getACall() and
|
||||
result = this.getCallback(_).getParameter(1)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = this.getArgument(0)
|
||||
or
|
||||
result = this.getOptionArgument(0, "sql")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that takes an array of SQL strings or { sql: string } objects.
|
||||
*/
|
||||
class SqlExecutionBatch extends SqlExecution, API::CallNode {
|
||||
SqlExecutionBatch() { this = transaction().getMember("batchUpdate").getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
none() // no results, batch update callbacks get only row counts.
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// just use the whole array as the query argument, as arrays becomes tainted if one of the elements
|
||||
// are tainted
|
||||
result = this.getArgument(0)
|
||||
or
|
||||
result = this.getParameter(0).getUnknownMember().getMember("sql").getARhs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL execution that only takes the input in the `sql` option, and do not accept query strings
|
||||
* directly.
|
||||
*/
|
||||
class SqlExecutionWithOption extends SqlExecution, DataFlow::CallNode {
|
||||
SqlExecutionWithOption() {
|
||||
this = v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
this = v1SpannerClient().getMember("executeSql").getACall() and
|
||||
result = this.getCallback(_).getParameter(1)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getOptionArgument(0, "sql") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a SQL string.
|
||||
*/
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() { this = any(SqlExecution se).getAQueryArgument().asExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ private import semmle.javascript.security.dataflow.CommandInjectionCustomization
|
||||
abstract class HeuristicSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* An access to a password, viewed a source of remote flow.
|
||||
* An access to a password, viewed as a source of remote flow.
|
||||
*/
|
||||
private class RemoteFlowPassword extends HeuristicSource, RemoteFlowSource {
|
||||
RemoteFlowPassword() { isReadFrom(this, "(?is).*(password|passwd).*") }
|
||||
@@ -52,3 +52,16 @@ class RemoteServerResponse extends HeuristicSource, RemoteFlowSource {
|
||||
|
||||
override string getSourceType() { result = "a response from a remote server" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The data read from a database.
|
||||
*/
|
||||
class DatabaseAccessResultRemoteFlowSource extends HeuristicSource, RemoteFlowSource {
|
||||
DatabaseAccessResultRemoteFlowSource() { exists(DatabaseAccess dba | this = dba.getAResult()) }
|
||||
|
||||
override string getSourceType() { result = "Database query result" }
|
||||
|
||||
override predicate isUserControlledObject() {
|
||||
this.(DatabaseAccess).returnsUserControlledObject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import javascript
|
||||
private import semmle.javascript.heuristics.AdditionalSinks
|
||||
|
||||
select any(HeuristicSink s)
|
||||
select any(HeuristicSink s | s.getFile().getBaseName() = "sinks.js")
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
| sources.js:2:5:2:12 | password |
|
||||
| sources.js:3:5:3:20 | JSON.stringify() |
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import javascript
|
||||
private import semmle.javascript.heuristics.AdditionalSources
|
||||
import testUtilities.ConsistencyChecking
|
||||
|
||||
select any(HeuristicSource s)
|
||||
class Taint extends TaintTracking::Configuration {
|
||||
Taint() { this = "Taint" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof HeuristicSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,234 @@
|
||||
const pg = require('pg');
|
||||
const { Stream } = require('stream');
|
||||
const pool = new pg.Pool({});
|
||||
|
||||
function sink(data) {}
|
||||
|
||||
(function() {
|
||||
password;
|
||||
JSON.stringify();
|
||||
const password = '1234';
|
||||
sink(password); // NOT OK
|
||||
|
||||
const s = JSON.stringify();
|
||||
sink(s); // NOT OK
|
||||
})();
|
||||
|
||||
(async function() {
|
||||
const knex = require('knex');
|
||||
|
||||
const users = knex().select('*').from('users');
|
||||
users.then(function (users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
users.asCallback(function (err, users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
sink(await users); // NOT OK
|
||||
})();
|
||||
|
||||
(function() {
|
||||
const pg = require('pg');
|
||||
|
||||
const pool = new pg.Pool({});
|
||||
pool.connect(async function (err, client, done) {
|
||||
client.query('SELECT * FROM users', function (err, users) {
|
||||
sink(users);
|
||||
});
|
||||
|
||||
const thenable = client.query('SELECT * FROM users')
|
||||
thenable.then(function(users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
const pgpromise = client.query('SELECT * FROM users');
|
||||
sink(await pgpromise); // NOT OK
|
||||
});
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const pgpromise = require('pg-promise')();
|
||||
const db = pgpromise('postgres://username:password@localhost:1234/database');
|
||||
const pgppromise = db.any('SELECT * FROM users');
|
||||
|
||||
pgppromise.then(function (users) {
|
||||
sink(users);
|
||||
});
|
||||
|
||||
sink(await pgppromise);
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const mysql = require('mysql2');
|
||||
const conn = mysql.createConnection({});
|
||||
|
||||
conn.query(
|
||||
'SELECT * FROM `users`',
|
||||
function(err, users, fields) {
|
||||
sink(users); // NOT OK
|
||||
}
|
||||
);
|
||||
|
||||
conn.execute(
|
||||
'SELECT * FROM `users` WHERE name = ?',
|
||||
['Alice'],
|
||||
function(err, users) {
|
||||
sink(users);
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const sqlite = require('sqlite3');
|
||||
const db = new sqlite.Database(':memory:');
|
||||
|
||||
db.all('SELECT * FROM users', function (err, users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
const sqlitepromise = db.all('SELECT * FROM users');
|
||||
|
||||
sink(await sqlitepromise); // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const { Sequelize } = require('sequelize');
|
||||
const sequelize = new Sequelize('sqlite::memory:');
|
||||
|
||||
class User extends sequelize.Model {}
|
||||
User.init({ name: sequelize.DataTypes.String }, { sequelize, modelName: 'user' });
|
||||
|
||||
sequelize.query('SELECT * FROM users').then(function (users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const sql = require('mssql');
|
||||
await sql.connect('...');
|
||||
|
||||
sql.query('SELECT * FROM users', function (err, users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
const mssqlthenable = sql.query('SELECT * FROM users');
|
||||
|
||||
mssqlthenable.then(function (users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
const mssqlpromise = sql.query('SELECT * FROM users');
|
||||
sink(await mssqlpromise); // NOT OK
|
||||
|
||||
const uname = 'Alice';
|
||||
const mssqltaggedquery = sql.query`SELECT * FROM users where name=${uname}`
|
||||
sink(await mssqltaggedquery); // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const {Spanner} = require('@google-cloud/spanner');
|
||||
const db = new Spanner({projectId: 'test'})
|
||||
.instance('instanceid')
|
||||
.database('databaseid');
|
||||
|
||||
const spannerpromise = db.run({
|
||||
sql: 'SELECT * FROM users'
|
||||
});
|
||||
|
||||
sink(await spannerpromise); // NOT OK
|
||||
|
||||
db.run({
|
||||
sql: 'SELECT * FROM users'
|
||||
}, function (err, rows, stats, meta) {
|
||||
sink(rows); // NOT OK
|
||||
});
|
||||
|
||||
const client = new Spanner.v1.SpannerClient({});
|
||||
client.executeSql('SELECT * FROM users', {}, function (err, users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
|
||||
db.runTransaction(function(err, txn) {
|
||||
txn.run('SELECT * FROM users', function (err, users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
});
|
||||
|
||||
db.getSnapshot(function (err, txn) {
|
||||
txn.run('SELECT * FROM users', function (err, users) {
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
txn.end();
|
||||
});
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
MongoClient.connect('mongodb://localhost:1234', async function (err, db) {
|
||||
const collection = db.collection('users');
|
||||
const users = await collection.find({});
|
||||
sink(users); // NOT OK
|
||||
});
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const mongoose = require('mongoose');
|
||||
await mongoose.connect('mongodb://localhost:1234');
|
||||
|
||||
const User = mongoose.model('User', {
|
||||
name: {
|
||||
type: String,
|
||||
unique: true
|
||||
}
|
||||
});
|
||||
|
||||
User.find({ name: 'Alice' }, function (err, alice) {
|
||||
sink(alice); // NOT OK
|
||||
});
|
||||
|
||||
User.find({ name: 'Bob' }).exec(function (err, bob) {
|
||||
sink(bob); // NOT OK
|
||||
});
|
||||
|
||||
const promise = User.find({ name: 'Claire' });
|
||||
promise.then(c => sink(c)); // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const minimongo = require('minimongo');
|
||||
const LocalDb = minimongo.MemoryDb;
|
||||
const db = new LocalDb();
|
||||
const doc = db.users;
|
||||
|
||||
const users = await doc.find({});
|
||||
sink(users); // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const { Collection } = require('marsdb');
|
||||
|
||||
const doc = new Collection('users');
|
||||
|
||||
const users = await doc.find({});
|
||||
|
||||
sink(users); // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const redis = require("redis");
|
||||
const client = redis.createClient();
|
||||
|
||||
const alice = await client.get('alice');
|
||||
|
||||
sink(alice); // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const Redis = require('ioredis');
|
||||
const redis = new Redis();
|
||||
|
||||
const bob = await redis.get('bob');
|
||||
|
||||
sink(bob); // NOT OK
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user