Merge remote-tracking branch 'upstream/master'

This commit is contained in:
ubuntu
2020-06-17 11:13:13 +02:00
378 changed files with 10887 additions and 3608 deletions

View File

@@ -77,8 +77,8 @@ import com.semmle.util.trap.TrapWriter;
* <li><code>LGTM_INDEX_EXCLUDE</code>: a newline-separated list of paths to exclude
* <li><code>LGTM_REPOSITORY_FOLDERS_CSV</code>: the path of a CSV file containing file
* classifications
* <li><code>LGTM_INDEX_FILTERS</code>: a newline-separated list of {@link ProjectLayout}-style
* patterns that can be used to refine the list of files to include and exclude
* <li><code>LGTM_INDEX_FILTERS</code>: a newline-separated list of strings of form "include:PATTERN"
* or "exclude:PATTERN" that can be used to refine the list of files to include and exclude.
* <li><code>LGTM_INDEX_TYPESCRIPT</code>: whether to extract TypeScript
* <li><code>LGTM_INDEX_FILETYPES</code>: a newline-separated list of ".extension:filetype" pairs
* specifying which {@link FileType} to use for the given extension; the additional file type

View File

@@ -0,0 +1,4 @@
- description: Security-and-quality queries for JavaScript
- qlpack: codeql-javascript
- apply: security-and-quality-selectors.yml
from: codeql-suite-helpers

View File

@@ -0,0 +1,4 @@
- description: Security-extended queries for JavaScript
- qlpack: codeql-javascript
- apply: security-extended-selectors.yml
from: codeql-suite-helpers

View File

@@ -1,5 +1,6 @@
import javascript
private import semmle.javascript.dataflow.InferredTypes
private import semmle.javascript.dataflow.internal.PreCallGraphStep
/**
* Classes and predicates for modelling TaintTracking steps for arrays.
@@ -222,29 +223,32 @@ private module ArrayDataFlow {
*
* And the second parameter in the callback is the array ifself, so there is a `loadStoreStep` from the array to that second parameter.
*/
private class ArrayIteration extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ArrayIteration() {
this.getMethodName() = "map" or
this.getMethodName() = "forEach"
}
private class ArrayIteration extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
prop = arrayElement() and
obj = this.getReceiver() and
element = getCallback(0).getParameter(0)
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["map", "forEach"] and
prop = arrayElement() and
obj = call.getReceiver() and
element = call.getCallback(0).getParameter(0)
)
}
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
this.getMethodName() = "map" and
prop = arrayElement() and
element = this.getCallback(0).getAReturn() and
obj = this
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "map" and
prop = arrayElement() and
element = call.getCallback(0).getAReturn() and
obj = call
)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = arrayElement() and
pred = this.getReceiver() and
succ = getCallback(0).getParameter(2)
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["map", "forEach"] and
prop = arrayElement() and
pred = call.getReceiver() and
succ = call.getCallback(0).getParameter(2)
)
}
}
@@ -311,16 +315,13 @@ private module ArrayDataFlow {
/**
* A step for modelling `for of` iteration on arrays.
*/
private class ForOfStep extends DataFlow::AdditionalFlowStep, DataFlow::ValueNode {
ForOfStmt forOf;
DataFlow::Node element;
ForOfStep() { this.asExpr() = forOf.getIterationDomain() }
private class ForOfStep extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) {
obj = this and
e = DataFlow::lvalueNode(forOf.getLValue()) and
prop = arrayElement()
exists(ForOfStmt forOf |
obj = forOf.getIterationDomain().flow() and
e = DataFlow::lvalueNode(forOf.getLValue()) and
prop = arrayElement()
)
}
}
}

View File

