mirror of
https://github.com/github/codeql.git
synced 2026-05-02 20:25:13 +02:00
Merge branch 'master' of git.semmle.com:Semmle/ql into UrlSearch
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Provides classes for working with [SockJS](http://sockjs.org).
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A model of the `SockJS` websocket data handler (https://sockjs.org).
|
||||
*/
|
||||
module SockJS {
|
||||
/**
|
||||
* Access to user-controlled data object received from websocket
|
||||
* For example:
|
||||
* ```
|
||||
* server.on('connection', function(conn) {
|
||||
* conn.on('data', function(message) {
|
||||
* ...
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
class SourceFromSocketJS extends RemoteFlowSource {
|
||||
SourceFromSocketJS() {
|
||||
exists(
|
||||
DataFlow::CallNode createServer, DataFlow::CallNode connNode,
|
||||
DataFlow::CallNode dataHandlerNode
|
||||
|
|
||||
createServer = appCreation() and
|
||||
connNode = createServer.getAMethodCall("on") and
|
||||
connNode.getArgument(0).getStringValue() = "connection" and
|
||||
dataHandlerNode = connNode.getCallback(1).getParameter(0).getAMethodCall("on") and
|
||||
dataHandlerNode.getArgument(0).getStringValue() = "data" and
|
||||
this = dataHandlerNode.getCallback(1).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "input from SockJS WebSocket" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new SockJS server.
|
||||
*/
|
||||
private DataFlow::CallNode appCreation() {
|
||||
result = DataFlow::moduleImport("sockjs").getAMemberCall("createServer")
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
import Customizations
|
||||
import semmle.javascript.Aliases
|
||||
import semmle.javascript.AMD
|
||||
import semmle.javascript.Arrays
|
||||
import semmle.javascript.AST
|
||||
import semmle.javascript.BasicBlocks
|
||||
import semmle.javascript.Base64
|
||||
|
||||
263
javascript/ql/src/semmle/javascript/Arrays.qll
Normal file
263
javascript/ql/src/semmle/javascript/Arrays.qll
Normal file
@@ -0,0 +1,263 @@
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
/**
|
||||
* Classes and predicates for modelling TaintTracking steps for arrays.
|
||||
*/
|
||||
module ArrayTaintTracking {
|
||||
/**
|
||||
* A taint propagating data flow edge caused by the builtin array functions.
|
||||
*/
|
||||
private class ArrayFunctionTaintStep extends TaintTracking::AdditionalTaintStep,
|
||||
DataFlow::CallNode {
|
||||
ArrayFunctionTaintStep() { arrayFunctionTaintStep(_, _, this) }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
arrayFunctionTaintStep(pred, succ, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
|
||||
*/
|
||||
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists(string name, Function f, int i |
|
||||
(name = "map" or name = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = name and
|
||||
pred = call.getReceiver() and
|
||||
succ = DataFlow::parameterNode(f.getParameter(i))
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
exists(DataFlow::FunctionNode f |
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
|
||||
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
|
||||
pred = f.getAReturn() and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getASpreadArgument() and
|
||||
// Make sure we handle reflective calls
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = name
|
||||
)
|
||||
or
|
||||
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name | name = "splice" |
|
||||
pred = call.getArgument(2) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
exists(string name |
|
||||
name = "pop" or
|
||||
name = "shift" or
|
||||
name = "slice" or
|
||||
name = "splice"
|
||||
|
|
||||
call.(DataFlow::MethodCallNode).calls(pred, name) and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
|
||||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
or
|
||||
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
|
||||
call.(DataFlow::MethodCallNode).calls(pred, "concat") and
|
||||
succ = call
|
||||
or
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
|
||||
succ = call and
|
||||
pred = call.getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modelling data-flow for arrays.
|
||||
*/
|
||||
private module ArrayDataFlow {
|
||||
/**
|
||||
* Gets a pseudo-field representing an element inside an array.
|
||||
*/
|
||||
private string arrayElement() { result = "$arrayElement$" }
|
||||
|
||||
/**
|
||||
* A step for storing an element on an array using `arr.push(e)` or `arr.unshift(e)`.
|
||||
*/
|
||||
private class ArrayAppendStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ArrayAppendStep() {
|
||||
this.getMethodName() = "push" or
|
||||
this.getMethodName() = "unshift"
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
(element = this.getAnArgument() or element = this.getASpreadArgument()) and
|
||||
obj = this.getReceiver().getALocalSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for reading/writing an element from an array inside a for-loop.
|
||||
* E.g. a read from `foo[i]` to `bar` in `for(var i = 0; i < arr.length; i++) {bar = foo[i]}`.
|
||||
*/
|
||||
private class ArrayIndexingStep extends DataFlow::AdditionalFlowStep, DataFlow::Node {
|
||||
DataFlow::PropRef read;
|
||||
|
||||
ArrayIndexingStep() {
|
||||
read = this and
|
||||
forex(InferredType type | type = read.getPropertyNameExpr().flow().analyze().getAType() |
|
||||
type = TTNumber()
|
||||
) and
|
||||
exists(VarAccess i, ExprOrVarDecl init |
|
||||
i = read.getPropertyNameExpr() and init = any(ForStmt f).getInit()
|
||||
|
|
||||
i.getVariable().getADefinition() = init or
|
||||
i.getVariable().getADefinition().(VariableDeclarator).getDeclStmt() = init
|
||||
)
|
||||
}
|
||||
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
prop = arrayElement() and
|
||||
obj = this.(DataFlow::PropRead).getBase() and
|
||||
element = this
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
element = this.(DataFlow::PropWrite).getRhs() and
|
||||
this = obj.(DataFlow::SourceNode).getAPropertyWrite()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for retrieving an element from an array using `.pop()` or `.shift()`.
|
||||
* E.g. `array.pop()`.
|
||||
*/
|
||||
private class ArrayPopStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ArrayPopStep() {
|
||||
getMethodName() = "pop" or
|
||||
getMethodName() = "shift"
|
||||
}
|
||||
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
prop = arrayElement() and
|
||||
obj = this.getReceiver() and
|
||||
element = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for iterating an array using `map` or `forEach`.
|
||||
*
|
||||
* Array elements can be loaded from the array `arr` to `e` in e.g: `arr.forEach(e => ...)`.
|
||||
*
|
||||
* And array elements can be stored into a resulting array using `map(...)`.
|
||||
* E.g. in `arr.map(e => foo)`, the resulting array (`arr.map(e => foo)`) will contain the element `foo`.
|
||||
*
|
||||
* 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"
|
||||
}
|
||||
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
prop = arrayElement() and
|
||||
obj = this.getReceiver() and
|
||||
element = getCallback(0).getParameter(0)
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
this.getMethodName() = "map" and
|
||||
prop = arrayElement() and
|
||||
element = this.getCallback(0).getAReturn() and
|
||||
obj = this
|
||||
}
|
||||
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = arrayElement() and
|
||||
pred = this.getReceiver() and
|
||||
succ = getCallback(0).getParameter(2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for creating an array and storing the elements in the array.
|
||||
*/
|
||||
private class ArrayCreationStep extends DataFlow::AdditionalFlowStep, DataFlow::Node {
|
||||
ArrayCreationStep() { this instanceof DataFlow::ArrayCreationNode }
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
element = this.(DataFlow::ArrayCreationNode).getAnElement() and
|
||||
obj = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step modelling that `splice` can insert elements into an array.
|
||||
* For example in `array.splice(i, del, e)`: if `e` is tainted, then so is `array
|
||||
*/
|
||||
private class ArraySpliceStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ArraySpliceStep() { this.getMethodName() = "splice" }
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
element = getArgument(2) and
|
||||
obj = this.getReceiver().getALocalSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for modelling `concat`.
|
||||
* For example in `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
|
||||
*/
|
||||
private class ArrayConcatStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ArrayConcatStep() { this.getMethodName() = "concat" }
|
||||
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = arrayElement() and
|
||||
(pred = this.getReceiver() or pred = this.getAnArgument()) and
|
||||
succ = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for modelling that elements from an array `arr` also appear in the result from calling `slice`/`splice`/`filter`.
|
||||
*/
|
||||
private class ArraySliceStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ArraySliceStep() {
|
||||
this.getMethodName() = "slice" or
|
||||
this.getMethodName() = "splice" or
|
||||
this.getMethodName() = "filter"
|
||||
}
|
||||
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
prop = arrayElement() and
|
||||
pred = this.getReceiver() and
|
||||
succ = this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,14 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
ArgumentsVariable getArgumentsVariable() { result.getFunction() = this }
|
||||
|
||||
/** Holds if the body of this function refers to the function's `arguments` variable. */
|
||||
predicate usesArgumentsObject() { exists(getArgumentsVariable().getAnAccess()) }
|
||||
predicate usesArgumentsObject() {
|
||||
exists(getArgumentsVariable().getAnAccess())
|
||||
or
|
||||
exists(PropAccess read |
|
||||
read.getBase() = getVariable().getAnAccess() and
|
||||
read.getPropertyName() = "arguments"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function declares a parameter or local variable named `arguments`.
|
||||
|
||||
@@ -167,6 +167,7 @@ module PromiseTypeTracking {
|
||||
* Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`.
|
||||
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
|
||||
*/
|
||||
pragma[inline]
|
||||
DataFlow::SourceNode promiseStep(DataFlow::SourceNode pred, StepSummary summary) {
|
||||
exists(PromiseFlowStep step, string field | field = Promises::valueProp() |
|
||||
summary = LoadStep(field) and
|
||||
|
||||
@@ -633,32 +633,28 @@ pragma[inline]
|
||||
private predicate basicFlowStepNoBarrier(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary, DataFlow::Configuration cfg
|
||||
) {
|
||||
isLive() and
|
||||
isRelevantForward(pred, cfg) and
|
||||
(
|
||||
// Local flow
|
||||
exists(FlowLabel predlbl, FlowLabel succlbl |
|
||||
localFlowStep(pred, succ, cfg, predlbl, succlbl) and
|
||||
not cfg.isBarrierEdge(pred, succ) and
|
||||
summary = MkPathSummary(false, false, predlbl, succlbl)
|
||||
)
|
||||
or
|
||||
// Flow through properties of objects
|
||||
propertyFlowStep(pred, succ) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
// Flow through global variables
|
||||
globalFlowStep(pred, succ) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
// Flow into function
|
||||
callStep(pred, succ) and
|
||||
summary = PathSummary::call()
|
||||
or
|
||||
// Flow out of function
|
||||
returnStep(pred, succ) and
|
||||
summary = PathSummary::return()
|
||||
// Local flow
|
||||
exists(FlowLabel predlbl, FlowLabel succlbl |
|
||||
localFlowStep(pred, succ, cfg, predlbl, succlbl) and
|
||||
not cfg.isBarrierEdge(pred, succ) and
|
||||
summary = MkPathSummary(false, false, predlbl, succlbl)
|
||||
)
|
||||
or
|
||||
// Flow through properties of objects
|
||||
propertyFlowStep(pred, succ) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
// Flow through global variables
|
||||
globalFlowStep(pred, succ) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
// Flow into function
|
||||
callStep(pred, succ) and
|
||||
summary = PathSummary::call()
|
||||
or
|
||||
// Flow out of function
|
||||
returnStep(pred, succ) and
|
||||
summary = PathSummary::return()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,6 +667,7 @@ private predicate basicFlowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary, DataFlow::Configuration cfg
|
||||
) {
|
||||
basicFlowStepNoBarrier(pred, succ, summary, cfg) and
|
||||
isRelevant(pred, cfg) and
|
||||
not isLabeledBarrierEdge(cfg, pred, succ, summary.getStartLabel()) and
|
||||
not isBarrierEdge(cfg, pred, succ)
|
||||
}
|
||||
@@ -685,17 +682,21 @@ private predicate basicFlowStep(
|
||||
private predicate exploratoryFlowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration cfg
|
||||
) {
|
||||
basicFlowStepNoBarrier(pred, succ, _, cfg) or
|
||||
basicStoreStep(pred, succ, _) or
|
||||
basicLoadStep(pred, succ, _) or
|
||||
isAdditionalStoreStep(pred, succ, _, cfg) or
|
||||
isAdditionalLoadStep(pred, succ, _, cfg) or
|
||||
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
|
||||
// the following three disjuncts taken together over-approximate flow through
|
||||
// higher-order calls
|
||||
callback(pred, succ) or
|
||||
succ = pred.(DataFlow::FunctionNode).getAParameter() or
|
||||
exploratoryBoundInvokeStep(pred, succ)
|
||||
isRelevantForward(pred, cfg) and
|
||||
isLive() and
|
||||
(
|
||||
basicFlowStepNoBarrier(pred, succ, _, cfg) or
|
||||
basicStoreStep(pred, succ, _) or
|
||||
basicLoadStep(pred, succ, _) or
|
||||
isAdditionalStoreStep(pred, succ, _, cfg) or
|
||||
isAdditionalLoadStep(pred, succ, _, cfg) or
|
||||
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
|
||||
// the following three disjuncts taken together over-approximate flow through
|
||||
// higher-order calls
|
||||
callback(pred, succ) or
|
||||
succ = pred.(DataFlow::FunctionNode).getAParameter() or
|
||||
exploratoryBoundInvokeStep(pred, succ)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -850,9 +851,11 @@ private predicate storeStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg,
|
||||
PathSummary summary
|
||||
) {
|
||||
isRelevant(pred, cfg) and
|
||||
basicStoreStep(pred, succ, prop) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
isRelevant(pred, cfg) and
|
||||
isAdditionalStoreStep(pred, succ, prop, cfg) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
@@ -957,9 +960,11 @@ private predicate loadStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg,
|
||||
PathSummary summary
|
||||
) {
|
||||
isRelevant(pred, cfg) and
|
||||
basicLoadStep(pred, succ, prop) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
isRelevant(pred, cfg) and
|
||||
isAdditionalLoadStep(pred, succ, prop, cfg) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
@@ -1184,7 +1189,6 @@ private predicate flowStep(
|
||||
// Flow into higher-order call
|
||||
flowIntoHigherOrderCall(pred, succ, cfg, summary)
|
||||
) and
|
||||
isRelevant(succ, cfg) and
|
||||
not cfg.isBarrier(succ) and
|
||||
not isBarrierEdge(cfg, pred, succ) and
|
||||
not isLabeledBarrierEdge(cfg, pred, succ, summary.getEndLabel()) and
|
||||
@@ -1238,12 +1242,25 @@ private predicate onPath(DataFlow::Node nd, DataFlow::Configuration cfg, PathSum
|
||||
not cfg.isLabeledBarrier(nd, summary.getEndLabel())
|
||||
or
|
||||
exists(DataFlow::Node mid, PathSummary stepSummary |
|
||||
reachableFromSource(nd, cfg, summary) and
|
||||
flowStep(nd, id(cfg), mid, stepSummary) and
|
||||
onPathStep(nd, cfg, summary, stepSummary, mid) and
|
||||
onPath(mid, id(cfg), summary.append(stepSummary))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nd` can be reached from a source under `cfg`,
|
||||
* and there is a flowStep from `nd` (with summary `summary`) to `mid` (with summary `stepSummary`).
|
||||
*
|
||||
* This predicate has been outlined from `onPath` to give the optimizer a hint about join-ordering.
|
||||
*/
|
||||
private predicate onPathStep(
|
||||
DataFlow::Node nd, DataFlow::Configuration cfg, PathSummary summary, PathSummary stepSummary,
|
||||
DataFlow::Node mid
|
||||
) {
|
||||
reachableFromSource(nd, cfg, summary) and
|
||||
flowStep(nd, id(cfg), mid, stepSummary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a configuration that has at least one source and at least one sink.
|
||||
*/
|
||||
|
||||
@@ -587,8 +587,8 @@ class ArrayConstructorInvokeNode extends DataFlow::InvokeNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to the creation or a new array, either through an array literal
|
||||
* or an invocation of the `Array` constructor.
|
||||
* A data flow node corresponding to the creation or a new array, either through an array literal,
|
||||
* an invocation of the `Array` constructor, or the `Array.from` method.
|
||||
*
|
||||
*
|
||||
* Examples:
|
||||
|
||||
@@ -229,45 +229,45 @@ module TaintTracking {
|
||||
* promises.
|
||||
*/
|
||||
private class HeapTaintStep extends AdditionalTaintStep {
|
||||
HeapTaintStep() {
|
||||
this = DataFlow::valueNode(_) or
|
||||
this = DataFlow::parameterNode(_) or
|
||||
this instanceof DataFlow::PropRead
|
||||
}
|
||||
HeapTaintStep() { heapStep(_, this) }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
exists(Expr e, Expr f | e = this.asExpr() and f = pred.asExpr() |
|
||||
// arrays with tainted elements and objects with tainted property names are tainted
|
||||
e.(ArrayExpr).getAnElement() = f
|
||||
or
|
||||
exists(Property prop | e.(ObjectExpr).getAProperty() = prop |
|
||||
prop.isComputed() and f = prop.getNameExpr()
|
||||
)
|
||||
or
|
||||
// awaiting a tainted expression gives a tainted result
|
||||
e.(AwaitExpr).getOperand() = f
|
||||
or
|
||||
// spreading a tainted object into an object literal gives a tainted object
|
||||
e.(ObjectExpr).getAProperty().(SpreadProperty).getInit().(SpreadElement).getOperand() = f
|
||||
or
|
||||
// spreading a tainted value into an array literal gives a tainted array
|
||||
e.(ArrayExpr).getAnElement().(SpreadElement).getOperand() = f
|
||||
)
|
||||
or
|
||||
// reading from a tainted object yields a tainted result
|
||||
this = succ and
|
||||
succ.(DataFlow::PropRead).getBase() = pred
|
||||
or
|
||||
// iterating over a tainted iterator taints the loop variable
|
||||
exists(ForOfStmt fos |
|
||||
this = DataFlow::valueNode(fos.getIterationDomain()) and
|
||||
pred = this and
|
||||
succ = DataFlow::lvalueNode(fos.getLValue())
|
||||
)
|
||||
heapStep(pred, succ) and succ = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is taint propagation through the heap from `pred` to `succ`.
|
||||
*/
|
||||
private predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Expr e, Expr f | e = succ.asExpr() and f = pred.asExpr() |
|
||||
// arrays with tainted elements and objects with tainted property names are tainted
|
||||
e.(ArrayExpr).getAnElement() = f
|
||||
or
|
||||
exists(Property prop | e.(ObjectExpr).getAProperty() = prop |
|
||||
prop.isComputed() and f = prop.getNameExpr()
|
||||
)
|
||||
or
|
||||
// awaiting a tainted expression gives a tainted result
|
||||
e.(AwaitExpr).getOperand() = f
|
||||
or
|
||||
// spreading a tainted object into an object literal gives a tainted object
|
||||
e.(ObjectExpr).getAProperty().(SpreadProperty).getInit().(SpreadElement).getOperand() = f
|
||||
or
|
||||
// spreading a tainted value into an array literal gives a tainted array
|
||||
e.(ArrayExpr).getAnElement().(SpreadElement).getOperand() = f
|
||||
)
|
||||
or
|
||||
// reading from a tainted object yields a tainted result
|
||||
succ.(DataFlow::PropRead).getBase() = pred
|
||||
or
|
||||
// iterating over a tainted iterator taints the loop variable
|
||||
exists(ForOfStmt fos |
|
||||
pred = DataFlow::valueNode(fos.getIterationDomain()) and
|
||||
succ = DataFlow::lvalueNode(fos.getLValue())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge through persistent storage.
|
||||
*/
|
||||
@@ -282,92 +282,7 @@ module TaintTracking {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge caused by the builtin array functions.
|
||||
*/
|
||||
private class ArrayFunctionTaintStep extends AdditionalTaintStep {
|
||||
DataFlow::CallNode call;
|
||||
|
||||
ArrayFunctionTaintStep() { this = call }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
arrayFunctionTaintStep(pred, succ, call)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
|
||||
*/
|
||||
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists(string name, Function f, int i |
|
||||
(name = "map" or name = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = name and
|
||||
pred = call.getReceiver() and
|
||||
succ = DataFlow::parameterNode(f.getParameter(i))
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
exists(DataFlow::FunctionNode f |
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
|
||||
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
|
||||
pred = f.getAReturn() and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getASpreadArgument() and
|
||||
// Make sure we handle reflective calls
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = name
|
||||
)
|
||||
or
|
||||
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name | name = "splice" |
|
||||
pred = call.getArgument(2) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
exists(string name |
|
||||
name = "pop" or
|
||||
name = "shift" or
|
||||
name = "slice" or
|
||||
name = "splice"
|
||||
|
|
||||
call.(DataFlow::MethodCallNode).calls(pred, name) and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
|
||||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
or
|
||||
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
|
||||
call.(DataFlow::MethodCallNode).calls(pred, "concat") and
|
||||
succ = call
|
||||
or
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
|
||||
succ = call and
|
||||
pred = call.getAnArgument()
|
||||
}
|
||||
predicate arrayFunctionTaintStep = ArrayTaintTracking::arrayFunctionTaintStep/3;
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge for assignments of the form `o[k] = v`, where
|
||||
@@ -473,86 +388,93 @@ module TaintTracking {
|
||||
* functions defined in the standard library.
|
||||
*/
|
||||
private class StringManipulationTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
|
||||
StringManipulationTaintStep() { stringManipulationStep(_, this) }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
(
|
||||
// string operations that propagate taint
|
||||
exists(string name | name = astNode.(MethodCallExpr).getMethodName() |
|
||||
pred.asExpr() = astNode.(MethodCallExpr).getReceiver() and
|
||||
(
|
||||
// sorted, interesting, properties of String.prototype
|
||||
name = "anchor" or
|
||||
name = "big" or
|
||||
name = "blink" or
|
||||
name = "bold" or
|
||||
name = "concat" or
|
||||
name = "fixed" or
|
||||
name = "fontcolor" or
|
||||
name = "fontsize" or
|
||||
name = "italics" or
|
||||
name = "link" or
|
||||
name = "padEnd" or
|
||||
name = "padStart" or
|
||||
name = "repeat" or
|
||||
name = "replace" or
|
||||
name = "slice" or
|
||||
name = "small" or
|
||||
name = "split" or
|
||||
name = "strike" or
|
||||
name = "sub" or
|
||||
name = "substr" or
|
||||
name = "substring" or
|
||||
name = "sup" or
|
||||
name = "toLocaleLowerCase" or
|
||||
name = "toLocaleUpperCase" or
|
||||
name = "toLowerCase" or
|
||||
name = "toUpperCase" or
|
||||
name = "trim" or
|
||||
name = "trimLeft" or
|
||||
name = "trimRight" or
|
||||
// sorted, interesting, properties of Object.prototype
|
||||
name = "toString" or
|
||||
name = "valueOf" or
|
||||
// sorted, interesting, properties of Array.prototype
|
||||
name = "join"
|
||||
)
|
||||
or
|
||||
exists(int i | pred.asExpr() = astNode.(MethodCallExpr).getArgument(i) |
|
||||
name = "concat"
|
||||
or
|
||||
name = "replace" and i = 1
|
||||
)
|
||||
)
|
||||
or
|
||||
// standard library constructors that propagate taint: `RegExp` and `String`
|
||||
exists(DataFlow::InvokeNode invk, string gv | gv = "RegExp" or gv = "String" |
|
||||
this = invk and
|
||||
invk = DataFlow::globalVarRef(gv).getAnInvocation() and
|
||||
pred = invk.getArgument(0)
|
||||
)
|
||||
or
|
||||
// String.fromCharCode and String.fromCodePoint
|
||||
exists(int i, MethodCallExpr mce |
|
||||
mce = astNode and
|
||||
pred.asExpr() = mce.getArgument(i) and
|
||||
(mce.getMethodName() = "fromCharCode" or mce.getMethodName() = "fromCodePoint")
|
||||
)
|
||||
or
|
||||
// `(encode|decode)URI(Component)?` propagate taint
|
||||
exists(DataFlow::CallNode c, string name |
|
||||
this = c and
|
||||
c = DataFlow::globalVarRef(name).getACall() and
|
||||
pred = c.getArgument(0)
|
||||
|
|
||||
name = "encodeURI" or
|
||||
name = "decodeURI" or
|
||||
name = "encodeURIComponent" or
|
||||
name = "decodeURIComponent"
|
||||
)
|
||||
)
|
||||
stringManipulationStep(pred, succ)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can propagate from `pred` to `succ` with a step related to string manipulation.
|
||||
*/
|
||||
private predicate stringManipulationStep(DataFlow::Node pred, DataFlow::ValueNode succ) {
|
||||
// string operations that propagate taint
|
||||
exists(string name | name = succ.getAstNode().(MethodCallExpr).getMethodName() |
|
||||
pred.asExpr() = succ.getAstNode().(MethodCallExpr).getReceiver() and
|
||||
(
|
||||
// sorted, interesting, properties of String.prototype
|
||||
name = "anchor" or
|
||||
name = "big" or
|
||||
name = "blink" or
|
||||
name = "bold" or
|
||||
name = "concat" or
|
||||
name = "fixed" or
|
||||
name = "fontcolor" or
|
||||
name = "fontsize" or
|
||||
name = "italics" or
|
||||
name = "link" or
|
||||
name = "padEnd" or
|
||||
name = "padStart" or
|
||||
name = "repeat" or
|
||||
name = "replace" or
|
||||
name = "slice" or
|
||||
name = "small" or
|
||||
name = "split" or
|
||||
name = "strike" or
|
||||
name = "sub" or
|
||||
name = "substr" or
|
||||
name = "substring" or
|
||||
name = "sup" or
|
||||
name = "toLocaleLowerCase" or
|
||||
name = "toLocaleUpperCase" or
|
||||
name = "toLowerCase" or
|
||||
name = "toUpperCase" or
|
||||
name = "trim" or
|
||||
name = "trimLeft" or
|
||||
name = "trimRight" or
|
||||
// sorted, interesting, properties of Object.prototype
|
||||
name = "toString" or
|
||||
name = "valueOf" or
|
||||
// sorted, interesting, properties of Array.prototype
|
||||
name = "join"
|
||||
)
|
||||
or
|
||||
exists(int i | pred.asExpr() = succ.getAstNode().(MethodCallExpr).getArgument(i) |
|
||||
name = "concat"
|
||||
or
|
||||
name = "replace" and i = 1
|
||||
)
|
||||
)
|
||||
or
|
||||
// standard library constructors that propagate taint: `RegExp` and `String`
|
||||
exists(DataFlow::InvokeNode invk, string gv | gv = "RegExp" or gv = "String" |
|
||||
succ = invk and
|
||||
invk = DataFlow::globalVarRef(gv).getAnInvocation() and
|
||||
pred = invk.getArgument(0)
|
||||
)
|
||||
or
|
||||
// String.fromCharCode and String.fromCodePoint
|
||||
exists(int i, MethodCallExpr mce |
|
||||
mce = succ.getAstNode() and
|
||||
pred.asExpr() = mce.getArgument(i) and
|
||||
(mce.getMethodName() = "fromCharCode" or mce.getMethodName() = "fromCodePoint")
|
||||
)
|
||||
or
|
||||
// `(encode|decode)URI(Component)?` propagate taint
|
||||
exists(DataFlow::CallNode c, string name |
|
||||
succ = c and
|
||||
c = DataFlow::globalVarRef(name).getACall() and
|
||||
pred = c.getArgument(0)
|
||||
|
|
||||
name = "encodeURI" or
|
||||
name = "decodeURI" or
|
||||
name = "encodeURIComponent" or
|
||||
name = "decodeURIComponent"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from string formatting.
|
||||
*/
|
||||
|
||||
@@ -201,7 +201,18 @@ private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataF
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() { result = getCallback([1 .. 2]).getParameter(1) }
|
||||
override DataFlow::Node getAFileName() { result = trackFileSource(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackFileSource(DataFlow::TypeTracker t) {
|
||||
t.start() and result = getCallback([1 .. 2]).getParameter(1)
|
||||
or
|
||||
t.startInPromise() and not exists(getCallback([1 .. 2])) and result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackFileSource(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,10 +231,24 @@ private module JSONFile {
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() {
|
||||
this.getCalleeName() = "readFile" and result = getCallback([1 .. 2]).getParameter(1)
|
||||
override DataFlow::Node getADataNode() { result = trackRead(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackRead(DataFlow::TypeTracker t) {
|
||||
this.getCalleeName() = "readFile" and
|
||||
(
|
||||
t.start() and result = getCallback([1 .. 2]).getParameter(1)
|
||||
or
|
||||
t.startInPromise() and not exists(getCallback([1 .. 2])) and result = this
|
||||
)
|
||||
or
|
||||
this.getCalleeName() = "readFileSync" and result = this
|
||||
t.start() and
|
||||
this.getCalleeName() = "readFileSync" and
|
||||
result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackRead(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +268,122 @@ private module JSONFile {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `load-json-file`.
|
||||
*/
|
||||
private class LoadJsonFile extends FileSystemReadAccess, DataFlow::CallNode {
|
||||
LoadJsonFile() {
|
||||
this = DataFlow::moduleImport("load-json-file").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("load-json-file", "sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = trackRead(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackRead(DataFlow::TypeTracker t) {
|
||||
this.getCalleeName() = "sync" and t.start() and result = this
|
||||
or
|
||||
not this.getCalleeName() = "sync" and t.startInPromise() and result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackRead(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `write-json-file`.
|
||||
*/
|
||||
private class WriteJsonFile extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
WriteJsonFile() {
|
||||
this = DataFlow::moduleImport("write-json-file").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("write-json-file", "sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `walkdir`.
|
||||
*/
|
||||
private class WalkDir extends FileNameProducer, FileSystemAccess, DataFlow::CallNode {
|
||||
WalkDir() {
|
||||
this = DataFlow::moduleImport("walkdir").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("walkdir", "sync").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("walkdir", "async").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() { result = trackFileSource(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private DataFlow::SourceNode trackFileSource(DataFlow::TypeTracker t) {
|
||||
not this.getCalleeName() = any(string s | s = "sync" or s = "async") and
|
||||
t.start() and
|
||||
(
|
||||
result = getCallback(getNumArgument() - 1).getParameter(0)
|
||||
or
|
||||
result = getAMethodCall(EventEmitter::on()).getCallback(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
t.start() and this.getCalleeName() = "sync" and result = this
|
||||
or
|
||||
t.startInPromise() and this.getCalleeName() = "async" and result = this
|
||||
or
|
||||
// Tracking out of a promise
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
result = PromiseTypeTracking::promiseStep(trackFileSource(t2), t, t2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `globule`.
|
||||
*/
|
||||
private class Globule extends FileNameProducer, FileSystemAccess, DataFlow::CallNode {
|
||||
Globule() {
|
||||
this = DataFlow::moduleMember("globule", "find").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "match").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "isMatch").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "mapping").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("globule", "findMapping").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
(this.getCalleeName() = "match" or this.getCalleeName() = "isMatch") and
|
||||
result = getArgument(1)
|
||||
or
|
||||
this.getCalleeName() = "mapping" and
|
||||
(
|
||||
result = getAnArgument() and not exists(result.getALocalSource().getAPropertyWrite("src"))
|
||||
or
|
||||
result = getAnArgument().getALocalSource().getAPropertyWrite("src").getRhs()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAFileName() {
|
||||
result = this and
|
||||
(
|
||||
this.getCalleeName() = "find" or
|
||||
this.getCalleeName() = "match" or
|
||||
this.getCalleeName() = "findMapping" or
|
||||
this.getCalleeName() = "mapping"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A file system access made by a NodeJS library.
|
||||
* This class models multiple NodeJS libraries that access files.
|
||||
@@ -257,6 +398,10 @@ private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
|
||||
or
|
||||
this = DataFlow::moduleImport("rimraf").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("readdirp").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("walker").getACall()
|
||||
or
|
||||
this =
|
||||
DataFlow::moduleMember("node-dir",
|
||||
any(string s |
|
||||
|
||||
@@ -132,15 +132,22 @@ private module Postgres {
|
||||
result = DataFlow::moduleImport("pg-pool").getAnInstantiation()
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode clientOrPool(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
(result = client() or result = newPool())
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = clientOrPool(t2).track(t2, t))
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode clientOrPool() {
|
||||
result = clientOrPool(DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/** A call to the Postgres `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::ValueNode {
|
||||
override MethodCallExpr astNode;
|
||||
|
||||
QueryCall() {
|
||||
exists(DataFlow::SourceNode recv | recv = client() or recv = newPool() |
|
||||
this = recv.getAMethodCall("query")
|
||||
)
|
||||
}
|
||||
QueryCall() { this = clientOrPool().getAMethodCall("query") }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = DataFlow::valueNode(astNode.getArgument(0))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides classes for working with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [ws](https://github.com/websockets/ws).
|
||||
* Provides classes for working with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), [ws](https://github.com/websockets/ws), and [SockJS](http://sockjs.org).
|
||||
*
|
||||
* The model is based on the EventEmitter model, and there is therefore a
|
||||
* data-flow step from where a WebSocket event is sent to where the message
|
||||
@@ -18,26 +18,64 @@ import javascript
|
||||
*/
|
||||
private string channelName() { result = "message" }
|
||||
|
||||
/**
|
||||
* The names of the libraries modelled in this file.
|
||||
*/
|
||||
private module LibraryNames {
|
||||
string sockjs() { result = "SockJS" }
|
||||
|
||||
string websocket() { result = "WebSocket" }
|
||||
|
||||
string ws() { result = "ws" }
|
||||
|
||||
class LibraryName extends string {
|
||||
LibraryName() { this = sockjs() or this = websocket() or this = ws() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the websocket library named `client` can send a message to the library named `server`.
|
||||
* Both `client` and `server` are library names defined in `LibraryNames`.
|
||||
*/
|
||||
private predicate areLibrariesCompatible(
|
||||
LibraryNames::LibraryName client, LibraryNames::LibraryName server
|
||||
) {
|
||||
// sockjs is a WebSocket emulating library, but not actually an implementation of WebSockets.
|
||||
client = LibraryNames::sockjs() and server = LibraryNames::sockjs()
|
||||
or
|
||||
server = LibraryNames::ws() and
|
||||
(client = LibraryNames::ws() or client = LibraryNames::websocket())
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes that model WebSockets clients.
|
||||
*/
|
||||
module ClientWebSocket {
|
||||
private import LibraryNames
|
||||
|
||||
/**
|
||||
* A class that can be used to instantiate a WebSocket instance.
|
||||
*/
|
||||
class SocketClass extends DataFlow::SourceNode {
|
||||
boolean isNode;
|
||||
LibraryName library; // the name of the WebSocket library. Can be one of the libraries defined in `LibraryNames`.
|
||||
|
||||
SocketClass() {
|
||||
this = DataFlow::globalVarRef("WebSocket") and isNode = false
|
||||
this = DataFlow::globalVarRef("WebSocket") and library = websocket()
|
||||
or
|
||||
this = DataFlow::moduleImport("ws") and isNode = true
|
||||
this = DataFlow::moduleImport("ws") and library = ws()
|
||||
or
|
||||
// the sockjs-client library:https://www.npmjs.com/package/sockjs-client
|
||||
library = sockjs() and
|
||||
(
|
||||
this = DataFlow::moduleImport("sockjs-client") or
|
||||
this = DataFlow::globalVarRef("SockJS")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class is an import of the "ws" module.
|
||||
* Gets the WebSocket library name.
|
||||
*/
|
||||
predicate isNode() { isNode = true }
|
||||
LibraryName getLibrary() { result = library }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,11 +87,9 @@ module ClientWebSocket {
|
||||
ClientSocket() { this = socketClass.getAnInstantiation() }
|
||||
|
||||
/**
|
||||
* Holds if this ClientSocket is created from the "ws" module.
|
||||
*
|
||||
* The predicate is used to differentiate where the behavior of the "ws" module differs from the native WebSocket in browsers.
|
||||
* Gets the WebSocket library name.
|
||||
*/
|
||||
predicate isNode() { socketClass.isNode() }
|
||||
LibraryName getLibrary() { result = socketClass.getLibrary() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +104,10 @@ module ClientWebSocket {
|
||||
|
||||
override DataFlow::Node getSentItem(int i) { i = 0 and result = this.getArgument(0) }
|
||||
|
||||
override ServerWebSocket::ReceiveNode getAReceiver() { any() }
|
||||
override ServerWebSocket::ReceiveNode getAReceiver() {
|
||||
areLibrariesCompatible(emitter.getLibrary(),
|
||||
result.getEmitter().(ServerWebSocket::ServerSocket).getLibrary())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +155,7 @@ module ClientWebSocket {
|
||||
*/
|
||||
private class WSReceiveNode extends ClientWebSocket::ReceiveNode {
|
||||
WSReceiveNode() {
|
||||
emitter.isNode() and
|
||||
emitter.getLibrary() = ws() and
|
||||
this = getAMessageHandler(emitter, EventEmitter::on())
|
||||
}
|
||||
|
||||
@@ -128,21 +167,38 @@ module ClientWebSocket {
|
||||
* Provides classes that model WebSocket servers.
|
||||
*/
|
||||
module ServerWebSocket {
|
||||
private import LibraryNames
|
||||
|
||||
/**
|
||||
* Gets a server created by a library named `library`.
|
||||
*/
|
||||
DataFlow::SourceNode getAServer(LibraryName library) {
|
||||
library = ws() and
|
||||
result = DataFlow::moduleImport("ws").getAConstructorInvocation("Server")
|
||||
or
|
||||
library = sockjs() and
|
||||
result = DataFlow::moduleImport("sockjs").getAMemberCall("createServer")
|
||||
}
|
||||
|
||||
/**
|
||||
* A server WebSocket instance.
|
||||
*/
|
||||
class ServerSocket extends EventEmitter::Range, DataFlow::SourceNode {
|
||||
LibraryName library;
|
||||
|
||||
ServerSocket() {
|
||||
exists(DataFlow::CallNode onCall |
|
||||
onCall =
|
||||
DataFlow::moduleImport("ws")
|
||||
.getAConstructorInvocation("Server")
|
||||
.getAMemberCall(EventEmitter::on()) and
|
||||
onCall = getAServer(library).getAMemberCall(EventEmitter::on()) and
|
||||
onCall.getArgument(0).mayHaveStringValue("connection")
|
||||
|
|
||||
this = onCall.getCallback(1).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the library that created this server socket.
|
||||
*/
|
||||
LibraryName getLibrary() { result = library }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +207,13 @@ module ServerWebSocket {
|
||||
class SendNode extends EventDispatch::Range, DataFlow::CallNode {
|
||||
override ServerSocket emitter;
|
||||
|
||||
SendNode() { this = emitter.getAMemberCall("send") }
|
||||
SendNode() {
|
||||
emitter.getLibrary() = ws() and
|
||||
this = emitter.getAMemberCall("send")
|
||||
or
|
||||
emitter.getLibrary() = sockjs() and
|
||||
this = emitter.getAMemberCall("write")
|
||||
}
|
||||
|
||||
override string getChannel() { result = channelName() }
|
||||
|
||||
@@ -160,7 +222,10 @@ module ServerWebSocket {
|
||||
result = getArgument(0)
|
||||
}
|
||||
|
||||
override ClientWebSocket::ReceiveNode getAReceiver() { any() }
|
||||
override ClientWebSocket::ReceiveNode getAReceiver() {
|
||||
areLibrariesCompatible(result.getEmitter().(ClientWebSocket::ClientSocket).getLibrary(),
|
||||
emitter.getLibrary())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,8 +235,14 @@ module ServerWebSocket {
|
||||
override ServerSocket emitter;
|
||||
|
||||
ReceiveNode() {
|
||||
this = emitter.getAMemberCall(EventEmitter::on()) and
|
||||
this.getArgument(0).mayHaveStringValue("message")
|
||||
exists(string eventName |
|
||||
emitter.getLibrary() = ws() and eventName = "message"
|
||||
or
|
||||
emitter.getLibrary() = sockjs() and eventName = "data"
|
||||
|
|
||||
this = emitter.getAMemberCall(EventEmitter::on()) and
|
||||
this.getArgument(0).mayHaveStringValue(eventName)
|
||||
)
|
||||
}
|
||||
|
||||
override string getChannel() { result = channelName() }
|
||||
|
||||
13
javascript/ql/test/library-tests/Arrays/DataFlow.expected
Normal file
13
javascript/ql/test/library-tests/Arrays/DataFlow.expected
Normal file
@@ -0,0 +1,13 @@
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:5:8:5:14 | obj.foo |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:11:10:11:15 | arr[i] |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:15:27:15:27 | e |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:16:23:16:23 | e |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:20:8:20:16 | arr.pop() |
|
||||
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
|
||||
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
|
||||
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:30:8:30:17 | arr4.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:33:8:33:17 | arr5.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:35:8:35:26 | arr5.slice(2).pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:41:8:41:17 | arr6.pop() |
|
||||
| arrays.js:44:4:44:11 | "source" | arrays.js:45:10:45:18 | ary.pop() |
|
||||
15
javascript/ql/test/library-tests/Arrays/DataFlow.ql
Normal file
15
javascript/ql/test/library-tests/Arrays/DataFlow.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
import javascript
|
||||
|
||||
class ArrayFlowConfig extends DataFlow::Configuration {
|
||||
ArrayFlowConfig() { this = "ArrayFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
from ArrayFlowConfig config, DataFlow::Node src, DataFlow::Node snk
|
||||
where config.hasFlow(src, snk)
|
||||
select src, snk
|
||||
50
javascript/ql/test/library-tests/Arrays/arrays.js
Normal file
50
javascript/ql/test/library-tests/Arrays/arrays.js
Normal file
@@ -0,0 +1,50 @@
|
||||
(function () {
|
||||
let source = "source";
|
||||
|
||||
var obj = { foo: source };
|
||||
sink(obj.foo); // NOT OK
|
||||
|
||||
var arr = [];
|
||||
arr.push(source);
|
||||
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
sink(arr[i]); // NOT OK
|
||||
}
|
||||
|
||||
|
||||
arr.forEach((e) => sink(e)); // NOT OK
|
||||
arr.map((e) => sink(e)); // NOT OK
|
||||
|
||||
[1, 2, 3].map(i => "source").forEach(e => sink(e)); // NOT OK.
|
||||
|
||||
sink(arr.pop()); // NOT OK
|
||||
|
||||
var arr2 = ["source"];
|
||||
sink(arr2.pop()); // NOT OK
|
||||
|
||||
var arr3 = ["source"];
|
||||
sink(arr3.pop()); // NOT OK
|
||||
|
||||
var arr4 = [];
|
||||
arr4.splice(0, 0, "source");
|
||||
sink(arr4.pop()); // NOT OK
|
||||
|
||||
var arr5 = [].concat(arr4);
|
||||
sink(arr5.pop()); // NOT OK
|
||||
|
||||
sink(arr5.slice(2).pop()); // NOT OK
|
||||
|
||||
var arr6 = [];
|
||||
for (var i = 0; i < arr5.length; i++) {
|
||||
arr6[i] = arr5[i];
|
||||
}
|
||||
sink(arr6.pop()); // NOT OK
|
||||
|
||||
|
||||
["source"].forEach((e, i, ary) => {
|
||||
sink(ary.pop()); // NOT OK
|
||||
sink(ary); // OK - its the array itself, not an element.
|
||||
});
|
||||
|
||||
sink(arr[0]); // OK - tuple like usage.
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
| a.js:1:15:1:23 | "tainted" | b.js:6:13:6:13 | x |
|
||||
| a.js:2:15:2:28 | "also tainted" | b.js:5:13:5:29 | notTaintedTrustMe |
|
||||
| callback.js:16:14:16:21 | "source" | callback.js:13:14:13:14 | x |
|
||||
| callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
|
||||
| callback.js:27:15:27:23 | "source3" | callback.js:13:14:13:14 | x |
|
||||
| destructuring.js:2:16:2:24 | "tainted" | destructuring.js:9:15:9:22 | tainted2 |
|
||||
| destructuring.js:19:15:19:23 | "tainted" | destructuring.js:14:15:14:15 | p |
|
||||
@@ -44,6 +45,7 @@
|
||||
| tst2.js:6:24:6:37 | "also tainted" | tst2.js:11:15:11:24 | g(source2) |
|
||||
| tst6.mjs:12:14:12:21 | "source" | tst6.mjs:14:12:14:16 | a.m() |
|
||||
| tst6.mjs:16:15:16:23 | "source2" | tst6.mjs:18:13:18:24 | a.m.call(a2) |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:28:20:28:22 | elt |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:39:17:39:17 | x |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:41:19:41:19 | x |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:45:17:45:17 | x |
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
| a.js:1:15:1:23 | "tainted" | b.js:6:13:6:13 | x |
|
||||
| a.js:2:15:2:28 | "also tainted" | b.js:5:13:5:29 | notTaintedTrustMe |
|
||||
| callback.js:16:14:16:21 | "source" | callback.js:13:14:13:14 | x |
|
||||
| callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
|
||||
| callback.js:27:15:27:23 | "source3" | callback.js:13:14:13:14 | x |
|
||||
| custom.js:1:14:1:26 | "verschmutzt" | custom.js:2:15:2:20 | quelle |
|
||||
| destructuring.js:2:16:2:24 | "tainted" | destructuring.js:9:15:9:22 | tainted2 |
|
||||
@@ -45,6 +46,7 @@
|
||||
| tst2.js:6:24:6:37 | "also tainted" | tst2.js:11:15:11:24 | g(source2) |
|
||||
| tst6.mjs:12:14:12:21 | "source" | tst6.mjs:14:12:14:16 | a.m() |
|
||||
| tst6.mjs:16:15:16:23 | "source2" | tst6.mjs:18:13:18:24 | a.m.call(a2) |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:28:20:28:22 | elt |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:39:17:39:17 | x |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:41:19:41:19 | x |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:45:17:45:17 | x |
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:8:16:8:20 | xs[i] |
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:6:26:6:28 | val |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:7:16:7:18 | val |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:37:11:37:11 | v |
|
||||
@@ -30,3 +33,5 @@
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:24:20:24:20 | v |
|
||||
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:38:32:38:32 | v |
|
||||
| missing | tst.js:2:17:2:22 | "src1" | tst.js:27:22:27:24 | elt |
|
||||
| missing | tst.js:2:17:2:22 | "src1" | tst.js:28:20:28:22 | elt |
|
||||
|
||||
@@ -16,11 +16,34 @@ getPathArgument
|
||||
| file-access.js:47:1:47:36 | vfs.des ... true }) | file-access.js:47:10:47:13 | './' |
|
||||
| file-access.js:51:1:51:36 | ncp("fr ... rr) {}) | file-access.js:51:5:51:10 | "from" |
|
||||
| file-access.js:51:1:51:36 | ncp("fr ... rr) {}) | file-access.js:51:13:51:16 | "to" |
|
||||
| file-access.js:56:23:56:46 | loadJso ... .json') | file-access.js:56:36:56:45 | 'foo.json' |
|
||||
| file-access.js:57:14:57:42 | loadJso ... .json') | file-access.js:57:32:57:41 | 'foo.json' |
|
||||
| file-access.js:62:5:62:42 | writeJs ... true}) | file-access.js:62:19:62:28 | 'bar.json' |
|
||||
| file-access.js:63:2:63:60 | writeJs ... : " "}) | file-access.js:63:21:63:30 | 'bar.json' |
|
||||
| file-access.js:67:1:67:35 | readdir ... *.js'}) | file-access.js:67:10:67:12 | '.' |
|
||||
| file-access.js:70:1:72:2 | recursi ... es);\\n}) | file-access.js:70:11:70:29 | "directory/to/read" |
|
||||
| file-access.js:73:1:73:30 | recursi ... /read") | file-access.js:73:11:73:29 | "directory/to/read" |
|
||||
| file-access.js:75:1:75:29 | jsonfil ... .json') | file-access.js:75:19:75:28 | 'baz.json' |
|
||||
| file-access.js:79:2:81:3 | walk('. ... h);\\n\\t}) | file-access.js:79:7:79:11 | '../' |
|
||||
| file-access.js:82:16:82:26 | walk('../') | file-access.js:82:21:82:25 | '../' |
|
||||
| file-access.js:84:2:86:3 | walk.sy ... h);\\n\\t}) | file-access.js:84:12:84:16 | '../' |
|
||||
| file-access.js:87:14:87:29 | walk.sync('../') | file-access.js:87:24:87:28 | '../' |
|
||||
| file-access.js:88:21:88:37 | walk.async('../') | file-access.js:88:32:88:36 | '../' |
|
||||
| file-access.js:92:1:92:15 | walker('/etc/') | file-access.js:92:8:92:14 | '/etc/' |
|
||||
| tst-file-names.js:43:15:43:50 | globule ... o.js"]) | tst-file-names.js:43:40:43:49 | ["foo.js"] |
|
||||
| tst-file-names.js:44:12:44:49 | globule ... o.js"]) | tst-file-names.js:44:39:44:48 | ["foo.js"] |
|
||||
| tst-file-names.js:46:12:46:51 | globule ... .js"]}) | tst-file-names.js:46:34:46:49 | ["a.js", "b.js"] |
|
||||
| tst-file-names.js:47:12:47:52 | globule ... b.js"]) | tst-file-names.js:47:28:47:51 | ["foo/a ... /b.js"] |
|
||||
getReadNode
|
||||
| file-access.js:25:1:25:59 | jsonfil ... bj) {}) | file-access.js:25:52:25:54 | obj |
|
||||
| file-access.js:26:1:26:39 | jsonfil ... .json') | file-access.js:26:1:26:39 | jsonfil ... .json') |
|
||||
| file-access.js:56:23:56:46 | loadJso ... .json') | file-access.js:56:17:56:46 | await l ... .json') |
|
||||
| file-access.js:57:14:57:42 | loadJso ... .json') | file-access.js:57:14:57:42 | loadJso ... .json') |
|
||||
| file-access.js:75:1:75:29 | jsonfil ... .json') | file-access.js:75:36:75:38 | obj |
|
||||
getWriteNode
|
||||
| file-access.js:15:1:15:60 | writeFi ... rr) {}) | file-access.js:15:31:15:36 | 'Data' |
|
||||
| file-access.js:18:1:18:59 | writeFi ... tions]) | file-access.js:18:37:18:47 | "More data" |
|
||||
| file-access.js:28:1:28:60 | jsonfil ... rr) {}) | file-access.js:28:38:28:40 | obj |
|
||||
| file-access.js:29:1:29:45 | jsonfil ... ', obj) | file-access.js:29:42:29:44 | obj |
|
||||
| file-access.js:62:5:62:42 | writeJs ... true}) | file-access.js:62:31:62:41 | {bar: true} |
|
||||
| file-access.js:63:2:63:60 | writeJs ... : " "}) | file-access.js:63:33:63:44 | {bar: false} |
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
| file-access.js:22:39:22:43 | files |
|
||||
| file-access.js:70:47:70:51 | files |
|
||||
| file-access.js:73:37:73:42 | files2 |
|
||||
| file-access.js:79:23:79:26 | path |
|
||||
| file-access.js:83:30:83:37 | filename |
|
||||
| file-access.js:84:2:86:3 | walk.sy ... h);\\n\\t}) |
|
||||
| file-access.js:87:14:87:29 | walk.sync('../') |
|
||||
| file-access.js:88:15:88:37 | await w ... ('../') |
|
||||
| tst-file-names.js:7:1:7:10 | walkSync() |
|
||||
| tst-file-names.js:9:35:9:44 | stats.name |
|
||||
| tst-file-names.js:11:1:11:12 | glob.sync(_) |
|
||||
@@ -15,3 +22,8 @@
|
||||
| tst-file-names.js:34:15:34:29 | await globby(_) |
|
||||
| tst-file-names.js:36:16:36:38 | await f ... sync(_) |
|
||||
| tst-file-names.js:38:16:38:57 | await f ... => {}) |
|
||||
| tst-file-names.js:42:17:42:39 | globule ... /*.js') |
|
||||
| tst-file-names.js:43:15:43:50 | globule ... o.js"]) |
|
||||
| tst-file-names.js:45:12:45:42 | globule ... /*.js") |
|
||||
| tst-file-names.js:46:12:46:51 | globule ... .js"]}) |
|
||||
| tst-file-names.js:47:12:47:52 | globule ... b.js"]) |
|
||||
|
||||
@@ -48,4 +48,45 @@ vfs.dest('./', { sourcemaps: true });
|
||||
|
||||
|
||||
var ncp = require('ncp').ncp;
|
||||
ncp("from", "to", function (err) {});
|
||||
ncp("from", "to", function (err) {});
|
||||
|
||||
|
||||
const loadJsonFile = require('load-json-file');
|
||||
(async () => {
|
||||
console.log(await loadJsonFile('foo.json'));
|
||||
console.log(loadJsonFile.sync('foo.json'));
|
||||
})();
|
||||
|
||||
const writeJsonFile = require('write-json-file');
|
||||
(async () => {
|
||||
writeJsonFile('bar.json', {bar: true});
|
||||
writeJsonFile.sync('bar.json', {bar: false}, {indent: " "})
|
||||
})();
|
||||
|
||||
var readdirp = require("readdirp");
|
||||
readdirp('.', {fileFilter: '*.js'}).on('data', (entry) => { /* stream and promise api not modelled yet */ })
|
||||
|
||||
var recursive = require("recursive-readdir");
|
||||
recursive("directory/to/read", function (err, files) {
|
||||
console.log(files);
|
||||
});
|
||||
recursive("directory/to/read").then(files2 => console.log(files2));
|
||||
|
||||
jsonfile.readFile('baz.json').then(obj => console.log(obj))
|
||||
|
||||
(async function () {
|
||||
var walk = require('walkdir');
|
||||
walk('../', function(path, stat) {
|
||||
console.log('found: ', path);
|
||||
});
|
||||
var emitter = walk('../');
|
||||
emitter.on('file', function(filename, stat) { });
|
||||
walk.sync('../', function(path, stat) {
|
||||
console.log('found sync:', path);
|
||||
});
|
||||
var paths = walk.sync('../');
|
||||
let result = await walk.async('../')
|
||||
})();
|
||||
|
||||
var walker = require("walker");
|
||||
walker('/etc/').filterDir(() => {}).on('entry', () => {}); // only file access modelled.
|
||||
|
||||
@@ -37,3 +37,11 @@ async function foo() {
|
||||
|
||||
var files2 = await fastGlob.async(_).catch((wat) => {});
|
||||
}
|
||||
|
||||
var globule = require('globule');
|
||||
var filepaths = globule.find('**/*.js');
|
||||
var matches = globule.match('**/*.js', ["foo.js"])
|
||||
var bool = globule.isMatch('**/*.js', ["foo.js"])
|
||||
var map1 = globule.findMapping("foo/*.js")
|
||||
var map2 = globule.mapping({src: ["a.js", "b.js"]})
|
||||
var map3 = globule.mapping(["foo/a.js", "foo/b.js"])
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
| postgres1.js:37:21:37:24 | text |
|
||||
| postgres2.js:30:16:30:41 | 'SELECT ... number' |
|
||||
| postgres3.js:15:16:15:40 | 'SELECT ... s name' |
|
||||
| postgres5.js:8:21:8:25 | query |
|
||||
| sequelize2.js:10:17:10:118 | 'SELECT ... Y name' |
|
||||
| sequelize.js:8:17:8:118 | 'SELECT ... Y name' |
|
||||
| spanner2.js:5:26:5:35 | "SQL code" |
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const pg = require('pg');
|
||||
|
||||
function PgWrapper() {
|
||||
this.pool = new pg.Pool({});
|
||||
}
|
||||
|
||||
PgWrapper.prototype.query = function (query, params, cb) {
|
||||
this.pool.query(query, params || [], cb);
|
||||
};
|
||||
@@ -2,14 +2,31 @@
|
||||
const socket = new WebSocket('ws://localhost:8080');
|
||||
|
||||
socket.addEventListener('open', function (event) {
|
||||
socket.send('Hi from browser!');
|
||||
socket.send('Hi from browser!');
|
||||
});
|
||||
|
||||
socket.addEventListener('message', function (event) {
|
||||
console.log('Message from server ', event.data);
|
||||
console.log('Message from server ', event.data);
|
||||
});
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
console.log("Message from server 2", event.data)
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
(function () {
|
||||
var sock = new SockJS('http://0.0.0.0:9999/echo');
|
||||
sock.onopen = function () {
|
||||
sock.send('test');
|
||||
};
|
||||
|
||||
socket.onmessage = function(event) {
|
||||
console.log("Message from server 2", event.data)
|
||||
};
|
||||
})();
|
||||
sock.onmessage = function (e) {
|
||||
console.log('message', e.data);
|
||||
sock.close();
|
||||
};
|
||||
|
||||
sock.addEventListener('message', function (event) {
|
||||
console.log('Using addEventListener ', event.data);
|
||||
});
|
||||
})
|
||||
@@ -4,10 +4,10 @@
|
||||
const ws = new WebSocket('ws://example.org');
|
||||
|
||||
ws.on('open', function open() {
|
||||
ws.send('Hi from client!');
|
||||
ws.send('Hi from client!');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(data) {
|
||||
console.log(data);
|
||||
console.log(data);
|
||||
});
|
||||
})();
|
||||
@@ -4,10 +4,10 @@
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
|
||||
ws.send('Hi from server!');
|
||||
ws.send('Hi from server!');
|
||||
});
|
||||
})();
|
||||
@@ -5,12 +5,12 @@ const sockjs = require('sockjs');
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const sockjs_echo = sockjs.createServer({});
|
||||
sockjs_echo.on('connection', function(conn) {
|
||||
conn.on('data', function(message) {
|
||||
sockjs_echo.on('connection', function (conn) {
|
||||
conn.on('data', function (message) {
|
||||
var data = JSON.parse(message);
|
||||
conn.write(JSON.stringify(eval(data.test)));
|
||||
});
|
||||
});
|
||||
|
||||
sockjs_echo.installHandlers(server, {prefix:'/echo'});
|
||||
sockjs_echo.installHandlers(server, { prefix: '/echo' });
|
||||
server.listen(9090, '127.0.0.1');
|
||||
@@ -1,22 +1,35 @@
|
||||
clientSocket
|
||||
| browser.js:2:17:2:52 | new Web ... :8080') |
|
||||
| browser.js:19:13:19:50 | new Soc ... /echo') |
|
||||
| client.js:4:13:4:45 | new Web ... e.org') |
|
||||
clientSend
|
||||
| browser.js:5:6:5:36 | socket. ... wser!') |
|
||||
| client.js:7:5:7:30 | ws.send ... ient!') |
|
||||
| browser.js:5:3:5:33 | socket. ... wser!') |
|
||||
| browser.js:21:3:21:19 | sock.send('test') |
|
||||
| client.js:7:3:7:28 | ws.send ... ient!') |
|
||||
clientReceive
|
||||
| browser.js:8:37:10:2 | functio ... ta);\\n\\t} |
|
||||
| browser.js:12:21:14:5 | functio ... )\\n } |
|
||||
| browser.js:12:21:14:2 | functio ... ata)\\n\\t} |
|
||||
| browser.js:24:19:27:2 | functio ... e();\\n\\t} |
|
||||
| browser.js:29:35:31:2 | functio ... ta);\\n\\t} |
|
||||
| client.js:10:19:12:2 | functio ... ta);\\n\\t} |
|
||||
serverSocket
|
||||
| server.js:6:43:6:44 | ws |
|
||||
| sockjs.js:8:40:8:43 | conn |
|
||||
serverSend
|
||||
| server.js:11:5:11:30 | ws.send ... rver!') |
|
||||
| server.js:11:3:11:28 | ws.send ... rver!') |
|
||||
| sockjs.js:11:9:11:51 | conn.wr ... test))) |
|
||||
serverReceive
|
||||
| server.js:7:5:9:6 | ws.on(' ... \\n \\t\\t}) |
|
||||
| server.js:7:3:9:4 | ws.on(' ... );\\n\\t\\t}) |
|
||||
| sockjs.js:9:5:12:6 | conn.on ... \\n }) |
|
||||
taintStep
|
||||
| browser.js:5:18:5:35 | 'Hi from browser!' | server.js:7:40:7:46 | message |
|
||||
| client.js:7:13:7:29 | 'Hi from client!' | server.js:7:40:7:46 | message |
|
||||
| server.js:11:13:11:29 | 'Hi from server!' | browser.js:9:42:9:51 | event.data |
|
||||
| server.js:11:13:11:29 | 'Hi from server!' | browser.js:13:44:13:53 | event.data |
|
||||
| server.js:11:13:11:29 | 'Hi from server!' | client.js:10:37:10:40 | data |
|
||||
| browser.js:5:15:5:32 | 'Hi from browser!' | server.js:7:38:7:44 | message |
|
||||
| browser.js:21:13:21:18 | 'test' | sockjs.js:9:31:9:37 | message |
|
||||
| client.js:7:11:7:27 | 'Hi from client!' | server.js:7:38:7:44 | message |
|
||||
| server.js:11:11:11:27 | 'Hi from server!' | browser.js:9:39:9:48 | event.data |
|
||||
| server.js:11:11:11:27 | 'Hi from server!' | browser.js:13:40:13:49 | event.data |
|
||||
| server.js:11:11:11:27 | 'Hi from server!' | client.js:10:37:10:40 | data |
|
||||
| sockjs.js:11:20:11:50 | JSON.st ... .test)) | browser.js:25:26:25:31 | e.data |
|
||||
| sockjs.js:11:20:11:50 | JSON.st ... .test)) | browser.js:30:42:30:51 | event.data |
|
||||
remoteFlow
|
||||
| server.js:7:38:7:44 | message |
|
||||
| sockjs.js:9:31:9:37 | message |
|
||||
|
||||
@@ -15,3 +15,5 @@ query ServerWebSocket::ReceiveNode serverReceive() { any() }
|
||||
query predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(DataFlow::AdditionalFlowStep s).step(pred, succ)
|
||||
}
|
||||
|
||||
query RemoteFlowSource remoteFlow() { any() }
|
||||
|
||||
@@ -120,3 +120,13 @@ parseFloat("123", 10);
|
||||
throwerWithParam(42, 87); // NOT OK
|
||||
throwerIndirect(42); // OK, but still flagged due to complexity
|
||||
});
|
||||
|
||||
function sum2() {
|
||||
var result = 0;
|
||||
for (var i=0,n=sum2.arguments.length; i<n; ++i)
|
||||
result += sum2.arguments[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
// OK
|
||||
sum2(1, 2, 3);
|
||||
@@ -529,6 +529,7 @@ edges
|
||||
| tst.js:73:3:73:19 | document.location | tst.js:73:3:73:26 | documen ... .search |
|
||||
| tst.js:73:3:73:19 | document.location | tst.js:73:3:73:26 | documen ... .search |
|
||||
| tst.js:73:3:73:26 | documen ... .search | tst.js:73:1:73:27 | [,docum ... search] |
|
||||
| tst.js:73:3:73:26 | documen ... .search | tst.js:73:46:73:46 | x |
|
||||
| tst.js:73:46:73:46 | x | tst.js:76:20:76:20 | x |
|
||||
| tst.js:73:46:73:46 | x | tst.js:76:20:76:20 | x |
|
||||
| tst.js:80:49:80:65 | document.location | tst.js:80:49:80:72 | documen ... .search |
|
||||
|
||||
@@ -533,6 +533,7 @@ edges
|
||||
| tst.js:73:3:73:19 | document.location | tst.js:73:3:73:26 | documen ... .search |
|
||||
| tst.js:73:3:73:19 | document.location | tst.js:73:3:73:26 | documen ... .search |
|
||||
| tst.js:73:3:73:26 | documen ... .search | tst.js:73:1:73:27 | [,docum ... search] |
|
||||
| tst.js:73:3:73:26 | documen ... .search | tst.js:73:46:73:46 | x |
|
||||
| tst.js:73:46:73:46 | x | tst.js:76:20:76:20 | x |
|
||||
| tst.js:73:46:73:46 | x | tst.js:76:20:76:20 | x |
|
||||
| tst.js:80:49:80:65 | document.location | tst.js:80:49:80:72 | documen ... .search |
|
||||
|
||||
Reference in New Issue
Block a user