@@ -31,7 +31,7 @@ private class PseudoProperty extends TypeTrackingPseudoProperty {
* `load`/`store`/`loadStore` can be used in the `CollectionsTypeTracking` module.
* (Thereby avoiding naming conflicts with a "cousin" `AdditionalFlowStep` implementation.)
*/
abstract private class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
abstract class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate step(

View File

@@ -33,7 +33,7 @@ deprecated module GlobalAccessPath {
/**
* Provides predicates for associating access paths with data flow nodes.
*
* For example, `AccessPath.getAReferenceTo(x)` can be used to obtain the global access path
* For example, `AccessPath::getAReferenceTo(x)` can be used to obtain the global access path
* that `x` refers to, as in the following sample:
* ```
* function f() {
@@ -240,7 +240,7 @@ module AccessPath {
* ```
* function f(x) {
* x.foo.bar = class {};
* x.foo = { bar: class() };
* x.foo = { bar: class {} };
* let alias = x;
* alias.foo.bar = class {};
* }
@@ -338,7 +338,7 @@ module AccessPath {
* ```
* function f(x) {
* x.foo.bar = class {};
* x.foo = { bar: class() };
* x.foo = { bar: class {} };
* let alias = x;
* alias.foo.bar = class {};
* }
@@ -355,7 +355,7 @@ module AccessPath {
* Only gets the immediate right-hand side of an assignment or property or a global declaration,
* not nodes that transitively flow there.
*
* For example, the class nodes below are all assignmetns to `foo.bar`:
* For example, the class nodes below are all assignments to `foo.bar`:
* ```
* foo.bar = class {};
* foo = { bar: class {} };

View File

@@ -23,6 +23,7 @@ private import internal.CallGraphs
private import internal.FlowSteps as FlowSteps
private import internal.DataFlowNode
private import internal.AnalyzedParameters
private import internal.PreCallGraphStep
module DataFlow {
/**

View File

@@ -335,5 +335,20 @@ abstract class AdditionalTypeTrackingStep extends DataFlow::Node {
/**
* Holds if type-tracking should step from `pred` to `succ`.
*/
abstract predicate step(DataFlow::Node pred, DataFlow::Node succ);
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if type-tracking should step from `pred` into the `prop` property of `succ`.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `prop` property of `pred` to `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `prop` property of `pred` to the same property in `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
}

View File

@@ -61,6 +61,19 @@ module CallGraph {
function = cls.getConstructor() and
cls.getAClassReference(t.continue()).flowsTo(result)
)
or
imprecision = 0 and
exists(DataFlow::FunctionNode outer |
result = getAFunctionReference(outer, 0, t.continue()).getAnInvocation() and
locallyReturnedFunction(outer, function)
)
}
cached
private predicate locallyReturnedFunction(
DataFlow::FunctionNode outer, DataFlow::FunctionNode inner
) {
inner.flowsTo(outer.getAReturn())
}
/**

View File

@@ -0,0 +1,134 @@
/**
* Provides an extension point for contributing flow edges prior
* to call graph construction and type tracking.
*/
private import javascript
private newtype TUnit = MkUnit()
private class Unit extends TUnit {
string toString() { result = "unit" }
}
/**
* Internal extension point for adding flow edges prior to call graph construction
* and type tracking.
*
* Steps added here will be added to both `AdditionalFlowStep` and `AdditionalTypeTrackingStep`.
*
* Contributing steps that rely on type tracking will lead to negative recursion.
*/
class PreCallGraphStep extends Unit {
/**
* Holds if there is a step from `pred` to `succ`.
*/
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if there is a step from `pred` into the `prop` property of `succ`.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if there is a step from the `prop` property of `pred` to `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if there is a step from the `prop` property of `pred` to the same property in `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
}
module PreCallGraphStep {
/**
* Holds if there is a step from `pred` to `succ`.
*/
cached
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(PreCallGraphStep s).step(pred, succ)
}
/**
* Holds if there is a step from `pred` into the `prop` property of `succ`.
*/
cached
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(PreCallGraphStep s).storeStep(pred, succ, prop)
}
/**
* Holds if there is a step from the `prop` property of `pred` to `succ`.
*/
cached
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(PreCallGraphStep s).loadStep(pred, succ, prop)
}
/**
* Holds if there is a step from the `prop` property of `pred` to the same property in `succ`.
*/
cached
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(PreCallGraphStep s).loadStoreStep(pred, succ, prop)
}
}
private class NodeWithPreCallGraphStep extends DataFlow::Node {
NodeWithPreCallGraphStep() {
PreCallGraphStep::step(this, _)
or
PreCallGraphStep::storeStep(this, _, _)
or
PreCallGraphStep::loadStep(this, _, _)
or
PreCallGraphStep::loadStoreStep(this, _, _)
}
}
private class AdditionalFlowStepFromPreCallGraph extends NodeWithPreCallGraphStep,
DataFlow::AdditionalFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = this and
PreCallGraphStep::step(this, succ)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
pred = this and
PreCallGraphStep::storeStep(this, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
pred = this and
PreCallGraphStep::loadStep(this, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
pred = this and
PreCallGraphStep::loadStoreStep(this, succ, prop)
}
}
private class AdditionalTypeTrackingStepFromPreCallGraph extends NodeWithPreCallGraphStep,
DataFlow::AdditionalTypeTrackingStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = this and
PreCallGraphStep::step(this, succ)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
pred = this and
PreCallGraphStep::storeStep(this, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
pred = this and
PreCallGraphStep::loadStep(this, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
pred = this and
PreCallGraphStep::loadStoreStep(this, succ, prop)
}
}

View File

@@ -110,6 +110,15 @@ module StepSummary {
or
basicLoadStep(pred, succ, prop) and
summary = LoadStep(prop)
or
any(AdditionalTypeTrackingStep st).storeStep(pred, succ, prop) and
summary = StoreStep(prop)
or
any(AdditionalTypeTrackingStep st).loadStep(pred, succ, prop) and
summary = LoadStep(prop)
or
any(AdditionalTypeTrackingStep st).loadStoreStep(pred, succ, prop) and
summary = CopyStep(prop)
)
or
any(AdditionalTypeTrackingStep st).step(pred, succ) and

View File

@@ -119,7 +119,14 @@ module Express {
t.start() and
result = getARouteHandlerExpr().flow().getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 | result = getARouteHandler(t2).backtrack(t2, t))
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode succ | succ = getARouteHandler(t2) |
result = succ.backtrack(t2, t)
or
exists(HTTP::RouteHandlerCandidateContainer container |
result = container.getRouteHandler(succ)
) and
t = t2
)
}
override Expr getServer() { result.(Application).getARouteHandler() = getARouteHandler() }

View File

@@ -3,6 +3,8 @@
*/
import javascript
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.internal.StepSummary
module HTTP {
/**
@@ -496,4 +498,111 @@ module HTTP {
class CookieCryptographicKey extends CryptographicKey {
CookieCryptographicKey() { this = any(CookieMiddlewareInstance instance).getASecretKey() }
}
/**
* An object that contains one or more potential route handlers.
*/
class RouteHandlerCandidateContainer extends DataFlow::Node {
RouteHandlerCandidateContainer::Range self;
RouteHandlerCandidateContainer() { this = self }
/**
* Gets the route handler in this container that is accessed at `access`.
*/
DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
result = self.getRouteHandler(access)
}
}
/**
* Provides classes for working with objects that may contain one or more route handlers.
*/
module RouteHandlerCandidateContainer {
private DataFlow::SourceNode ref(DataFlow::TypeTracker t, RouteHandlerCandidateContainer c) {
t.start() and result = c
or
exists(DataFlow::TypeTracker t2 | result = ref(t2, c).track(t2, t))
}
private DataFlow::SourceNode ref(RouteHandlerCandidateContainer c) {
result = ref(DataFlow::TypeTracker::end(), c)
}
/**
* A container for one or more potential route handlers.
*
* Extend this class and implement its abstract member predicates to model additional
* containers.
*/
abstract class Range extends DataFlow::SourceNode {
/**
* Gets the route handler in this container that is accessed at `access`.
*/
abstract DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access);
}
/**
* An object that contains one or more potential route handlers.
*/
private class ContainerObject extends Range {
ContainerObject() {
(
this instanceof DataFlow::ObjectLiteralNode
or
exists(DataFlow::CallNode create | this = create |
create = DataFlow::globalVarRef("Object").getAMemberCall("create") and
create.getArgument(0).asExpr() instanceof NullLiteral
)
) and
exists(RouteHandlerCandidate candidate | candidate.flowsTo(getAPropertyWrite().getRhs()))
}
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
result instanceof RouteHandlerCandidate and
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
access = read and
ref(this).getAPropertyRead() = read and
result.flowsTo(write.getRhs()) and
write = this.getAPropertyWrite()
|
write.getPropertyName() = read.getPropertyName()
or
exists(EnumeratedPropName prop | access = prop.getASourceProp())
or
read = DataFlow::lvalueNode(any(ForOfStmt stmt).getLValue())
)
}
}
/**
* A collection that contains one or more route potential handlers.
*/
private class ContainerCollection extends HTTP::RouteHandlerCandidateContainer::Range {
ContainerCollection() {
this = DataFlow::globalVarRef("Map").getAnInstantiation() and // restrict to Map for now
exists(
CollectionFlowStep store, DataFlow::Node storeTo, DataFlow::Node input,
RouteHandlerCandidate candidate
|
this.flowsTo(storeTo) and
store.store(input, storeTo, _) and
candidate.flowsTo(input)
)
}
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
exists(
DataFlow::Node input, TypeTrackingPseudoProperty key, CollectionFlowStep store,
CollectionFlowStep load, DataFlow::Node storeTo, DataFlow::Node loadFrom
|
this.flowsTo(storeTo) and
store.store(input, storeTo, key) and
result.(RouteHandlerCandidate).flowsTo(input) and
ref(this).flowsTo(loadFrom) and
load.load(loadFrom, access, key)
)
}
}
}
}

View File

@@ -66,7 +66,8 @@ query predicate missingCallee(AnnotatedCall call, AnnotatedFunction target, int
query predicate badAnnotation(string name) {
name = any(AnnotatedCall cl).getCallTargetName() and
not name = any(AnnotatedFunction cl).getCalleeName()
not name = any(AnnotatedFunction cl).getCalleeName() and
name != "NONE"
or
not name = any(AnnotatedCall cl).getCallTargetName() and
name = any(AnnotatedFunction cl).getCalleeName()

View File

@@ -0,0 +1,32 @@
import 'dummy';
/** name:curry1 */
function curry1() {
/** name:curry2 */
function curry2(x) {
/** name:curry3 */
function curry3(y) {
}
return curry3;
}
return curry2;
};
/** calls:curry1 */
let r1 = curry1();
/** calls:curry2 */
let r2 = r1();
/** calls:curry3 */
r2();
function callback(f) {
// Call graph should not include callback invocations.
/** calls:NONE */
f();
}
let w1 = callback(curry1);
callback(() => {});

View File

@@ -0,0 +1,8 @@
import javascript
query predicate getRouteHandlerContainerStep(
HTTP::RouteHandlerCandidateContainer container, DataFlow::SourceNode handler,
DataFlow::SourceNode access
) {
handler = container.getRouteHandler(access)
}

View File

@@ -0,0 +1,163 @@
var express = require("express");
var app = express();
// registration of route handlers in bulk
let routes0 = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};
for (const p in routes0) {
app.get(p, routes0[p]);
}
// registration of route handlers in bulk
let routes1 = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};
for (const handler of routes1) {
app.use(handler);
}
// registration of route handlers in bulk, with indirection
let routes2 = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};
for (const p of Object.keys(routes2)) {
app.get(p, routes2[p]);
}
// registration of route handlers in bulk, with indirection
let routes3 = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};
for (const h of Object.values(routes3)) {
app.use(h);
}
// custom router indirection for all requests
let myRouter1 = {
handlers: {},
add: function(n, h) {
this.handlers[n] = h;
},
handle: function(req, res, target) {
this.handlers[target](req, res);
}
};
myRouter1.add("whatever", (req, res) => console.log(req));
app.use((req, res) => myRouter1.handle(req, res, "whatever"));
// simpler custom router indirection for all requests
let mySimpleRouter = {
handler: undefined,
add: function(h) {
this.handler = h;
},
handle: function(req, res) {
this.handler(req, res);
}
};
mySimpleRouter.add((req, res) => console.log(req));
app.use((req, res) => mySimpleRouter.handle(req, res));
// simplest custom router indirection for all requests
let mySimplestRouter = {
handler: (req, res) => console.log(req),
handle: function(req, res) {
this.handler(req, res);
}
};
app.use((req, res) => mySimplestRouter.handle(req, res));
// a combination of bulk registration and indirection through a custom router
let myRouter3 = {
handlers: {},
add: function(n, h) {
this.handlers[n] = h;
},
handle: function(req, res, target) {
this.handlers[target](req, res);
}
};
let routes3 = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};
for (const p of Object.keys(routes3)) {
myRouter3.add(p, routes3[p]);
}
app.use((req, res) => myRouter3.handle(req, res, "whatever"));
// a combination of bulk registration and indirection through a custom router. Using a map instead of an object.
let myRouter4 = {
handlers: new Map(),
add: function(n, h) {
this.handlers.set(n, h);
},
handle: function(req, res, target) {
this.handlers.get(target)(req, res);
}
};
let routes4 = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};
for (const p of Object.keys(routes4)) {
myRouter4.add(p, routes4[p]);
}
app.use((req, res) => myRouter4.handle(req, res, "whatever"));
// registration of imported route handlers in bulk
let importedRoutes = require("./route-collection").routes;
for (const p in importedRoutes) {
app.get(p, importedRoutes[p]);
}
app.get("a", importedRoutes.a);
app.get("b", importedRoutes.b);
// registration of imported route handlers in a map
let routesMap = new Map();
routesMap.set("a", (req, res) => console.log(req));
routesMap.set("b", (req, res) => console.log(req));
routesMap.forEach((v, k) => app.get(k, v));
app.get("a", routesMap.get("a"));
app.get("b", routesMap.get("b"));
let method = "GET";
app[method.toLowerCase()](path, (req, res) => undefined);
let names = ["handler-in-dynamic-require"];
names.forEach(name => {
let dynamicRequire = require("./controllers/" + name);
app.get(dynamicRequire.path, dynamicRequire.handler);
});
let bulkRequire = require("./controllers");
app.get(bulkRequire.bulky.path, bulkRequire.bulky.handler);
let options = { app: app };
let args = [];
args.push((req, res) => undefined);
app.use.apply(options.app, args);
let handlers = { handlerA: (req, res) => undefined};
app.use(handlers.handlerA.bind(data));
for ([k, v] of routesMap) {
app.get(k, v) // not supported - requires one too many heap steps
}
app.get("b", routesMap.get("NOT_A_KEY!")); // unknown route handler
let routesMap2 = new Map();
routesMap2.set("c", (req, res) => console.log(req));
routesMap2.set(unknown(), (req, res) => console.log(req));
routesMap2.set("e", (req, res) => console.log(req));
app.get("c", routesMap2.get("c"));
app.get("d", routesMap2.get(unknown()));
app.get("e", unknown());
app.get("d", routesMap2.get("f"));

View File

@@ -0,0 +1 @@
module.exports = { path: "bulky", handler: (req, res) => undefined };

View File

@@ -0,0 +1 @@
module.exports = { path: "/A", handler: (req, res) => undefined };

View File

@@ -0,0 +1,4 @@
let bulky = require("./handler-in-bulk-require");
module.exports = {
bulky: bulky
};

View File

@@ -0,0 +1,4 @@
exports.routes = {
a: (req, res) => console.log(req),
b: (req, res) => console.log(req)
};

View File

@@ -46,3 +46,4 @@ import RequestExpr
import RouteHandlerExpr_getAsSubRouter
import Credentials
import RouteHandler_getARequestExpr
import RouteHandlerContainer

View File

@@ -15,6 +15,7 @@
| src/handler-in-property.js:12:18:12:37 | function(req, res){} |
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} |
| src/middleware-attacher.js:3:13:3:32 | function(req, res){} |
| src/nodejs.js:3:19:3:38 | function(req, res){} |
| src/nodejs.js:8:14:8:33 | function(req, res){} |

View File

@@ -2,7 +2,6 @@
| src/bound-handler.js:9:12:9:31 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/hapi.js:1:1:1:30 | functio ... t, h){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:7:19:7:38 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:8:12:10:5 | (req, res) {\\n\\n } | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
| src/route-objects.js:20:16:22:9 | (req, r ... } | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |

View File

@@ -41,4 +41,5 @@
| spanner.js:19:23:19:32 | "SQL code" |
| spannerImport.js:4:8:4:17 | "SQL code" |
| sqlite.js:7:8:7:45 | "UPDATE ... id = ?" |
| sqliteArray.js:6:12:6:49 | "UPDATE ... id = ?" |
| sqliteImport.js:2:8:2:44 | "UPDATE ... id = ?" |

View File

@@ -0,0 +1,7 @@
var sqlite = require('sqlite3');
let databaseNames = [":memory:", ":foo:"];
let databases = databaseNames.map(name => new sqlite.Database(name));
for (let db of databases) {
db.run("UPDATE tbl SET name = ? WHERE id = ?", "bar", 2);
}