mirror of
https://github.com/github/codeql.git
synced 2026-06-05 21:47:10 +02:00
Compare commits
46 Commits
codeql-cli
...
codeml/aut
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fbc61d712 | ||
|
|
22902b07c9 | ||
|
|
6f0bc906d7 | ||
|
|
bb4b65654f | ||
|
|
7460719a60 | ||
|
|
8a374eba54 | ||
|
|
d6c06e3592 | ||
|
|
e9356d840d | ||
|
|
54fc0e12ba | ||
|
|
c60555d28a | ||
|
|
2c28d2402b | ||
|
|
d6d6ee9f9e | ||
|
|
beebbb2a36 | ||
|
|
96e3aa4188 | ||
|
|
4ce01be846 | ||
|
|
37c7c430bd | ||
|
|
3f17544235 | ||
|
|
4e1cb75610 | ||
|
|
f395cee944 | ||
|
|
88799b2692 | ||
|
|
f801a393f1 | ||
|
|
508358c8ba | ||
|
|
5196c49ed4 | ||
|
|
83d5b52a3d | ||
|
|
f6d3703561 | ||
|
|
d5dbdb122f | ||
|
|
6048f8fbf1 | ||
|
|
a511489e90 | ||
|
|
295a3f51e1 | ||
|
|
769236fc7f | ||
|
|
278fef93f2 | ||
|
|
d52082f41b | ||
|
|
44340a8ce4 | ||
|
|
827c55c612 | ||
|
|
6f28d39213 | ||
|
|
4f420c72d9 | ||
|
|
3c01011b51 | ||
|
|
1b32b53205 | ||
|
|
65eba5c01e | ||
|
|
5e6b17672d | ||
|
|
2e65873488 | ||
|
|
51ac3c270a | ||
|
|
88172e1347 | ||
|
|
826267ca9b | ||
|
|
a6f5487298 | ||
|
|
386672d4e0 |
@@ -16,102 +16,8 @@ private import FunctionBodyFeatures as FunctionBodyFeatures
|
||||
private string getTokenFeature(DataFlow::Node endpoint, string featureName) {
|
||||
// Performance optimization: Restrict feature extraction to endpoints we've explicitly asked to featurize.
|
||||
endpoint = any(FeaturizationConfig cfg).getAnEndpointToFeaturize() and
|
||||
(
|
||||
// Features for endpoints that are contained within a function.
|
||||
exists(Function function |
|
||||
function = FunctionBodyFeatures::getRepresentativeFunctionForEndpoint(endpoint)
|
||||
|
|
||||
// The name of the function that encloses the endpoint.
|
||||
featureName = "enclosingFunctionName" and result = FunctionNames::getNameToFeaturize(function)
|
||||
or
|
||||
// A feature containing natural language tokens from the function that encloses the endpoint in
|
||||
// the order that they appear in the source code.
|
||||
featureName = "enclosingFunctionBody" and
|
||||
result = FunctionBodyFeatures::getBodyTokensFeature(function)
|
||||
)
|
||||
or
|
||||
result =
|
||||
strictconcat(DataFlow::CallNode call, string component |
|
||||
component = getACallBasedTokenFeatureComponent(endpoint, call, featureName)
|
||||
|
|
||||
component, " "
|
||||
)
|
||||
or
|
||||
// The access path of the function being called, both with and without structural info, if the
|
||||
// function being called originates from an external API. For example, the endpoint here:
|
||||
//
|
||||
// ```js
|
||||
// const mongoose = require('mongoose'),
|
||||
// User = mongoose.model('User', null);
|
||||
// User.findOne(ENDPOINT);
|
||||
// ```
|
||||
//
|
||||
// would have a callee access path with structural info of
|
||||
// `mongoose member model instanceorreturn member findOne instanceorreturn`, and a callee access
|
||||
// path without structural info of `mongoose model findOne`.
|
||||
//
|
||||
// These features indicate that the callee comes from (reading the access path backwards) an
|
||||
// instance of the `findOne` member of an instance of the `model` member of the `mongoose`
|
||||
// external library.
|
||||
exists(AccessPaths::Boolean includeStructuralInfo |
|
||||
featureName =
|
||||
"calleeAccessPath" +
|
||||
any(string x | if includeStructuralInfo = true then x = "WithStructuralInfo" else x = "") and
|
||||
result =
|
||||
concat(API::Node node, string accessPath |
|
||||
node.getInducingNode().(DataFlow::CallNode).getAnArgument() = endpoint and
|
||||
AccessPaths::accessPaths(node, includeStructuralInfo, accessPath, _)
|
||||
|
|
||||
accessPath, " "
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value of the function-call-related token-based feature named `featureName` associated
|
||||
* with the function call `call` and the endpoint `endpoint`.
|
||||
*
|
||||
* This may in general report multiple strings, each containing a space-separated list of tokens.
|
||||
*
|
||||
* **Technical details:** This predicate can have multiple values per endpoint and feature name. As
|
||||
* a result, the results from this predicate must be concatenated together. However concatenating
|
||||
* other features like the function body tokens is expensive, so for performance reasons we separate
|
||||
* out this predicate from those other features.
|
||||
*/
|
||||
private string getACallBasedTokenFeatureComponent(
|
||||
DataFlow::Node endpoint, DataFlow::CallNode call, string featureName
|
||||
) {
|
||||
// Performance optimization: Restrict feature extraction to endpoints we've explicitly asked to featurize.
|
||||
endpoint = any(FeaturizationConfig cfg).getAnEndpointToFeaturize() and
|
||||
// Features for endpoints that are an argument to a function call.
|
||||
endpoint = call.getAnArgument() and
|
||||
(
|
||||
// The name of the function being called, e.g. in a call `Artist.findOne(...)`, this is `findOne`.
|
||||
featureName = "calleeName" and result = call.getCalleeName()
|
||||
or
|
||||
// The name of the receiver of the call, e.g. in a call `Artist.findOne(...)`, this is `Artist`.
|
||||
featureName = "receiverName" and result = call.getReceiver().asExpr().(VarRef).getName()
|
||||
or
|
||||
// The argument index of the endpoint, e.g. in `f(a, endpoint, b)`, this is 1.
|
||||
featureName = "argumentIndex" and
|
||||
result = any(int argIndex | call.getArgument(argIndex) = endpoint).toString()
|
||||
or
|
||||
// The name of the API that the function being called originates from, if the function being
|
||||
// called originates from an external API. For example, the endpoint here:
|
||||
//
|
||||
// ```js
|
||||
// const mongoose = require('mongoose'),
|
||||
// User = mongoose.model('User', null);
|
||||
// User.findOne(ENDPOINT);
|
||||
// ```
|
||||
//
|
||||
// would have a callee API name of `mongoose`.
|
||||
featureName = "calleeApiName" and
|
||||
exists(API::Node apiNode |
|
||||
AccessPaths::accessPaths(apiNode, false, _, result) and call = apiNode.getInducingNode()
|
||||
)
|
||||
)
|
||||
exists(EndpointFeature f | f.getName() = featureName and result = f.getValue(endpoint)) and
|
||||
featureName = getASupportedFeatureName()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +69,8 @@ private module AccessPaths {
|
||||
API::Node node, Boolean includeStructuralInfo, string accessPath, string apiName
|
||||
) {
|
||||
//node = API::moduleImport(result)
|
||||
node = API::moduleImport(apiName) and accessPath = apiName
|
||||
node = API::moduleImport(apiName) and
|
||||
accessPath = apiName
|
||||
or
|
||||
exists(API::Node previousNode, string previousAccessPath |
|
||||
previousNode.getDepth() < node.getDepth() and
|
||||
@@ -285,11 +192,8 @@ private module FunctionNames {
|
||||
|
||||
/** Get a name of a supported generic token-based feature. */
|
||||
string getASupportedFeatureName() {
|
||||
result =
|
||||
[
|
||||
"enclosingFunctionName", "calleeName", "receiverName", "argumentIndex", "calleeApiName",
|
||||
"calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody"
|
||||
]
|
||||
// allowlist of vetted features that are permitted in production
|
||||
result = any(EndpointFeature f).getName()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,3 +207,566 @@ predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string feat
|
||||
endpoint = any(FeaturizationConfig cfg).getAnEndpointToFeaturize() and
|
||||
featureValue = getTokenFeature(endpoint, featureName)
|
||||
}
|
||||
|
||||
/**
|
||||
* See EndpointFeature
|
||||
*/
|
||||
private newtype TEndpointFeature =
|
||||
TEnclosingFunctionName() or
|
||||
TReceiverName() or
|
||||
TEnclosingFunctionBody() or
|
||||
TFileImports() or
|
||||
TCalleeImports() or
|
||||
TCalleeFlexibleAccessPath() or
|
||||
TInputAccessPathFromCallee() or
|
||||
TInputArgumentIndex() or
|
||||
TContextFunctionInterfaces() or
|
||||
TContextSurroundingFunctionParameters() or
|
||||
TAssignedToPropName() or
|
||||
TStringConcatenatedWith()
|
||||
|
||||
/**
|
||||
* An implementation of an endpoint feature: produces feature names and values for use in ML.
|
||||
*/
|
||||
abstract class EndpointFeature extends TEndpointFeature {
|
||||
/**
|
||||
* Gets the name of the feature. Used by the ML model.
|
||||
* Changes to the name of a feature requires training the model again.
|
||||
*/
|
||||
abstract string getName();
|
||||
|
||||
/**
|
||||
* Gets the value of the feature. Used by the ML model.
|
||||
* Changes to the value of a feature requires training the model again.
|
||||
*/
|
||||
abstract string getValue(DataFlow::Node endpoint);
|
||||
|
||||
string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the name of the function that encloses the endpoint.
|
||||
*/
|
||||
class EnclosingFunctionName extends EndpointFeature, TEnclosingFunctionName {
|
||||
override string getName() { result = "enclosingFunctionName" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
result =
|
||||
FunctionNames::getNameToFeaturize(FunctionBodyFeatures::getRepresentativeFunctionForEndpoint(endpoint))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the name of the receiver of the call, e.g. in a call `Artist.findOne(...)`, this is `Artist`.
|
||||
*/
|
||||
class ReceiverName extends EndpointFeature, TReceiverName {
|
||||
override string getName() { result = "receiverName" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
result =
|
||||
strictconcat(DataFlow::CallNode call, string component |
|
||||
endpoint = call.getAnArgument() and
|
||||
component = call.getReceiver().asExpr().(VarRef).getName()
|
||||
|
|
||||
component, " "
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the natural language tokens from the function that encloses the endpoint in
|
||||
* the order that they appear in the source code.
|
||||
*/
|
||||
class EnclosingFunctionBody extends EndpointFeature, TEnclosingFunctionBody {
|
||||
override string getName() { result = "enclosingFunctionBody" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
endpoint = any(FeaturizationConfig cfg).getAnEndpointToFeaturize() and
|
||||
result =
|
||||
FunctionBodyFeatures::getBodyTokensFeature(FunctionBodyFeatures::getRepresentativeFunctionForEndpoint(endpoint))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the imports defined in the file containing an endpoint.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```javascript
|
||||
* import { findOne } from 'mongoose';
|
||||
* import * as _ from 'lodash';
|
||||
* const pg = require('pg');
|
||||
*
|
||||
* // ...
|
||||
* ```
|
||||
*
|
||||
* In this file, all endpoints will have the value `lodash mongoose pg` for the feature `fileImports`.
|
||||
*/
|
||||
class FileImports extends EndpointFeature, TFileImports {
|
||||
override string getName() { result = "fileImports" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
result = SyntacticUtilities::getImportPathsForFile(endpoint.getFile())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the function parameters of the functions that enclose an endpoint.
|
||||
*
|
||||
* ### Example
|
||||
* ```javascript
|
||||
* function f(a, b) {
|
||||
* // ...
|
||||
* const g = (c, d) => x.foo(endpoint);
|
||||
* // ^^^^^^^^
|
||||
* }
|
||||
* ```
|
||||
* In the above example, the feature for the marked endpoint has value '(a, b)\n(c, d)'.
|
||||
*/
|
||||
class ContextSurroundingFunctionParameters extends EndpointFeature,
|
||||
TContextSurroundingFunctionParameters {
|
||||
override string getName() { result = "contextSurroundingFunctionParameters" }
|
||||
|
||||
Function getRelevantFunction(DataFlow::Node endpoint) {
|
||||
result = endpoint.asExpr().getEnclosingFunction*()
|
||||
}
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
result =
|
||||
concat(string functionParameterLine, Function f |
|
||||
f = this.getRelevantFunction(endpoint) and
|
||||
functionParameterLine = SyntacticUtilities::getFunctionParametersFeatureComponent(f)
|
||||
|
|
||||
functionParameterLine, "\n"
|
||||
order by
|
||||
f.getLocation().getStartLine(), f.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature that gives the name an endpoint is assigned to (if any).
|
||||
*
|
||||
* ### Example
|
||||
* ```javascript
|
||||
* const div = document.createElement('div');
|
||||
* div.innerHTML = endpoint; // feature value is 'innerHTML'
|
||||
* ```
|
||||
*/
|
||||
class AssignedToPropName extends EndpointFeature, TAssignedToPropName {
|
||||
override string getName() { result = "assignedToPropName" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
exists(DataFlow::PropWrite w | w.getRhs().asExpr().getUnderlyingValue().flow() = endpoint |
|
||||
result = w.getPropertyName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature that shows the text an endpoint is being concatenated with.class
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```javascript
|
||||
* const x = 'foo' + endpoint + 'bar'; // feature value is `'foo' -endpoint- 'bar'`
|
||||
*/
|
||||
class StringConcatenatedWith extends EndpointFeature, TStringConcatenatedWith {
|
||||
override string getName() { result = "stringConcatenatedWith" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
exists(StringOps::ConcatenationRoot root |
|
||||
root.getALeaf() = endpoint and
|
||||
result =
|
||||
concat(StringOps::ConcatenationLeaf p |
|
||||
p.getRoot() = root and
|
||||
(
|
||||
p.getStartLine() < endpoint.getStartLine()
|
||||
or
|
||||
p.getStartLine() = endpoint.getStartLine() and
|
||||
p.getStartColumn() < endpoint.getStartColumn()
|
||||
)
|
||||
|
|
||||
SyntacticUtilities::renderStringConcatOperand(p), " + "
|
||||
order by
|
||||
p.getStartLine(), p.getStartColumn()
|
||||
) + " -endpoint- " +
|
||||
concat(StringOps::ConcatenationLeaf p |
|
||||
p.getRoot() = root and
|
||||
(
|
||||
p.getStartLine() > endpoint.getStartLine()
|
||||
or
|
||||
p.getStartLine() = endpoint.getStartLine() and
|
||||
p.getStartColumn() > endpoint.getStartColumn()
|
||||
)
|
||||
|
|
||||
SyntacticUtilities::renderStringConcatOperand(p), " + "
|
||||
order by
|
||||
p.getStartLine(), p.getStartColumn()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the imports used in the callee of an invocation.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```javascript
|
||||
* import * as _ from 'lodash';
|
||||
*
|
||||
* // ...
|
||||
* _.deepClone(someObject);
|
||||
* // ^^^^^^^^^^ will have the value `lodash` for the feature `calleeImports`.
|
||||
* ```
|
||||
*/
|
||||
class CalleeImports extends EndpointFeature, TCalleeImports {
|
||||
override string getName() { result = "calleeImports" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
not result = SyntacticUtilities::getUnknownSymbol() and
|
||||
exists(DataFlow::InvokeNode invk |
|
||||
(
|
||||
invk.getAnArgument() = endpoint or
|
||||
SyntacticUtilities::getANestedInitializerValue(invk.getAnArgument()
|
||||
.asExpr()
|
||||
.getUnderlyingValue()).flow() = endpoint
|
||||
) and
|
||||
result =
|
||||
concat(string importPath |
|
||||
importPath = SyntacticUtilities::getCalleeImportPath(invk.getCalleeNode())
|
||||
|
|
||||
importPath, " " order by importPath
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the interfaces of all named functions in the same file as the endpoint.
|
||||
*
|
||||
* ### Example
|
||||
* ```javascript
|
||||
* // Will return: "f(a, b, c)\ng(x, y, z)\nh(u, v)" for this file.
|
||||
* function f(a, b, c) { ... }
|
||||
*
|
||||
* function g(x, y, z) {
|
||||
* function h(u, v) { ... }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The feature value for the marked endpoint will be `f(a, b, c)\ng(x, y, z)\nh(u, v)`.
|
||||
*/
|
||||
class ContextFunctionInterfaces extends EndpointFeature, TContextFunctionInterfaces {
|
||||
override string getName() { result = "contextFunctionInterfaces" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
result = SyntacticUtilities::getFunctionInterfacesForFile(endpoint.getFile())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntactic utilities for feature value computation.
|
||||
*/
|
||||
private module SyntacticUtilities {
|
||||
string renderStringConcatOperand(DataFlow::Node operand) {
|
||||
if exists(unique(string v | operand.mayHaveStringValue(v)))
|
||||
then result = "'" + any(string v | operand.mayHaveStringValue(v)) + "'"
|
||||
else result = getSimpleAccessPath(operand)
|
||||
}
|
||||
|
||||
/** Gets all the imports defined in the file containing the endpoint. */
|
||||
string getImportPathsForFile(File file) {
|
||||
result =
|
||||
concat(string importPath |
|
||||
importPath = SyntacticUtilities::getImportPathForFile(file)
|
||||
|
|
||||
importPath, " " order by importPath
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an import located in `file`. */
|
||||
string getImportPathForFile(File file) {
|
||||
result = any(Import imp | imp.getFile() = file).getImportedPath().getValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the feature component for the parameters of a function.
|
||||
*
|
||||
* ```javascript
|
||||
* function f(a, b, c) { // will return "(a, b, c)" for this function
|
||||
* return a + b + c;
|
||||
* }
|
||||
*
|
||||
* async function g(a) { // will return "(a)" for this function
|
||||
* return 2*a
|
||||
* };
|
||||
*
|
||||
* const h = (b) => 3*b; // will return "(b)" for this function
|
||||
* ```
|
||||
*/
|
||||
string getFunctionParametersFeatureComponent(Function f) {
|
||||
result =
|
||||
"(" +
|
||||
concat(string parameter, int i |
|
||||
parameter = getParameterNameOrUnknown(f.getParameter(i))
|
||||
|
|
||||
parameter, ", " order by i
|
||||
) + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function interfaces of all named functions in a file, concatenated together.
|
||||
*
|
||||
* ```javascript
|
||||
* // Will return: "f(a, b, c)\ng(x, y, z)\nh(u, v)" for this file.
|
||||
* function f(a, b, c) { ... }
|
||||
*
|
||||
* function g(x, y, z) {
|
||||
* function h(u, v) { ... }
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
string getFunctionInterfacesForFile(File file) {
|
||||
result =
|
||||
concat(Function func, string line |
|
||||
func.getFile() = file and
|
||||
line = func.getName() + getFunctionParametersFeatureComponent(func)
|
||||
|
|
||||
line, "\n" order by line
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property initializer value in an object literal or one of its nested object literals.
|
||||
*/
|
||||
Expr getANestedInitializerValue(ObjectExpr o) {
|
||||
exists(Expr init | init = o.getAProperty().getInit().getUnderlyingValue() |
|
||||
result = [init, getANestedInitializerValue(init)]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a simple access path for how a callee can refer to a value that appears in an argument to a call.
|
||||
*
|
||||
* Supports:
|
||||
* - direct arguments
|
||||
* - properties of (nested) objects that are arguments
|
||||
*
|
||||
* Unknown cases and property names results in `?`.
|
||||
*/
|
||||
string getSimpleParameterAccessPath(DataFlow::Node node) {
|
||||
if exists(DataFlow::CallNode call | node = call.getArgument(_))
|
||||
then exists(DataFlow::CallNode call, int i | node = call.getArgument(i) | result = i + "")
|
||||
else result = getSimplePropertyAccessPath(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a simple access path for how a user can refer to a value that appears in an (nested) object.
|
||||
*
|
||||
* Supports:
|
||||
* - properties of (nested) objects
|
||||
*
|
||||
* Unknown cases and property names results in `?`.
|
||||
*/
|
||||
string getSimplePropertyAccessPath(DataFlow::Node node) {
|
||||
if exists(ObjectExpr o | o.getAProperty().getInit().getUnderlyingValue() = node.asExpr())
|
||||
then
|
||||
exists(DataFlow::PropWrite w |
|
||||
w.getRhs() = node and
|
||||
result = getSimpleParameterAccessPath(w.getBase()) + "." + getPropertyNameOrUnknown(w)
|
||||
)
|
||||
else result = getUnknownSymbol()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the imported package path that this node depends on, if any.
|
||||
*
|
||||
* Otherwise, returns '?'.
|
||||
*
|
||||
* XXX Be careful with using this in your features, as it might teach the model
|
||||
* a fixed list of "dangerous" libraries that could lead to bad generalization.
|
||||
*/
|
||||
string getCalleeImportPath(DataFlow::Node node) {
|
||||
exists(DataFlow::Node src | src = node.getALocalSource() |
|
||||
if src instanceof DataFlow::ModuleImportNode
|
||||
then result = src.(DataFlow::ModuleImportNode).getPath()
|
||||
else
|
||||
if src instanceof DataFlow::PropRead
|
||||
then result = getCalleeImportPath(src.(DataFlow::PropRead).getBase())
|
||||
else
|
||||
if src instanceof DataFlow::InvokeNode
|
||||
then result = getCalleeImportPath(src.(DataFlow::InvokeNode).getCalleeNode())
|
||||
else
|
||||
if src.asExpr() instanceof AwaitExpr
|
||||
then result = getCalleeImportPath(src.asExpr().(AwaitExpr).getOperand().flow())
|
||||
else result = getUnknownSymbol()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a simple access path for a node.
|
||||
*
|
||||
* Supports:
|
||||
* - variable reads (including `this` and `super`)
|
||||
* - imports
|
||||
* - await
|
||||
* - property reads
|
||||
* - invocations
|
||||
*
|
||||
* Unknown cases and property names results in `?`.
|
||||
*/
|
||||
string getSimpleAccessPath(DataFlow::Node node) {
|
||||
exists(Expr e | e = node.asExpr().getUnderlyingValue() |
|
||||
if e instanceof SuperAccess
|
||||
then result = "super"
|
||||
else
|
||||
if e instanceof ThisAccess
|
||||
then result = "this"
|
||||
else
|
||||
if e instanceof VarAccess
|
||||
then result = e.(VarAccess).getName()
|
||||
else
|
||||
if e instanceof Import
|
||||
then result = "import(" + getSimpleImportPath(e) + ")"
|
||||
else
|
||||
if e instanceof AwaitExpr
|
||||
then result = "(await " + getSimpleAccessPath(e.(AwaitExpr).getOperand().flow()) + ")"
|
||||
else
|
||||
if node instanceof DataFlow::PropRead
|
||||
then
|
||||
result =
|
||||
getSimpleAccessPath(node.(DataFlow::PropRead).getBase()) + "." +
|
||||
getPropertyNameOrUnknown(node)
|
||||
else
|
||||
if node instanceof DataFlow::InvokeNode
|
||||
then
|
||||
result = getSimpleAccessPath(node.(DataFlow::InvokeNode).getCalleeNode()) + "()"
|
||||
else result = getUnknownSymbol()
|
||||
)
|
||||
}
|
||||
|
||||
string getUnknownSymbol() { result = "?" }
|
||||
|
||||
/**
|
||||
* Gets the imported path.
|
||||
*
|
||||
* XXX To avoid teaching the ML model about npm packages, only relative paths are supported
|
||||
*
|
||||
* Unknown paths result in `?`.
|
||||
*/
|
||||
string getSimpleImportPath(Import i) {
|
||||
if exists(i.getImportedPath().getValue())
|
||||
then
|
||||
exists(string p | p = i.getImportedPath().getValue() |
|
||||
if p.matches(".%") then result = "\"p\"" else result = "!" // hide absolute imports from the ML training
|
||||
)
|
||||
else result = getUnknownSymbol()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property name of a property reference or `?` if it is unknown.
|
||||
*/
|
||||
string getPropertyNameOrUnknown(DataFlow::PropRef ref) {
|
||||
if exists(ref.getPropertyName())
|
||||
then result = ref.getPropertyName()
|
||||
else result = getUnknownSymbol()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter name if it exists, or `?` if it is unknown.
|
||||
*/
|
||||
string getParameterNameOrUnknown(Parameter p) {
|
||||
if exists(p.getName()) then result = p.getName() else result = getUnknownSymbol()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for the access path of the callee node of a call that has an argument that "contains" the endpoint.
|
||||
*
|
||||
* "Containment" is syntactic, and currently means that the endpoint is an argument to the call, or that the endpoint is a (nested) property value of an argument.
|
||||
*
|
||||
* This feature is intended as a superior version of the many `Callee*` features.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* foo(endpoint); // -> foo
|
||||
* foo.bar(endpoint); // -> foo.bar
|
||||
* foo.bar({ baz: endpoint }); // -> foo.bar
|
||||
* this.foo.bar(endpoint); // -> this.foo.bar
|
||||
* foo[complex()].bar(endpoint); // -> foo.?.bar
|
||||
* ```
|
||||
*/
|
||||
class CalleeFlexibleAccessPath extends EndpointFeature, TCalleeFlexibleAccessPath {
|
||||
override string getName() { result = "CalleeFlexibleAccessPath" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
exists(DataFlow::InvokeNode invk |
|
||||
result = SyntacticUtilities::getSimpleAccessPath(invk.getCalleeNode()) and
|
||||
// ignore the unknown path
|
||||
not result = SyntacticUtilities::getUnknownSymbol() and
|
||||
(
|
||||
invk.getAnArgument() = endpoint or
|
||||
SyntacticUtilities::getANestedInitializerValue(invk.getAnArgument()
|
||||
.asExpr()
|
||||
.getUnderlyingValue()).flow() = endpoint
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for how a callee can refer to a the endpoint that is "contained" in some argument to a call
|
||||
*
|
||||
* "Containment" is syntactic, and currently means that the endpoint is an argument to the call, or that the endpoint is a (nested) property value of an argument.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* foo({ bar: endpoint }); // -> bar
|
||||
* foo(x, { bar: { baz: endpoint } }); // -> bar.baz
|
||||
* ```
|
||||
*/
|
||||
class InputAccessPathFromCallee extends EndpointFeature, TInputAccessPathFromCallee {
|
||||
override string getName() { result = "InputAccessPathFromCallee" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
exists(DataFlow::InvokeNode invk |
|
||||
result = SyntacticUtilities::getSimpleParameterAccessPath(endpoint) and
|
||||
SyntacticUtilities::getANestedInitializerValue(invk.getAnArgument()
|
||||
.asExpr()
|
||||
.getUnderlyingValue()).flow() = endpoint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The feature for how the index of an argument that "contains" and endpoint.
|
||||
*
|
||||
* "Containment" is syntactic, and currently means that the endpoint is an argument to the call, or that the endpoint is a (nested) property value of an argument.
|
||||
*
|
||||
* This feature is intended as a superior version of the `ArgumentIndexFeature`.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* foo(endpoint); // -> 0
|
||||
* foo({ bar: endpoint }); // -> 0
|
||||
* foo(x, { bar: { baz: endpoint } }); // -> 1
|
||||
* ```
|
||||
*/
|
||||
class InputArgumentIndex extends EndpointFeature, TInputArgumentIndex {
|
||||
override string getName() { result = "InputArgumentIndex" }
|
||||
|
||||
override string getValue(DataFlow::Node endpoint) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::Node arg, int i | arg = invk.getArgument(i) |
|
||||
result = i + "" and
|
||||
(
|
||||
invk.getArgument(i) = endpoint
|
||||
or
|
||||
SyntacticUtilities::getANestedInitializerValue(arg.asExpr().getUnderlyingValue()).flow() =
|
||||
endpoint
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -296,179 +296,267 @@ endpoints
|
||||
| index.js:84:12:84:18 | foo.bar | Xss | notASinkReason | ClientRequest | string |
|
||||
| index.js:84:12:84:18 | foo.bar | Xss | sinkLabel | NotASink | string |
|
||||
tokenFeatures
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | argumentIndex | 0 |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | calleeAccessPath | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | calleeAccessPathWithStructuralInfo | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | calleeApiName | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | calleeName | log |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | CalleeFlexibleAccessPath | console.log |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | InputAccessPathFromCallee | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | InputArgumentIndex | 0 |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | assignedToPropName | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | calleeImports | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | contextFunctionInterfaces | should_be_ignored() |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | contextSurroundingFunctionParameters | () |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | enclosingFunctionBody | console log Should be ignored |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | enclosingFunctionName | should_be_ignored |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | fileImports | |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | receiverName | console |
|
||||
| index.js:1:25:1:33 | "express" | argumentIndex | 0 |
|
||||
| index.js:1:25:1:33 | "express" | calleeAccessPath | |
|
||||
| index.js:1:25:1:33 | "express" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:1:25:1:33 | "express" | calleeApiName | |
|
||||
| index.js:1:25:1:33 | "express" | calleeName | require |
|
||||
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | stringConcatenatedWith | |
|
||||
| index.js:1:25:1:33 | "express" | CalleeFlexibleAccessPath | require |
|
||||
| index.js:1:25:1:33 | "express" | InputAccessPathFromCallee | |
|
||||
| index.js:1:25:1:33 | "express" | InputArgumentIndex | 0 |
|
||||
| index.js:1:25:1:33 | "express" | assignedToPropName | |
|
||||
| index.js:1:25:1:33 | "express" | calleeImports | |
|
||||
| index.js:1:25:1:33 | "express" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:1:25:1:33 | "express" | contextSurroundingFunctionParameters | |
|
||||
| index.js:1:25:1:33 | "express" | enclosingFunctionBody | |
|
||||
| index.js:1:25:1:33 | "express" | enclosingFunctionName | |
|
||||
| index.js:1:25:1:33 | "express" | fileImports | express mongoose |
|
||||
| index.js:1:25:1:33 | "express" | receiverName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | argumentIndex | 0 |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeAccessPath | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeApiName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeName | require |
|
||||
| index.js:1:25:1:33 | "express" | stringConcatenatedWith | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | CalleeFlexibleAccessPath | require |
|
||||
| index.js:2:26:2:35 | 'mongoose' | InputAccessPathFromCallee | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | InputArgumentIndex | 0 |
|
||||
| index.js:2:26:2:35 | 'mongoose' | assignedToPropName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeImports | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:2:26:2:35 | 'mongoose' | contextSurroundingFunctionParameters | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | enclosingFunctionBody | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | enclosingFunctionName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | fileImports | express mongoose |
|
||||
| index.js:2:26:2:35 | 'mongoose' | receiverName | |
|
||||
| index.js:3:29:3:34 | 'User' | argumentIndex | 0 |
|
||||
| index.js:3:29:3:34 | 'User' | calleeAccessPath | mongoose model |
|
||||
| index.js:3:29:3:34 | 'User' | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn |
|
||||
| index.js:3:29:3:34 | 'User' | calleeApiName | mongoose |
|
||||
| index.js:3:29:3:34 | 'User' | calleeName | model |
|
||||
| index.js:2:26:2:35 | 'mongoose' | stringConcatenatedWith | |
|
||||
| index.js:3:29:3:34 | 'User' | CalleeFlexibleAccessPath | mongoose.model |
|
||||
| index.js:3:29:3:34 | 'User' | InputAccessPathFromCallee | |
|
||||
| index.js:3:29:3:34 | 'User' | InputArgumentIndex | 0 |
|
||||
| index.js:3:29:3:34 | 'User' | assignedToPropName | |
|
||||
| index.js:3:29:3:34 | 'User' | calleeImports | mongoose |
|
||||
| index.js:3:29:3:34 | 'User' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:3:29:3:34 | 'User' | contextSurroundingFunctionParameters | |
|
||||
| index.js:3:29:3:34 | 'User' | enclosingFunctionBody | |
|
||||
| index.js:3:29:3:34 | 'User' | enclosingFunctionName | |
|
||||
| index.js:3:29:3:34 | 'User' | fileImports | express mongoose |
|
||||
| index.js:3:29:3:34 | 'User' | receiverName | mongoose |
|
||||
| index.js:3:37:3:40 | null | argumentIndex | 1 |
|
||||
| index.js:3:37:3:40 | null | calleeAccessPath | mongoose model |
|
||||
| index.js:3:37:3:40 | null | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn |
|
||||
| index.js:3:37:3:40 | null | calleeApiName | mongoose |
|
||||
| index.js:3:37:3:40 | null | calleeName | model |
|
||||
| index.js:3:29:3:34 | 'User' | stringConcatenatedWith | |
|
||||
| index.js:3:37:3:40 | null | CalleeFlexibleAccessPath | mongoose.model |
|
||||
| index.js:3:37:3:40 | null | InputAccessPathFromCallee | |
|
||||
| index.js:3:37:3:40 | null | InputArgumentIndex | 1 |
|
||||
| index.js:3:37:3:40 | null | assignedToPropName | |
|
||||
| index.js:3:37:3:40 | null | calleeImports | mongoose |
|
||||
| index.js:3:37:3:40 | null | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:3:37:3:40 | null | contextSurroundingFunctionParameters | |
|
||||
| index.js:3:37:3:40 | null | enclosingFunctionBody | |
|
||||
| index.js:3:37:3:40 | null | enclosingFunctionName | |
|
||||
| index.js:3:37:3:40 | null | fileImports | express mongoose |
|
||||
| index.js:3:37:3:40 | null | receiverName | mongoose |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | argumentIndex | 0 |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeAccessPath | express post |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeApiName | express |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeName | post |
|
||||
| index.js:3:37:3:40 | null | stringConcatenatedWith | |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | InputAccessPathFromCallee | |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | InputArgumentIndex | 0 |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | assignedToPropName | |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeImports | express |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | contextSurroundingFunctionParameters | () |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | fileImports | express mongoose |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | receiverName | app |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | argumentIndex | 1 |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeAccessPath | express post |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeApiName | express |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeName | post |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | stringConcatenatedWith | |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | InputArgumentIndex | 1 |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | assignedToPropName | |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeImports | express |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | fileImports | express mongoose |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | receiverName | app |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | argumentIndex | 0 |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPath | mongoose model find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeApiName | mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeName | find |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | stringConcatenatedWith | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | InputAccessPathFromCallee | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | InputArgumentIndex | 0 |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | assignedToPropName | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeImports | mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | fileImports | express mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | receiverName | User |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | argumentIndex | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeAccessPath | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeApiName | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeName | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | stringConcatenatedWith | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | InputAccessPathFromCallee | 0.isAdmin |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | InputArgumentIndex | 0 |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | assignedToPropName | isAdmin |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeImports | mongoose |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | fileImports | express mongoose |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | receiverName | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | argumentIndex | 0 |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeAccessPath | express post |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeApiName | express |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeName | post |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | stringConcatenatedWith | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | InputAccessPathFromCallee | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | InputArgumentIndex | 0 |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | assignedToPropName | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeImports | express |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | contextSurroundingFunctionParameters | () |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | fileImports | express mongoose |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | receiverName | app |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | argumentIndex | 1 |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeAccessPath | express post |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeApiName | express |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeName | post |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | stringConcatenatedWith | |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | InputArgumentIndex | 1 |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | assignedToPropName | |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeImports | express |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | fileImports | express mongoose |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | receiverName | app |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | argumentIndex | 0 |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeAccessPath | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeApiName | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeName | log |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | stringConcatenatedWith | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | InputAccessPathFromCallee | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | InputArgumentIndex | 0 |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | assignedToPropName | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeImports | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | fileImports | express mongoose |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | receiverName | console |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | argumentIndex | 0 |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeAccessPath | mongoose model find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeApiName | mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeName | find |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | stringConcatenatedWith | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | InputAccessPathFromCallee | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | InputArgumentIndex | 0 |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | assignedToPropName | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeImports | mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | contextSurroundingFunctionParameters | () |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | enclosingFunctionBody | User find isAdmin true |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | enclosingFunctionName | notFlowFromSource |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | fileImports | express mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | receiverName | User |
|
||||
| index.js:20:26:20:29 | true | argumentIndex | |
|
||||
| index.js:20:26:20:29 | true | calleeAccessPath | |
|
||||
| index.js:20:26:20:29 | true | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:20:26:20:29 | true | calleeApiName | |
|
||||
| index.js:20:26:20:29 | true | calleeName | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | stringConcatenatedWith | |
|
||||
| index.js:20:26:20:29 | true | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:20:26:20:29 | true | InputAccessPathFromCallee | 0.isAdmin |
|
||||
| index.js:20:26:20:29 | true | InputArgumentIndex | 0 |
|
||||
| index.js:20:26:20:29 | true | assignedToPropName | isAdmin |
|
||||
| index.js:20:26:20:29 | true | calleeImports | mongoose |
|
||||
| index.js:20:26:20:29 | true | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:20:26:20:29 | true | contextSurroundingFunctionParameters | () |
|
||||
| index.js:20:26:20:29 | true | enclosingFunctionBody | User find isAdmin true |
|
||||
| index.js:20:26:20:29 | true | enclosingFunctionName | notFlowFromSource |
|
||||
| index.js:20:26:20:29 | true | fileImports | express mongoose |
|
||||
| index.js:20:26:20:29 | true | receiverName | |
|
||||
| index.js:24:13:24:22 | "constant" | argumentIndex | 0 |
|
||||
| index.js:24:13:24:22 | "constant" | calleeAccessPath | mongoose model find |
|
||||
| index.js:24:13:24:22 | "constant" | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:24:13:24:22 | "constant" | calleeApiName | mongoose |
|
||||
| index.js:24:13:24:22 | "constant" | calleeName | find |
|
||||
| index.js:20:26:20:29 | true | stringConcatenatedWith | |
|
||||
| index.js:24:13:24:22 | "constant" | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:24:13:24:22 | "constant" | InputAccessPathFromCallee | |
|
||||
| index.js:24:13:24:22 | "constant" | InputArgumentIndex | 0 |
|
||||
| index.js:24:13:24:22 | "constant" | assignedToPropName | |
|
||||
| index.js:24:13:24:22 | "constant" | calleeImports | mongoose |
|
||||
| index.js:24:13:24:22 | "constant" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:24:13:24:22 | "constant" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:24:13:24:22 | "constant" | enclosingFunctionBody | User find constant |
|
||||
| index.js:24:13:24:22 | "constant" | enclosingFunctionName | constantExpression |
|
||||
| index.js:24:13:24:22 | "constant" | fileImports | express mongoose |
|
||||
| index.js:24:13:24:22 | "constant" | receiverName | User |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | argumentIndex | 0 |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeAccessPath | mongoose model find |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeApiName | mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeName | find |
|
||||
| index.js:24:13:24:22 | "constant" | stringConcatenatedWith | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | InputAccessPathFromCallee | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | InputArgumentIndex | 0 |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | assignedToPropName | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeImports | mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | contextSurroundingFunctionParameters | () |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionBody | User find UNDEFINED_GLOBAL |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionName | notConstantExpression |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | fileImports | express mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | receiverName | User |
|
||||
| index.js:32:15:32:24 | "someData" | argumentIndex | 0 |
|
||||
| index.js:32:15:32:24 | "someData" | calleeAccessPath | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeApiName | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeName | log |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | stringConcatenatedWith | |
|
||||
| index.js:32:15:32:24 | "someData" | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:32:15:32:24 | "someData" | InputAccessPathFromCallee | |
|
||||
| index.js:32:15:32:24 | "someData" | InputArgumentIndex | 0 |
|
||||
| index.js:32:15:32:24 | "someData" | assignedToPropName | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeImports | |
|
||||
| index.js:32:15:32:24 | "someData" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:32:15:32:24 | "someData" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:32:15:32:24 | "someData" | enclosingFunctionBody | console log someData |
|
||||
| index.js:32:15:32:24 | "someData" | enclosingFunctionName | notASink |
|
||||
| index.js:32:15:32:24 | "someData" | fileImports | express mongoose |
|
||||
| index.js:32:15:32:24 | "someData" | receiverName | console |
|
||||
| index.js:36:20:36:22 | "a" | argumentIndex | 0 |
|
||||
| index.js:36:20:36:22 | "a" | calleeAccessPath | |
|
||||
| index.js:36:20:36:22 | "a" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:36:20:36:22 | "a" | calleeApiName | |
|
||||
| index.js:36:20:36:22 | "a" | calleeName | startsWith |
|
||||
| index.js:32:15:32:24 | "someData" | stringConcatenatedWith | |
|
||||
| index.js:36:20:36:22 | "a" | CalleeFlexibleAccessPath | ?.startsWith |
|
||||
| index.js:36:20:36:22 | "a" | InputAccessPathFromCallee | |
|
||||
| index.js:36:20:36:22 | "a" | InputArgumentIndex | 0 |
|
||||
| index.js:36:20:36:22 | "a" | assignedToPropName | |
|
||||
| index.js:36:20:36:22 | "a" | calleeImports | |
|
||||
| index.js:36:20:36:22 | "a" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:36:20:36:22 | "a" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:36:20:36:22 | "a" | enclosingFunctionBody | abc startsWith a |
|
||||
| index.js:36:20:36:22 | "a" | enclosingFunctionName | notASinkMultipleReasons |
|
||||
| index.js:36:20:36:22 | "a" | fileImports | express mongoose |
|
||||
| index.js:36:20:36:22 | "a" | receiverName | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | argumentIndex | 0 |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeAccessPath | mongoose model find |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeApiName | mongoose |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeName | find |
|
||||
| index.js:36:20:36:22 | "a" | stringConcatenatedWith | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | InputAccessPathFromCallee | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | InputArgumentIndex | 0 |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | assignedToPropName | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeImports | mongoose |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | enclosingFunctionBody | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | enclosingFunctionName | veryLongFunctionBody |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | fileImports | express mongoose |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | receiverName | User |
|
||||
| index.js:78:30:78:39 | "someData" | argumentIndex | 0 |
|
||||
| index.js:78:30:78:39 | "someData" | calleeAccessPath | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeApiName | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeName | log |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | stringConcatenatedWith | |
|
||||
| index.js:78:30:78:39 | "someData" | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:78:30:78:39 | "someData" | InputAccessPathFromCallee | |
|
||||
| index.js:78:30:78:39 | "someData" | InputArgumentIndex | 0 |
|
||||
| index.js:78:30:78:39 | "someData" | assignedToPropName | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeImports | |
|
||||
| index.js:78:30:78:39 | "someData" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:78:30:78:39 | "someData" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:78:30:78:39 | "someData" | enclosingFunctionBody | console log someData |
|
||||
| index.js:78:30:78:39 | "someData" | enclosingFunctionName | identity#functionalargument |
|
||||
| index.js:78:30:78:39 | "someData" | fileImports | express mongoose |
|
||||
| index.js:78:30:78:39 | "someData" | receiverName | console |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | argumentIndex | 0 |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPath | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeApiName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeName | ajax |
|
||||
| index.js:78:30:78:39 | "someData" | stringConcatenatedWith | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | CalleeFlexibleAccessPath | $.ajax |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | InputArgumentIndex | 0 |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | assignedToPropName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeImports | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | contextSurroundingFunctionParameters | (foo) |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionBody | foo $ ajax url foo bar |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionName | effectiveSinkAndNotASink |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | fileImports | express mongoose |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | receiverName | $ |
|
||||
| index.js:84:12:84:18 | foo.bar | argumentIndex | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeAccessPath | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeApiName | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | stringConcatenatedWith | |
|
||||
| index.js:84:12:84:18 | foo.bar | CalleeFlexibleAccessPath | $.ajax |
|
||||
| index.js:84:12:84:18 | foo.bar | InputAccessPathFromCallee | 0.url |
|
||||
| index.js:84:12:84:18 | foo.bar | InputArgumentIndex | 0 |
|
||||
| index.js:84:12:84:18 | foo.bar | assignedToPropName | url |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeImports | |
|
||||
| index.js:84:12:84:18 | foo.bar | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:84:12:84:18 | foo.bar | contextSurroundingFunctionParameters | (foo) |
|
||||
| index.js:84:12:84:18 | foo.bar | enclosingFunctionBody | foo $ ajax url foo bar |
|
||||
| index.js:84:12:84:18 | foo.bar | enclosingFunctionName | effectiveSinkAndNotASink |
|
||||
| index.js:84:12:84:18 | foo.bar | fileImports | express mongoose |
|
||||
| index.js:84:12:84:18 | foo.bar | receiverName | |
|
||||
| index.js:84:12:84:18 | foo.bar | stringConcatenatedWith | |
|
||||
|
||||
@@ -276,171 +276,255 @@ endpoints
|
||||
| index.js:84:12:84:18 | foo.bar | Xss | notASinkReason | ClientRequest | string |
|
||||
| index.js:84:12:84:18 | foo.bar | Xss | sinkLabel | NotASink | string |
|
||||
tokenFeatures
|
||||
| index.js:1:25:1:33 | "express" | argumentIndex | 0 |
|
||||
| index.js:1:25:1:33 | "express" | calleeAccessPath | |
|
||||
| index.js:1:25:1:33 | "express" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:1:25:1:33 | "express" | calleeApiName | |
|
||||
| index.js:1:25:1:33 | "express" | calleeName | require |
|
||||
| index.js:1:25:1:33 | "express" | CalleeFlexibleAccessPath | require |
|
||||
| index.js:1:25:1:33 | "express" | InputAccessPathFromCallee | |
|
||||
| index.js:1:25:1:33 | "express" | InputArgumentIndex | 0 |
|
||||
| index.js:1:25:1:33 | "express" | assignedToPropName | |
|
||||
| index.js:1:25:1:33 | "express" | calleeImports | |
|
||||
| index.js:1:25:1:33 | "express" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:1:25:1:33 | "express" | contextSurroundingFunctionParameters | |
|
||||
| index.js:1:25:1:33 | "express" | enclosingFunctionBody | |
|
||||
| index.js:1:25:1:33 | "express" | enclosingFunctionName | |
|
||||
| index.js:1:25:1:33 | "express" | fileImports | express mongoose |
|
||||
| index.js:1:25:1:33 | "express" | receiverName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | argumentIndex | 0 |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeAccessPath | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeApiName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeName | require |
|
||||
| index.js:1:25:1:33 | "express" | stringConcatenatedWith | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | CalleeFlexibleAccessPath | require |
|
||||
| index.js:2:26:2:35 | 'mongoose' | InputAccessPathFromCallee | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | InputArgumentIndex | 0 |
|
||||
| index.js:2:26:2:35 | 'mongoose' | assignedToPropName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | calleeImports | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:2:26:2:35 | 'mongoose' | contextSurroundingFunctionParameters | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | enclosingFunctionBody | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | enclosingFunctionName | |
|
||||
| index.js:2:26:2:35 | 'mongoose' | fileImports | express mongoose |
|
||||
| index.js:2:26:2:35 | 'mongoose' | receiverName | |
|
||||
| index.js:3:29:3:34 | 'User' | argumentIndex | 0 |
|
||||
| index.js:3:29:3:34 | 'User' | calleeAccessPath | mongoose model |
|
||||
| index.js:3:29:3:34 | 'User' | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn |
|
||||
| index.js:3:29:3:34 | 'User' | calleeApiName | mongoose |
|
||||
| index.js:3:29:3:34 | 'User' | calleeName | model |
|
||||
| index.js:2:26:2:35 | 'mongoose' | stringConcatenatedWith | |
|
||||
| index.js:3:29:3:34 | 'User' | CalleeFlexibleAccessPath | mongoose.model |
|
||||
| index.js:3:29:3:34 | 'User' | InputAccessPathFromCallee | |
|
||||
| index.js:3:29:3:34 | 'User' | InputArgumentIndex | 0 |
|
||||
| index.js:3:29:3:34 | 'User' | assignedToPropName | |
|
||||
| index.js:3:29:3:34 | 'User' | calleeImports | mongoose |
|
||||
| index.js:3:29:3:34 | 'User' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:3:29:3:34 | 'User' | contextSurroundingFunctionParameters | |
|
||||
| index.js:3:29:3:34 | 'User' | enclosingFunctionBody | |
|
||||
| index.js:3:29:3:34 | 'User' | enclosingFunctionName | |
|
||||
| index.js:3:29:3:34 | 'User' | fileImports | express mongoose |
|
||||
| index.js:3:29:3:34 | 'User' | receiverName | mongoose |
|
||||
| index.js:3:37:3:40 | null | argumentIndex | 1 |
|
||||
| index.js:3:37:3:40 | null | calleeAccessPath | mongoose model |
|
||||
| index.js:3:37:3:40 | null | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn |
|
||||
| index.js:3:37:3:40 | null | calleeApiName | mongoose |
|
||||
| index.js:3:37:3:40 | null | calleeName | model |
|
||||
| index.js:3:29:3:34 | 'User' | stringConcatenatedWith | |
|
||||
| index.js:3:37:3:40 | null | CalleeFlexibleAccessPath | mongoose.model |
|
||||
| index.js:3:37:3:40 | null | InputAccessPathFromCallee | |
|
||||
| index.js:3:37:3:40 | null | InputArgumentIndex | 1 |
|
||||
| index.js:3:37:3:40 | null | assignedToPropName | |
|
||||
| index.js:3:37:3:40 | null | calleeImports | mongoose |
|
||||
| index.js:3:37:3:40 | null | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:3:37:3:40 | null | contextSurroundingFunctionParameters | |
|
||||
| index.js:3:37:3:40 | null | enclosingFunctionBody | |
|
||||
| index.js:3:37:3:40 | null | enclosingFunctionName | |
|
||||
| index.js:3:37:3:40 | null | fileImports | express mongoose |
|
||||
| index.js:3:37:3:40 | null | receiverName | mongoose |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | argumentIndex | 0 |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeAccessPath | express post |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeApiName | express |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeName | post |
|
||||
| index.js:3:37:3:40 | null | stringConcatenatedWith | |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | InputAccessPathFromCallee | |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | InputArgumentIndex | 0 |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | assignedToPropName | |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | calleeImports | express |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | contextSurroundingFunctionParameters | () |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | fileImports | express mongoose |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | receiverName | app |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | argumentIndex | 1 |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeAccessPath | express post |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeApiName | express |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeName | post |
|
||||
| index.js:8:12:8:21 | '/isAdmin' | stringConcatenatedWith | |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | InputArgumentIndex | 1 |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | assignedToPropName | |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | calleeImports | express |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | fileImports | express mongoose |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | receiverName | app |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | argumentIndex | 0 |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPath | mongoose model find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeApiName | mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeName | find |
|
||||
| index.js:8:24:10:3 | (req, r ... });\\n } | stringConcatenatedWith | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | InputAccessPathFromCallee | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | InputArgumentIndex | 0 |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | assignedToPropName | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeImports | mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | fileImports | express mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | receiverName | User |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | argumentIndex | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeAccessPath | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeApiName | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeName | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | stringConcatenatedWith | |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | InputAccessPathFromCallee | 0.isAdmin |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | InputArgumentIndex | 0 |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | assignedToPropName | isAdmin |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | calleeImports | mongoose |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | fileImports | express mongoose |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | receiverName | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | argumentIndex | 0 |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeAccessPath | express post |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeApiName | express |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeName | post |
|
||||
| index.js:9:28:9:43 | req.body.isAdmin | stringConcatenatedWith | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | InputAccessPathFromCallee | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | InputArgumentIndex | 0 |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | assignedToPropName | |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | calleeImports | express |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | contextSurroundingFunctionParameters | () |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | fileImports | express mongoose |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | receiverName | app |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | argumentIndex | 1 |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeAccessPath | express post |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeAccessPathWithStructuralInfo | express instanceorreturn member post instanceorreturn |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeApiName | express |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeName | post |
|
||||
| index.js:14:12:14:21 | '/isAdmin' | stringConcatenatedWith | |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | CalleeFlexibleAccessPath | app.post |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | InputArgumentIndex | 1 |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | assignedToPropName | |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | calleeImports | express |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | fileImports | express mongoose |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | receiverName | app |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | argumentIndex | 0 |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeAccessPath | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeApiName | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeName | log |
|
||||
| index.js:14:24:16:3 | (req, r ... n);\\n } | stringConcatenatedWith | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | InputAccessPathFromCallee | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | InputArgumentIndex | 0 |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | assignedToPropName | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeImports | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | fileImports | express mongoose |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | receiverName | console |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | argumentIndex | 0 |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeAccessPath | mongoose model find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeApiName | mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeName | find |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | stringConcatenatedWith | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | InputAccessPathFromCallee | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | InputArgumentIndex | 0 |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | assignedToPropName | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeImports | mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | contextSurroundingFunctionParameters | () |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | enclosingFunctionBody | User find isAdmin true |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | enclosingFunctionName | notFlowFromSource |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | fileImports | express mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | receiverName | User |
|
||||
| index.js:20:26:20:29 | true | argumentIndex | |
|
||||
| index.js:20:26:20:29 | true | calleeAccessPath | |
|
||||
| index.js:20:26:20:29 | true | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:20:26:20:29 | true | calleeApiName | |
|
||||
| index.js:20:26:20:29 | true | calleeName | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | stringConcatenatedWith | |
|
||||
| index.js:20:26:20:29 | true | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:20:26:20:29 | true | InputAccessPathFromCallee | 0.isAdmin |
|
||||
| index.js:20:26:20:29 | true | InputArgumentIndex | 0 |
|
||||
| index.js:20:26:20:29 | true | assignedToPropName | isAdmin |
|
||||
| index.js:20:26:20:29 | true | calleeImports | mongoose |
|
||||
| index.js:20:26:20:29 | true | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:20:26:20:29 | true | contextSurroundingFunctionParameters | () |
|
||||
| index.js:20:26:20:29 | true | enclosingFunctionBody | User find isAdmin true |
|
||||
| index.js:20:26:20:29 | true | enclosingFunctionName | notFlowFromSource |
|
||||
| index.js:20:26:20:29 | true | fileImports | express mongoose |
|
||||
| index.js:20:26:20:29 | true | receiverName | |
|
||||
| index.js:24:13:24:22 | "constant" | argumentIndex | 0 |
|
||||
| index.js:24:13:24:22 | "constant" | calleeAccessPath | mongoose model find |
|
||||
| index.js:24:13:24:22 | "constant" | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:24:13:24:22 | "constant" | calleeApiName | mongoose |
|
||||
| index.js:24:13:24:22 | "constant" | calleeName | find |
|
||||
| index.js:20:26:20:29 | true | stringConcatenatedWith | |
|
||||
| index.js:24:13:24:22 | "constant" | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:24:13:24:22 | "constant" | InputAccessPathFromCallee | |
|
||||
| index.js:24:13:24:22 | "constant" | InputArgumentIndex | 0 |
|
||||
| index.js:24:13:24:22 | "constant" | assignedToPropName | |
|
||||
| index.js:24:13:24:22 | "constant" | calleeImports | mongoose |
|
||||
| index.js:24:13:24:22 | "constant" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:24:13:24:22 | "constant" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:24:13:24:22 | "constant" | enclosingFunctionBody | User find constant |
|
||||
| index.js:24:13:24:22 | "constant" | enclosingFunctionName | constantExpression |
|
||||
| index.js:24:13:24:22 | "constant" | fileImports | express mongoose |
|
||||
| index.js:24:13:24:22 | "constant" | receiverName | User |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | argumentIndex | 0 |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeAccessPath | mongoose model find |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeApiName | mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeName | find |
|
||||
| index.js:24:13:24:22 | "constant" | stringConcatenatedWith | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | InputAccessPathFromCallee | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | InputArgumentIndex | 0 |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | assignedToPropName | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeImports | mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | contextSurroundingFunctionParameters | () |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionBody | User find UNDEFINED_GLOBAL |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionName | notConstantExpression |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | fileImports | express mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | receiverName | User |
|
||||
| index.js:32:15:32:24 | "someData" | argumentIndex | 0 |
|
||||
| index.js:32:15:32:24 | "someData" | calleeAccessPath | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeApiName | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeName | log |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | stringConcatenatedWith | |
|
||||
| index.js:32:15:32:24 | "someData" | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:32:15:32:24 | "someData" | InputAccessPathFromCallee | |
|
||||
| index.js:32:15:32:24 | "someData" | InputArgumentIndex | 0 |
|
||||
| index.js:32:15:32:24 | "someData" | assignedToPropName | |
|
||||
| index.js:32:15:32:24 | "someData" | calleeImports | |
|
||||
| index.js:32:15:32:24 | "someData" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:32:15:32:24 | "someData" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:32:15:32:24 | "someData" | enclosingFunctionBody | console log someData |
|
||||
| index.js:32:15:32:24 | "someData" | enclosingFunctionName | notASink |
|
||||
| index.js:32:15:32:24 | "someData" | fileImports | express mongoose |
|
||||
| index.js:32:15:32:24 | "someData" | receiverName | console |
|
||||
| index.js:36:20:36:22 | "a" | argumentIndex | 0 |
|
||||
| index.js:36:20:36:22 | "a" | calleeAccessPath | |
|
||||
| index.js:36:20:36:22 | "a" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:36:20:36:22 | "a" | calleeApiName | |
|
||||
| index.js:36:20:36:22 | "a" | calleeName | startsWith |
|
||||
| index.js:32:15:32:24 | "someData" | stringConcatenatedWith | |
|
||||
| index.js:36:20:36:22 | "a" | CalleeFlexibleAccessPath | ?.startsWith |
|
||||
| index.js:36:20:36:22 | "a" | InputAccessPathFromCallee | |
|
||||
| index.js:36:20:36:22 | "a" | InputArgumentIndex | 0 |
|
||||
| index.js:36:20:36:22 | "a" | assignedToPropName | |
|
||||
| index.js:36:20:36:22 | "a" | calleeImports | |
|
||||
| index.js:36:20:36:22 | "a" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:36:20:36:22 | "a" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:36:20:36:22 | "a" | enclosingFunctionBody | abc startsWith a |
|
||||
| index.js:36:20:36:22 | "a" | enclosingFunctionName | notASinkMultipleReasons |
|
||||
| index.js:36:20:36:22 | "a" | fileImports | express mongoose |
|
||||
| index.js:36:20:36:22 | "a" | receiverName | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | argumentIndex | 0 |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeAccessPath | mongoose model find |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeApiName | mongoose |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeName | find |
|
||||
| index.js:36:20:36:22 | "a" | stringConcatenatedWith | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | InputAccessPathFromCallee | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | InputArgumentIndex | 0 |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | assignedToPropName | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | calleeImports | mongoose |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | enclosingFunctionBody | |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | enclosingFunctionName | veryLongFunctionBody |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | fileImports | express mongoose |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | receiverName | User |
|
||||
| index.js:78:30:78:39 | "someData" | argumentIndex | 0 |
|
||||
| index.js:78:30:78:39 | "someData" | calleeAccessPath | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeApiName | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeName | log |
|
||||
| index.js:41:13:68:61 | "a" + " ... " + "a" | stringConcatenatedWith | |
|
||||
| index.js:78:30:78:39 | "someData" | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:78:30:78:39 | "someData" | InputAccessPathFromCallee | |
|
||||
| index.js:78:30:78:39 | "someData" | InputArgumentIndex | 0 |
|
||||
| index.js:78:30:78:39 | "someData" | assignedToPropName | |
|
||||
| index.js:78:30:78:39 | "someData" | calleeImports | |
|
||||
| index.js:78:30:78:39 | "someData" | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:78:30:78:39 | "someData" | contextSurroundingFunctionParameters | () |
|
||||
| index.js:78:30:78:39 | "someData" | enclosingFunctionBody | console log someData |
|
||||
| index.js:78:30:78:39 | "someData" | enclosingFunctionName | identity#functionalargument |
|
||||
| index.js:78:30:78:39 | "someData" | fileImports | express mongoose |
|
||||
| index.js:78:30:78:39 | "someData" | receiverName | console |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | argumentIndex | 0 |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPath | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeApiName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeName | ajax |
|
||||
| index.js:78:30:78:39 | "someData" | stringConcatenatedWith | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | CalleeFlexibleAccessPath | $.ajax |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | InputArgumentIndex | 0 |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | assignedToPropName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeImports | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | contextSurroundingFunctionParameters | (foo) |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionBody | foo $ ajax url foo bar |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionName | effectiveSinkAndNotASink |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | fileImports | express mongoose |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | receiverName | $ |
|
||||
| index.js:84:12:84:18 | foo.bar | argumentIndex | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeAccessPath | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeApiName | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | stringConcatenatedWith | |
|
||||
| index.js:84:12:84:18 | foo.bar | CalleeFlexibleAccessPath | $.ajax |
|
||||
| index.js:84:12:84:18 | foo.bar | InputAccessPathFromCallee | 0.url |
|
||||
| index.js:84:12:84:18 | foo.bar | InputArgumentIndex | 0 |
|
||||
| index.js:84:12:84:18 | foo.bar | assignedToPropName | url |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeImports | |
|
||||
| index.js:84:12:84:18 | foo.bar | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:84:12:84:18 | foo.bar | contextSurroundingFunctionParameters | (foo) |
|
||||
| index.js:84:12:84:18 | foo.bar | enclosingFunctionBody | foo $ ajax url foo bar |
|
||||
| index.js:84:12:84:18 | foo.bar | enclosingFunctionName | effectiveSinkAndNotASink |
|
||||
| index.js:84:12:84:18 | foo.bar | fileImports | express mongoose |
|
||||
| index.js:84:12:84:18 | foo.bar | receiverName | |
|
||||
| index.js:84:12:84:18 | foo.bar | stringConcatenatedWith | |
|
||||
|
||||
@@ -76,51 +76,75 @@ endpoints
|
||||
| index.js:84:12:84:18 | foo.bar | Xss | notASinkReason | ClientRequest | string |
|
||||
| index.js:84:12:84:18 | foo.bar | Xss | sinkLabel | NotASink | string |
|
||||
tokenFeatures
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | argumentIndex | 0 |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPath | mongoose model find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeApiName | mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeName | find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | InputAccessPathFromCallee | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | InputArgumentIndex | 0 |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | assignedToPropName | |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeImports | mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | enclosingFunctionBody | app post /isAdmin req res User find isAdmin req body isAdmin |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | enclosingFunctionName | flowFromSourceToSink |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | fileImports | express mongoose |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | receiverName | User |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | argumentIndex | 0 |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeAccessPath | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeApiName | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeName | log |
|
||||
| index.js:9:15:9:45 | { 'isAd ... Admin } | stringConcatenatedWith | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | CalleeFlexibleAccessPath | console.log |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | InputAccessPathFromCallee | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | InputArgumentIndex | 0 |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | assignedToPropName | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | calleeImports | |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | contextSurroundingFunctionParameters | ()\n(req, res) |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | enclosingFunctionBody | app post /isAdmin req res console log req body isAdmin |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | enclosingFunctionName | flowFromSourceToNotASink |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | fileImports | express mongoose |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | receiverName | console |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | argumentIndex | 0 |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeAccessPath | mongoose model find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeApiName | mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeName | find |
|
||||
| index.js:15:17:15:32 | req.body.isAdmin | stringConcatenatedWith | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | InputAccessPathFromCallee | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | InputArgumentIndex | 0 |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | assignedToPropName | |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | calleeImports | mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | contextSurroundingFunctionParameters | () |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | enclosingFunctionBody | User find isAdmin true |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | enclosingFunctionName | notFlowFromSource |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | fileImports | express mongoose |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | receiverName | User |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | argumentIndex | 0 |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeAccessPath | mongoose model find |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeAccessPathWithStructuralInfo | mongoose member model instanceorreturn member find instanceorreturn |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeApiName | mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeName | find |
|
||||
| index.js:20:13:20:31 | { 'isAdmin': true } | stringConcatenatedWith | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | CalleeFlexibleAccessPath | User.find |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | InputAccessPathFromCallee | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | InputArgumentIndex | 0 |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | assignedToPropName | |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | calleeImports | mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | contextSurroundingFunctionParameters | () |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionBody | User find UNDEFINED_GLOBAL |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionName | notConstantExpression |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | fileImports | express mongoose |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | receiverName | User |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | argumentIndex | 0 |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPath | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeApiName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeName | ajax |
|
||||
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | stringConcatenatedWith | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | CalleeFlexibleAccessPath | $.ajax |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | InputAccessPathFromCallee | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | InputArgumentIndex | 0 |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | assignedToPropName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeImports | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | contextSurroundingFunctionParameters | (foo) |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionBody | foo $ ajax url foo bar |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionName | effectiveSinkAndNotASink |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | fileImports | express mongoose |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | receiverName | $ |
|
||||
| index.js:84:12:84:18 | foo.bar | argumentIndex | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeAccessPath | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeAccessPathWithStructuralInfo | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeApiName | |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeName | |
|
||||
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | stringConcatenatedWith | |
|
||||
| index.js:84:12:84:18 | foo.bar | CalleeFlexibleAccessPath | $.ajax |
|
||||
| index.js:84:12:84:18 | foo.bar | InputAccessPathFromCallee | 0.url |
|
||||
| index.js:84:12:84:18 | foo.bar | InputArgumentIndex | 0 |
|
||||
| index.js:84:12:84:18 | foo.bar | assignedToPropName | url |
|
||||
| index.js:84:12:84:18 | foo.bar | calleeImports | |
|
||||
| index.js:84:12:84:18 | foo.bar | contextFunctionInterfaces | constantExpression()\neffectiveSinkAndNotASink(foo)\nflowFromSourceToNotASink()\nflowFromSourceToSink()\nidentity(x)\nnotASink()\nnotASinkMultipleReasons()\nnotConstantExpression()\nnotFlowFromSource()\nveryLongFunctionBody() |
|
||||
| index.js:84:12:84:18 | foo.bar | contextSurroundingFunctionParameters | (foo) |
|
||||
| index.js:84:12:84:18 | foo.bar | enclosingFunctionBody | foo $ ajax url foo bar |
|
||||
| index.js:84:12:84:18 | foo.bar | enclosingFunctionName | effectiveSinkAndNotASink |
|
||||
| index.js:84:12:84:18 | foo.bar | fileImports | express mongoose |
|
||||
| index.js:84:12:84:18 | foo.bar | receiverName | |
|
||||
| index.js:84:12:84:18 | foo.bar | stringConcatenatedWith | |
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.FeaturizationConfig
|
||||
import TestUtil
|
||||
|
||||
// every feature must produce a value for at least one endpoint, otherwise the feature is completely broken, or a relevant test example is missing
|
||||
from EndpointFeature feature
|
||||
where forall(Endpoint endpoint | not exists(feature.getValue(endpoint)))
|
||||
select feature.getName()
|
||||
@@ -0,0 +1,141 @@
|
||||
| test.html:2:61:2:68 | endpoint | CalleeFlexibleAccessPath | $event.target.files.item |
|
||||
| test.html:2:61:2:68 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.html:2:61:2:68 | endpoint | contextFunctionInterfaces | |
|
||||
| test.html:2:61:2:68 | endpoint | contextSurroundingFunctionParameters | |
|
||||
| test.html:2:61:2:68 | endpoint | fileImports | |
|
||||
| test.js:6:7:6:14 | endpoint | CalleeFlexibleAccessPath | f |
|
||||
| test.js:6:7:6:14 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:6:7:6:14 | endpoint | calleeImports | ? lib3 |
|
||||
| test.js:6:7:6:14 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:6:7:6:14 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:6:7:6:14 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:6:7:6:14 | endpoint | enclosingFunctionName | |
|
||||
| test.js:6:7:6:14 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:7:11:7:18 | endpoint | CalleeFlexibleAccessPath | f |
|
||||
| test.js:7:11:7:18 | endpoint | InputAccessPathFromCallee | 0.p |
|
||||
| test.js:7:11:7:18 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:7:11:7:18 | endpoint | assignedToPropName | p |
|
||||
| test.js:7:11:7:18 | endpoint | calleeImports | ? lib3 |
|
||||
| test.js:7:11:7:18 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:7:11:7:18 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:7:11:7:18 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:7:11:7:18 | endpoint | enclosingFunctionName | |
|
||||
| test.js:7:11:7:18 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:8:15:8:22 | endpoint | CalleeFlexibleAccessPath | f |
|
||||
| test.js:8:15:8:22 | endpoint | InputAccessPathFromCallee | 0.p.q |
|
||||
| test.js:8:15:8:22 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:8:15:8:22 | endpoint | assignedToPropName | q |
|
||||
| test.js:8:15:8:22 | endpoint | calleeImports | ? lib3 |
|
||||
| test.js:8:15:8:22 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:8:15:8:22 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:8:15:8:22 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:8:15:8:22 | endpoint | enclosingFunctionName | |
|
||||
| test.js:8:15:8:22 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:9:9:9:16 | endpoint | CalleeFlexibleAccessPath | o.m |
|
||||
| test.js:9:9:9:16 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:9:9:9:16 | endpoint | calleeImports | ? lib2 |
|
||||
| test.js:9:9:9:16 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:9:9:9:16 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:9:9:9:16 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:9:9:9:16 | endpoint | enclosingFunctionName | |
|
||||
| test.js:9:9:9:16 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:9:9:9:16 | endpoint | receiverName | o |
|
||||
| test.js:10:13:10:20 | endpoint | CalleeFlexibleAccessPath | o.m |
|
||||
| test.js:10:13:10:20 | endpoint | InputAccessPathFromCallee | 0.p |
|
||||
| test.js:10:13:10:20 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:10:13:10:20 | endpoint | assignedToPropName | p |
|
||||
| test.js:10:13:10:20 | endpoint | calleeImports | ? lib2 |
|
||||
| test.js:10:13:10:20 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:10:13:10:20 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:10:13:10:20 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:10:13:10:20 | endpoint | enclosingFunctionName | |
|
||||
| test.js:10:13:10:20 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:11:17:11:24 | endpoint | CalleeFlexibleAccessPath | o.m |
|
||||
| test.js:11:17:11:24 | endpoint | InputAccessPathFromCallee | 0.p.q |
|
||||
| test.js:11:17:11:24 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:11:17:11:24 | endpoint | assignedToPropName | q |
|
||||
| test.js:11:17:11:24 | endpoint | calleeImports | ? lib2 |
|
||||
| test.js:11:17:11:24 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:11:17:11:24 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:11:17:11:24 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:11:17:11:24 | endpoint | enclosingFunctionName | |
|
||||
| test.js:11:17:11:24 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:12:11:12:18 | endpoint | CalleeFlexibleAccessPath | F |
|
||||
| test.js:12:11:12:18 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:12:11:12:18 | endpoint | calleeImports | lib1 |
|
||||
| test.js:12:11:12:18 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:12:11:12:18 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:12:11:12:18 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:12:11:12:18 | endpoint | enclosingFunctionName | |
|
||||
| test.js:12:11:12:18 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:13:17:13:24 | endpoint | CalleeFlexibleAccessPath | o.m().m().m |
|
||||
| test.js:13:17:13:24 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:13:17:13:24 | endpoint | calleeImports | ? lib2 |
|
||||
| test.js:13:17:13:24 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:13:17:13:24 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:13:17:13:24 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:13:17:13:24 | endpoint | enclosingFunctionName | |
|
||||
| test.js:13:17:13:24 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:14:9:14:16 | endpoint | CalleeFlexibleAccessPath | f() |
|
||||
| test.js:14:9:14:16 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:14:9:14:16 | endpoint | calleeImports | ? lib3 |
|
||||
| test.js:14:9:14:16 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:14:9:14:16 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:14:9:14:16 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:14:9:14:16 | endpoint | enclosingFunctionName | |
|
||||
| test.js:14:9:14:16 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:15:12:15:19 | endpoint | CalleeFlexibleAccessPath | o.?.m |
|
||||
| test.js:15:12:15:19 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:15:12:15:19 | endpoint | calleeImports | ? lib2 |
|
||||
| test.js:15:12:15:19 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:15:12:15:19 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:15:12:15:19 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:15:12:15:19 | endpoint | enclosingFunctionName | |
|
||||
| test.js:15:12:15:19 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:16:16:16:23 | endpoint | CalleeFlexibleAccessPath | o.m.?.p.m |
|
||||
| test.js:16:16:16:23 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:16:16:16:23 | endpoint | calleeImports | ? lib2 |
|
||||
| test.js:16:16:16:23 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:16:16:16:23 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:16:16:16:23 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:16:16:16:23 | endpoint | enclosingFunctionName | |
|
||||
| test.js:16:16:16:23 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:17:15:17:22 | endpoint | CalleeFlexibleAccessPath | (await p) |
|
||||
| test.js:17:15:17:22 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:17:15:17:22 | endpoint | calleeImports | lib1 |
|
||||
| test.js:17:15:17:22 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:17:15:17:22 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:17:15:17:22 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:17:15:17:22 | endpoint | enclosingFunctionName | |
|
||||
| test.js:17:15:17:22 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:18:27:18:34 | endpoint | CalleeFlexibleAccessPath | import(!).bar.baz |
|
||||
| test.js:18:27:18:34 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:18:27:18:34 | endpoint | calleeImports | foo |
|
||||
| test.js:18:27:18:34 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:18:27:18:34 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:18:27:18:34 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:18:27:18:34 | endpoint | enclosingFunctionName | |
|
||||
| test.js:18:27:18:34 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:20:13:20:20 | endpoint | CalleeFlexibleAccessPath | bar |
|
||||
| test.js:20:13:20:20 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:20:13:20:20 | endpoint | calleeImports | lib1 |
|
||||
| test.js:20:13:20:20 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:20:13:20:20 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:20:13:20:20 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:20:13:20:20 | endpoint | enclosingFunctionName | |
|
||||
| test.js:20:13:20:20 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:22:21:22:28 | endpoint | InputArgumentIndex | 0 |
|
||||
| test.js:22:21:22:28 | endpoint | calleeImports | ? lib2 lib3 |
|
||||
| test.js:22:21:22:28 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:22:21:22:28 | endpoint | contextSurroundingFunctionParameters | () |
|
||||
| test.js:22:21:22:28 | endpoint | enclosingFunctionBody | f endpoint 12 f p endpoint f p q endpoint o m endpoint o m p endpoint o m p q endpoint F endpoint o m m m endpoint f endpoint o x m endpoint o m x p m endpoint p endpoint foo bar baz endpoint foo bar endpoint f f o m endpoint |
|
||||
| test.js:22:21:22:28 | endpoint | enclosingFunctionName | |
|
||||
| test.js:22:21:22:28 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:33:50:33:57 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:33:50:33:57 | endpoint | contextSurroundingFunctionParameters | |
|
||||
| test.js:33:50:33:57 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:33:50:33:57 | endpoint | stringConcatenatedWith | f() + '<a target="_blank" href="' -endpoint- '"></a>' |
|
||||
| test.js:35:18:35:25 | endpoint | contextFunctionInterfaces | f(?)\nfoo()\ng()\nm() |
|
||||
| test.js:35:18:35:25 | endpoint | contextSurroundingFunctionParameters | |
|
||||
| test.js:35:18:35:25 | endpoint | fileImports | foo lib1 lib2 lib3 |
|
||||
| test.js:35:18:35:25 | endpoint | stringConcatenatedWith | 'foo' -endpoint- 'bar' |
|
||||
@@ -0,0 +1,7 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures
|
||||
import TestUtil
|
||||
|
||||
// detailed output for the nearby tests
|
||||
from Endpoint endpoint, EndpointFeature feature
|
||||
select endpoint, feature.getName(), feature.getValue(endpoint)
|
||||
@@ -0,0 +1,8 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures
|
||||
import TestUtil
|
||||
|
||||
// every endpoint should have at least one feature value, otherwise the test source is likely malformed
|
||||
from Endpoint endpoint
|
||||
where not exists(EndpointFeature f | exists(f.getValue(endpoint)))
|
||||
select endpoint
|
||||
@@ -0,0 +1,8 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures
|
||||
import TestUtil
|
||||
|
||||
// every feature must produce a single value for each endpoint that it computes a value for, per the contract of the `scoreEndpoints` HOP
|
||||
from Endpoint endpoint, EndpointFeature feature, int arity
|
||||
where arity = count(feature.getValue(endpoint)) and arity > 1
|
||||
select endpoint, feature.getName(), arity
|
||||
@@ -0,0 +1,6 @@
|
||||
import javascript
|
||||
import extraction.NoFeaturizationRestrictionsConfig
|
||||
|
||||
class Endpoint extends DataFlow::Node {
|
||||
Endpoint() { this.asExpr().(VarAccess).getName() = "endpoint" }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="form-group">
|
||||
<input (change)="restoreBackup($event.target.files.item(endpoint))" />
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { bar, F, p } from 'lib1';
|
||||
import * as o from 'lib2';
|
||||
const f = require('lib3');
|
||||
|
||||
(async function () {
|
||||
f(endpoint, 12);
|
||||
f({p: endpoint});
|
||||
f({p: {q: endpoint}});
|
||||
o.m(endpoint);
|
||||
o.m({p: endpoint});
|
||||
o.m({p: {q: endpoint}});
|
||||
new F(endpoint);
|
||||
o.m().m().m(endpoint);
|
||||
f()(endpoint);
|
||||
o[x].m(endpoint);
|
||||
o.m[x].p.m(endpoint);
|
||||
(await p)(endpoint);
|
||||
import("foo").bar.baz(endpoint);
|
||||
function foo() {
|
||||
bar(endpoint);
|
||||
}
|
||||
(f() ? f : o.m)(endpoint);
|
||||
});
|
||||
|
||||
function f({ endpoint }) {}
|
||||
|
||||
const g = async () => undefined;
|
||||
|
||||
const o = { m: () => undefined }
|
||||
|
||||
const url = f();
|
||||
|
||||
const x = f() + "<a target=\"_blank\" href=\"" + endpoint + "\"></a>";
|
||||
|
||||
const y = "foo"+ endpoint + "bar";
|
||||
@@ -354,35 +354,6 @@ module DOM {
|
||||
call.getNumArgument() = 1 and
|
||||
unique(InferredType t | t = getArgumentTypeFromJQueryMethodGet(call)) = TTNumber()
|
||||
)
|
||||
or
|
||||
// A `this` node from a callback given to a `$().each(callback)` call.
|
||||
// purposely not using JQuery::MethodCall to avoid `jquery.each()`.
|
||||
exists(DataFlow::CallNode eachCall | eachCall = JQuery::objectRef().getAMethodCall("each") |
|
||||
this = DataFlow::thisNode(eachCall.getCallback(0).getFunction()) or
|
||||
this = eachCall.getABoundCallbackParameter(0, 1)
|
||||
)
|
||||
or
|
||||
// A read of an array-element from a JQuery object. E.g. `$("#foo")[0]`
|
||||
exists(DataFlow::PropRead read |
|
||||
read = this and read = JQuery::objectRef().getAPropertyRead()
|
||||
|
|
||||
unique(InferredType t | t = read.getPropertyNameExpr().analyze().getAType()) = TTNumber()
|
||||
)
|
||||
or
|
||||
// A receiver node of an event handler on a DOM node
|
||||
exists(DataFlow::SourceNode domNode, DataFlow::FunctionNode eventHandler |
|
||||
// NOTE: we do not use `getABoundFunctionValue()`, since bound functions tend to have
|
||||
// a different receiver anyway
|
||||
eventHandler = domNode.getAPropertySource(any(string n | n.matches("on%")))
|
||||
or
|
||||
eventHandler =
|
||||
domNode.getAMethodCall("addEventListener").getArgument(1).getAFunctionValue()
|
||||
|
|
||||
domNode = domValueRef() and
|
||||
this = eventHandler.getReceiver()
|
||||
)
|
||||
or
|
||||
this = DataFlow::thisNode(any(EventHandlerCode evt))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,11 +387,6 @@ module DOM {
|
||||
or
|
||||
t.start() and
|
||||
result = domValueRef().getAMethodCall(["item", "namedItem"])
|
||||
or
|
||||
t.startInProp("target") and
|
||||
result = domEventSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that may refer to a value from the DOM. */
|
||||
|
||||
@@ -183,19 +183,12 @@ module Promises {
|
||||
/**
|
||||
* Gets the pseudo-field used to describe resolved values in a promise.
|
||||
*/
|
||||
string valueProp() { result = "$PromiseResolveField$" }
|
||||
string valueProp() { none() }
|
||||
|
||||
/**
|
||||
* Gets the pseudo-field used to describe rejected values in a promise.
|
||||
*/
|
||||
string errorProp() { result = "$PromiseRejectField$" }
|
||||
|
||||
/** A property set containing the pseudo-properites of a promise object. */
|
||||
class PromiseProps extends DataFlow::PropertySet {
|
||||
PromiseProps() { this = "PromiseProps" }
|
||||
|
||||
override string getAProperty() { result = [valueProp(), errorProp()] }
|
||||
}
|
||||
string errorProp() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -756,10 +756,10 @@ private class AdditionalFlowStepAsSharedStep extends SharedFlowStep {
|
||||
*/
|
||||
module PseudoProperties {
|
||||
bindingset[s]
|
||||
private string pseudoProperty(string s) { result = "$" + s + "$" }
|
||||
private string pseudoProperty(string s) { none() }
|
||||
|
||||
bindingset[s, v]
|
||||
private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" }
|
||||
private string pseudoProperty(string s, string v) { none() }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of elements in a `Set`
|
||||
|
||||
@@ -136,7 +136,7 @@ module Angular2 {
|
||||
|
||||
/** Gets a reference to a `DomSanitizer` object. */
|
||||
DataFlow::SourceNode domSanitizer() {
|
||||
result.hasUnderlyingType(["@angular/platform-browser", "@angular/core"], "DomSanitizer")
|
||||
result.hasUnderlyingType("@angular/platform-browser", "DomSanitizer")
|
||||
}
|
||||
|
||||
/** A value that is about to be promoted to a trusted HTML or CSS value. */
|
||||
|
||||
@@ -932,28 +932,6 @@ module Express {
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
|
||||
/** A call to `response.sendFile`, considered as a file system access. */
|
||||
private class ResponseSendFileAsFileSystemAccess extends FileSystemReadAccess,
|
||||
DataFlow::MethodCallNode {
|
||||
ResponseSendFileAsFileSystemAccess() {
|
||||
exists(string name | name = "sendFile" or name = "sendfile" |
|
||||
this.calls(any(ResponseExpr res).flow(), name)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getRootPathArgument() {
|
||||
result = this.(DataFlow::CallNode).getOptionArgument(1, "root")
|
||||
}
|
||||
|
||||
override predicate isUpwardNavigationRejected(DataFlow::Node argument) {
|
||||
argument = this.getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that flows to a route setup.
|
||||
*/
|
||||
|
||||
@@ -4,23 +4,6 @@
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A call that can produce a file name.
|
||||
*/
|
||||
abstract private class FileNameProducer extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a file name produced by this producer.
|
||||
*/
|
||||
abstract DataFlow::Node getAFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that contains a file name, and is produced by a `ProducesFileNames`.
|
||||
*/
|
||||
private class ProducedFileName extends FileNameSource {
|
||||
ProducedFileName() { this = any(FileNameProducer producer).getAFileName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A file name from the `walk-sync` library.
|
||||
*/
|
||||
@@ -118,331 +101,3 @@ private API::Node fastGlobFileName() {
|
||||
private class FastGlobFileNameSource extends FileNameSource {
|
||||
FastGlobFileNameSource() { this = fastGlobFileName().asSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modeling the `fstream` library (https://www.npmjs.com/package/fstream).
|
||||
*/
|
||||
private module FStream {
|
||||
/**
|
||||
* Gets a reference to a method in the `fstream` library.
|
||||
*/
|
||||
private DataFlow::SourceNode getAnFStreamProperty(boolean writer) {
|
||||
exists(DataFlow::SourceNode mod, string readOrWrite, string subMod |
|
||||
mod = DataFlow::moduleImport("fstream") and
|
||||
(
|
||||
readOrWrite = "Reader" and writer = false
|
||||
or
|
||||
readOrWrite = "Writer" and writer = true
|
||||
) and
|
||||
subMod = ["File", "Dir", "Link", "Proxy"]
|
||||
|
|
||||
result = mod.getAPropertyRead(readOrWrite) or
|
||||
result = mod.getAPropertyRead(readOrWrite).getAPropertyRead(subMod) or
|
||||
result = mod.getAPropertyRead(subMod).getAPropertyRead(readOrWrite)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of a method defined in the `fstream` library.
|
||||
*/
|
||||
private class FStream extends FileSystemAccess, DataFlow::InvokeNode {
|
||||
boolean writer;
|
||||
|
||||
FStream() { this = getAnFStreamProperty(writer).getAnInvocation() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getOptionArgument(0, "path")
|
||||
or
|
||||
not exists(this.getOptionArgument(0, "path")) and
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of an `fstream` method that writes to a file.
|
||||
*/
|
||||
private class FStreamWriter extends FileSystemWriteAccess, FStream {
|
||||
FStreamWriter() { writer = true }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of an `fstream` method that reads a file.
|
||||
*/
|
||||
private class FStreamReader extends FileSystemReadAccess, FStream {
|
||||
FStreamReader() { writer = false }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `write-file-atomic`.
|
||||
*/
|
||||
private class WriteFileAtomic extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
WriteFileAtomic() {
|
||||
this = DataFlow::moduleImport("write-file-atomic").getACall()
|
||||
or
|
||||
this = DataFlow::moduleMember("write-file-atomic", "sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = this.getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `chownr`.
|
||||
* The library changes the owner of a file or directory recursively.
|
||||
*/
|
||||
private class Chownr extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
Chownr() { this = DataFlow::moduleImport("chownr").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `recursive-readdir`.
|
||||
*/
|
||||
private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, API::CallNode {
|
||||
RecursiveReadDir() { this = API::moduleImport("recursive-readdir").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() { result = this.trackFileSource().asSource() }
|
||||
|
||||
private API::Node trackFileSource() {
|
||||
result = this.getParameter([1 .. 2]).getParameter(1)
|
||||
or
|
||||
not exists(this.getCallback([1 .. 2])) and result = this.getReturn().getPromised()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modeling the `jsonfile` library (https://www.npmjs.com/package/jsonfile).
|
||||
*/
|
||||
private module JsonFile {
|
||||
/**
|
||||
* A reader for JSON files.
|
||||
*/
|
||||
class JsonFileReader extends FileSystemReadAccess, API::CallNode {
|
||||
JsonFileReader() {
|
||||
this = API::moduleImport("jsonfile").getMember(["readFile", "readFileSync"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = this.trackRead().asSource() }
|
||||
|
||||
private API::Node trackRead() {
|
||||
this.getCalleeName() = "readFile" and
|
||||
(
|
||||
result = this.getParameter([1 .. 2]).getParameter(1)
|
||||
or
|
||||
not exists(this.getCallback([1 .. 2])) and result = this.getReturn().getPromised()
|
||||
)
|
||||
or
|
||||
this.getCalleeName() = "readFileSync" and
|
||||
result = this.getReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for JsonFileReader */
|
||||
deprecated class JSONFileReader = JsonFileReader;
|
||||
|
||||
/**
|
||||
* A writer for JSON files.
|
||||
*/
|
||||
class JsonFileWriter extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
JsonFileWriter() {
|
||||
this =
|
||||
DataFlow::moduleMember("jsonfile", any(string s | s = "writeFile" or s = "writeFileSync"))
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = this.getArgument(1) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for JsonFileWriter */
|
||||
deprecated class JSONFileWriter = JsonFileWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `load-json-file`.
|
||||
*/
|
||||
private class LoadJsonFile extends FileSystemReadAccess, API::CallNode {
|
||||
LoadJsonFile() {
|
||||
this = API::moduleImport("load-json-file").getACall()
|
||||
or
|
||||
this = API::moduleImport("load-json-file").getMember("sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = this.trackRead().asSource() }
|
||||
|
||||
private API::Node trackRead() {
|
||||
this.getCalleeName() = "sync" and result = this.getReturn()
|
||||
or
|
||||
not this.getCalleeName() = "sync" and result = this.getReturn().getPromised()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getADataNode() { result = this.getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library `walkdir`.
|
||||
*/
|
||||
private class WalkDir extends FileNameProducer, FileSystemAccess, API::CallNode {
|
||||
WalkDir() {
|
||||
this = API::moduleImport("walkdir").getACall()
|
||||
or
|
||||
this = API::moduleImport("walkdir").getMember("sync").getACall()
|
||||
or
|
||||
this = API::moduleImport("walkdir").getMember("async").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() { result = this.trackFileSource().asSource() }
|
||||
|
||||
private API::Node trackFileSource() {
|
||||
not this.getCalleeName() = ["sync", "async"] and
|
||||
(
|
||||
result = this.getParameter(this.getNumArgument() - 1).getParameter(0)
|
||||
or
|
||||
result = this.getReturn().getMember(EventEmitter::on()).getParameter(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
this.getCalleeName() = "sync" and result = this.getReturn()
|
||||
or
|
||||
this.getCalleeName() = "async" and result = this.getReturn().getPromised()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = this.getArgument(1)
|
||||
or
|
||||
this.getCalleeName() = "mapping" and
|
||||
(
|
||||
result = this.getAnArgument() and
|
||||
not exists(result.getALocalSource().getAPropertyWrite("src"))
|
||||
or
|
||||
result = this.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.
|
||||
*/
|
||||
private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
|
||||
int pathArgument; // The index of the path argument.
|
||||
|
||||
LibraryAccess() {
|
||||
pathArgument = 0 and
|
||||
(
|
||||
this = DataFlow::moduleImport("path-exists").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("rimraf").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("readdirp").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("walker").getACall()
|
||||
or
|
||||
this =
|
||||
DataFlow::moduleMember("node-dir",
|
||||
["readFiles", "readFilesStream", "files", "promiseFiles", "subdirs", "paths"]).getACall()
|
||||
)
|
||||
or
|
||||
pathArgument = 0 and
|
||||
this =
|
||||
DataFlow::moduleMember("vinyl-fs", any(string s | s = "src" or s = "dest" or s = "symlink"))
|
||||
.getACall()
|
||||
or
|
||||
pathArgument = [0 .. 1] and
|
||||
(
|
||||
this = DataFlow::moduleImport("ncp").getACall() or
|
||||
this = DataFlow::moduleMember("ncp", "ncp").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArgument) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the library [`chokidar`](https://www.npmjs.com/package/chokidar), where a call to `on` receives file names.
|
||||
*/
|
||||
class Chokidar extends FileNameProducer, FileSystemAccess, API::CallNode {
|
||||
Chokidar() { this = API::moduleImport("chokidar").getMember("watch").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::Node getAFileName() {
|
||||
exists(DataFlow::CallNode onCall, int pathIndex |
|
||||
onCall = this.getAChainedMethodCall("on") and
|
||||
if onCall.getArgument(0).mayHaveStringValue("all") then pathIndex = 1 else pathIndex = 0
|
||||
|
|
||||
result = onCall.getCallback(1).getParameter(pathIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the [`mkdirp`](https://www.npmjs.com/package/mkdirp) library.
|
||||
*/
|
||||
private class Mkdirp extends FileSystemAccess, API::CallNode {
|
||||
Mkdirp() {
|
||||
this = API::moduleImport("mkdirp").getACall()
|
||||
or
|
||||
this = API::moduleImport("mkdirp").getMember("sync").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -16,135 +16,117 @@ module NoSql {
|
||||
/** DEPRECATED: Alias for NoSql */
|
||||
deprecated module NoSQL = NoSql;
|
||||
|
||||
/**
|
||||
* Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`.
|
||||
*/
|
||||
private DataFlow::Node getADollarWhereProperty(API::Node queryArg) {
|
||||
result = queryArg.getMember("$where").asSink()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the MongoDB library.
|
||||
*/
|
||||
private module MongoDB {
|
||||
/**
|
||||
* Gets an access to `mongodb.MongoClient` or a database.
|
||||
*
|
||||
* In Mongo version 2.x, a client and a database handle were the same concept, but in 3.x
|
||||
* they were separated. To handle everything with a single model, we treat them as the same here.
|
||||
* Gets an import of MongoDB.
|
||||
*/
|
||||
private API::Node getAMongoClientOrDatabase() {
|
||||
result = API::moduleImport("mongodb").getMember("MongoClient")
|
||||
DataFlow::ModuleImportNode mongodb() { result.getPath() = "mongodb" }
|
||||
|
||||
/**
|
||||
* Gets an access to `mongodb.MongoClient`.
|
||||
*/
|
||||
private DataFlow::SourceNode getAMongoClient(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = mongodb().getAPropertyRead("MongoClient")
|
||||
or
|
||||
result = getAMongoClientOrDatabase().getMember("db").getReturn()
|
||||
exists(DataFlow::TypeTracker t2 | result = getAMongoClient(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to `mongodb.MongoClient`.
|
||||
*/
|
||||
DataFlow::SourceNode getAMongoClient() { result = getAMongoClient(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a data flow node that leads to a `connect` callback. */
|
||||
private DataFlow::SourceNode getAMongoDbCallback(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getAMongoClient().getAMemberCall("connect").getArgument(1).getALocalSource()
|
||||
or
|
||||
result = getAMongoClientOrDatabase().getMember("connect").getLastParameter().getParameter(1)
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getAMongoDbCallback(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that leads to a `connect` callback. */
|
||||
private DataFlow::FunctionNode getAMongoDbCallback() {
|
||||
result = getAMongoDbCallback(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that may refer to a MongoDB database connection.
|
||||
*/
|
||||
private DataFlow::SourceNode getAMongoDb(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = getAMongoDbCallback().getParameter(1)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getAMongoDb(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that may refer to a MongoDB database connection.
|
||||
*/
|
||||
DataFlow::SourceNode getAMongoDb() { result = getAMongoDb(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* A data flow node that may hold a MongoDB collection.
|
||||
*/
|
||||
abstract class Collection extends DataFlow::SourceNode { }
|
||||
|
||||
/**
|
||||
* A collection resulting from calling `Db.collection(...)`.
|
||||
*/
|
||||
private class CollectionFromDb extends Collection {
|
||||
CollectionFromDb() {
|
||||
this = getAMongoDb().getAMethodCall("collection")
|
||||
or
|
||||
this = getAMongoDb().getAMethodCall("collection").getCallback(1).getParameter(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection based on the type `mongodb.Collection`.
|
||||
*
|
||||
* Note that this also covers `mongoose` models since they are subtypes
|
||||
* of `mongodb.Collection`.
|
||||
*/
|
||||
private class CollectionFromType extends Collection {
|
||||
CollectionFromType() { hasUnderlyingType("mongodb", "Collection") }
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
private API::Node getACollection() {
|
||||
// A collection resulting from calling `Db.collection(...)`.
|
||||
exists(API::Node collection |
|
||||
collection = getAMongoClientOrDatabase().getMember("collection").getReturn()
|
||||
|
|
||||
result = collection
|
||||
or
|
||||
result = collection.getParameter(1).getParameter(0)
|
||||
)
|
||||
private DataFlow::SourceNode getACollection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof Collection
|
||||
or
|
||||
// note that this also covers `mongoose` models since they are subtypes of `mongodb.Collection`
|
||||
result = API::Node::ofType("mongodb", "Collection")
|
||||
exists(DataFlow::TypeTracker t2 | result = getACollection(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
DataFlow::SourceNode getACollection() { result = getACollection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to a MongoDB query method. */
|
||||
private class QueryCall extends DatabaseAccess, API::CallNode {
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string method |
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(method, queryArgIdx) and
|
||||
this = getACollection().getMember(method).getACall()
|
||||
exists(string m | this = getACollection().getAMethodCall(m) |
|
||||
m = "count" and queryArgIdx = 0
|
||||
or
|
||||
m = "distinct" and queryArgIdx = 1
|
||||
or
|
||||
m = "find" and queryArgIdx = 0
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(queryArgIdx) }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(this.getParameter(queryArgIdx))
|
||||
}
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MongoDB query.
|
||||
*/
|
||||
class Query extends NoSql::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Collection methods.
|
||||
*/
|
||||
module CollectionMethodSignatures {
|
||||
/**
|
||||
* Holds if Collection method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// FilterQuery
|
||||
(
|
||||
name = "aggregate" and n = 0
|
||||
or
|
||||
name = "count" and n = 0
|
||||
or
|
||||
name = "countDocuments" and n = 0
|
||||
or
|
||||
name = "deleteMany" and n = 0
|
||||
or
|
||||
name = "deleteOne" and n = 0
|
||||
or
|
||||
name = "distinct" and n = 1
|
||||
or
|
||||
name = "find" and n = 0
|
||||
or
|
||||
name = "findOne" and n = 0
|
||||
or
|
||||
name = "findOneAndDelete" and n = 0
|
||||
or
|
||||
name = "findOneAndRemove" and n = 0
|
||||
or
|
||||
name = "findOneAndReplace" and n = 0
|
||||
or
|
||||
name = "findOneAndUpdate" and n = 0
|
||||
or
|
||||
name = "remove" and n = 0
|
||||
or
|
||||
name = "replaceOne" and n = 0
|
||||
or
|
||||
name = "update" and n = 0
|
||||
or
|
||||
name = "updateMany" and n = 0
|
||||
or
|
||||
name = "updateOne" and n = 0
|
||||
)
|
||||
or
|
||||
// UpdateQuery
|
||||
(
|
||||
name = "findOneAndUpdate" and n = 1
|
||||
or
|
||||
name = "update" and n = 1
|
||||
or
|
||||
name = "updateMany" and n = 1
|
||||
or
|
||||
name = "updateOne" and n = 1
|
||||
)
|
||||
}
|
||||
Query() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,342 +137,20 @@ private module Mongoose {
|
||||
/**
|
||||
* Gets an import of Mongoose.
|
||||
*/
|
||||
API::Node getAMongooseInstance() { result = API::moduleImport("mongoose") }
|
||||
DataFlow::ModuleImportNode getAMongooseInstance() { result.getPath() = "mongoose" }
|
||||
|
||||
/**
|
||||
* Gets a reference to `mongoose.createConnection`.
|
||||
* Gets a call to `mongoose.createConnection`.
|
||||
*/
|
||||
API::Node createConnection() { result = getAMongooseInstance().getMember("createConnection") }
|
||||
|
||||
/**
|
||||
* A Mongoose function.
|
||||
*/
|
||||
abstract private class MongooseFunction extends API::Node {
|
||||
/**
|
||||
* Gets the API-graph node for the result from this function (if the function returns a `Query`).
|
||||
*/
|
||||
abstract API::Node getQueryReturn();
|
||||
|
||||
/**
|
||||
* Holds if this function returns a `Query` that evaluates to one or
|
||||
* more Documents (`asArray` is false if it evaluates to a single
|
||||
* Document).
|
||||
*/
|
||||
abstract predicate returnsDocumentQuery(boolean asArray);
|
||||
|
||||
/**
|
||||
* Gets an argument that this function interprets as a query.
|
||||
*/
|
||||
abstract API::Node getQueryArgument();
|
||||
DataFlow::CallNode createConnection() {
|
||||
result = getAMongooseInstance().getAMemberCall("createConnection")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Model class
|
||||
* A Mongoose collection object.
|
||||
*/
|
||||
module Model {
|
||||
private class ModelFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
ModelFunction() { this = getModelObject().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a API-graph node referring to a Mongoose Model object.
|
||||
*/
|
||||
private API::Node getModelObject() {
|
||||
result = getAMongooseInstance().getMember("model").getReturn()
|
||||
or
|
||||
exists(API::Node conn | conn = createConnection().getReturn() |
|
||||
result = conn.getMember("model").getReturn() or
|
||||
result = conn.getMember("models").getAMember()
|
||||
)
|
||||
or
|
||||
result = API::Node::ofType("mongoose", "Model")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Model methods.
|
||||
*/
|
||||
module MethodSignatures {
|
||||
/**
|
||||
* Holds if Model method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// implement lots of the MongoDB collection interface
|
||||
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, n)
|
||||
or
|
||||
name = "find" + ["ById", "One"] + "AndUpdate" and n = 1
|
||||
or
|
||||
name in ["delete" + ["Many", "One"], "geoSearch", "remove", "replaceOne", "where"] and
|
||||
n = 0
|
||||
or
|
||||
name in [
|
||||
"find" + ["", "ById", "One"],
|
||||
"find" + ["ById", "One"] + "And" + ["Delete", "Remove", "Update"],
|
||||
"update" + ["", "Many", "One"]
|
||||
] and
|
||||
n = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Model method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
name =
|
||||
[
|
||||
"$where", "count", "findOne", "findOneAndDelete", "findOneAndRemove",
|
||||
"findOneAndReplace", "findOneAndUpdate", "geosearch", "remove", "replaceOne", "update",
|
||||
"updateMany", "countDocuments", "updateOne", "where", "deleteMany", "deleteOne", "find",
|
||||
"findById", "findByIdAndDelete", "findByIdAndRemove", "findByIdAndUpdate"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns a query that results in
|
||||
* one or more documents, the documents are wrapped in an array
|
||||
* if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocumentQuery(string name, boolean asArray) {
|
||||
asArray = false and name = "findOne"
|
||||
or
|
||||
asArray = true and name = "find"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Query class
|
||||
*/
|
||||
module Query {
|
||||
private class QueryFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
QueryFunction() { this = getAMongooseQuery().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NewQueryFunction extends MongooseFunction {
|
||||
NewQueryFunction() { this = getAMongooseInstance().getMember("Query") }
|
||||
|
||||
override API::Node getQueryReturn() { result = this.getInstance() }
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) { none() }
|
||||
|
||||
override API::Node getQueryArgument() { result = this.getParameter(2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose query object.
|
||||
*/
|
||||
API::Node getAMongooseQuery() {
|
||||
result = any(MongooseFunction f).getQueryReturn()
|
||||
or
|
||||
result = API::Node::ofType("mongoose", "Query")
|
||||
or
|
||||
result =
|
||||
getAMongooseQuery()
|
||||
.getMember(any(string name | MethodSignatures::returnsQuery(name)))
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Query methods.
|
||||
*/
|
||||
module MethodSignatures {
|
||||
/**
|
||||
* Holds if Query method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
n = 0 and
|
||||
name =
|
||||
[
|
||||
"and", "count", "findOneAndReplace", "findOneAndUpdate", "merge", "nor", "or", "remove",
|
||||
"replaceOne", "setQuery", "setUpdate", "update", "countDocuments", "updateMany",
|
||||
"updateOne", "where", "deleteMany", "deleteOne", "elemMatch", "find", "findOne",
|
||||
"findOneAndDelete", "findOneAndRemove"
|
||||
]
|
||||
or
|
||||
n = 1 and
|
||||
name = ["distinct", "findOneAndUpdate", "update", "updateMany", "updateOne"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Query method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
name =
|
||||
[
|
||||
"$where", "J", "comment", "count", "countDocuments", "distinct", "elemMatch", "equals",
|
||||
"error", "estimatedDocumentCount", "exists", "explain", "all", "find", "findById",
|
||||
"findOne", "findOneAndRemove", "findOneAndUpdate", "geometry", "get", "gt", "gte",
|
||||
"hint", "and", "in", "intersects", "lean", "limit", "lt", "lte", "map", "map",
|
||||
"maxDistance", "maxTimeMS", "batchsize", "maxscan", "mod", "ne", "near", "nearSphere",
|
||||
"nin", "or", "orFail", "polygon", "populate", "box", "read", "readConcern", "regexp",
|
||||
"remove", "select", "session", "set", "setOptions", "setQuery", "setUpdate", "center",
|
||||
"size", "skip", "slaveOk", "slice", "snapshot", "sort", "update", "w", "where",
|
||||
"within", "centerSphere", "wtimeout", "circle", "collation"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Query method `name` returns a query that results in
|
||||
* one or more documents, the documents are wrapped in an array
|
||||
* if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocumentQuery(string name, boolean asArray) {
|
||||
asArray = false and name = "findOne"
|
||||
or
|
||||
asArray = true and name = "find"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Document class
|
||||
*/
|
||||
module Document {
|
||||
private class DocumentFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
DocumentFunction() { this = getAMongooseDocument().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mongoose Document that is retrieved from the backing database.
|
||||
*/
|
||||
class RetrievedDocument extends API::Node {
|
||||
RetrievedDocument() {
|
||||
exists(boolean asArray, API::Node param |
|
||||
exists(MongooseFunction func |
|
||||
func.returnsDocumentQuery(asArray) and
|
||||
param = func.getLastParameter().getParameter(1)
|
||||
)
|
||||
or
|
||||
exists(API::Node f |
|
||||
f = Query::getAMongooseQuery().getMember("then") and
|
||||
param = f.getParameter(0).getParameter(0)
|
||||
or
|
||||
f = Query::getAMongooseQuery().getMember("exec") and
|
||||
param = f.getParameter(0).getParameter(1)
|
||||
|
|
||||
exists(DataFlow::MethodCallNode pred |
|
||||
// limitation: look at the previous method call
|
||||
Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and
|
||||
pred.getAMethodCall() = f.getACall()
|
||||
)
|
||||
)
|
||||
|
|
||||
asArray = false and this = param
|
||||
or
|
||||
asArray = true and
|
||||
// limitation: look for direct accesses
|
||||
this = param.getUnknownMember()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Document object.
|
||||
*/
|
||||
private API::Node getAMongooseDocument() {
|
||||
result instanceof RetrievedDocument
|
||||
or
|
||||
result = API::Node::ofType("mongoose", "Document")
|
||||
or
|
||||
result =
|
||||
getAMongooseDocument()
|
||||
.getMember(any(string name | MethodSignatures::returnsDocument(name)))
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
private module MethodSignatures {
|
||||
/**
|
||||
* Holds if Document method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::returnsQuery(name) or
|
||||
name = "replaceOne" or
|
||||
name = "update" or
|
||||
name = "updateOne"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::interpretsArgumentAsQuery(name, n)
|
||||
or
|
||||
n = 0 and
|
||||
(
|
||||
name = "replaceOne" or
|
||||
name = "update" or
|
||||
name = "updateOne"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns a query that results in
|
||||
* one or more documents, the documents are wrapped in an array
|
||||
* if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocumentQuery(string name, boolean asArray) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::returnsDocumentQuery(name, asArray)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns a Document.
|
||||
*/
|
||||
predicate returnsDocument(string name) {
|
||||
name = ["depopulate", "init", "populate", "overwrite"]
|
||||
}
|
||||
}
|
||||
class Model extends MongoDB::Collection {
|
||||
Model() { this = getAMongooseInstance().getAMemberCall("model") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,9 +160,7 @@ private module Mongoose {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop |
|
||||
this = createConnection().getParameter(3).getMember(prop).asSink().asExpr()
|
||||
|
|
||||
exists(string prop | this = createConnection().getOptionArgument(3, prop).asExpr() |
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "pass" and kind = "password"
|
||||
@@ -511,308 +169,4 @@ private module Mongoose {
|
||||
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a (part of a) MongoDB query.
|
||||
*/
|
||||
class MongoDBQueryPart extends NoSql::Query {
|
||||
MongooseFunction f;
|
||||
|
||||
MongoDBQueryPart() { this = f.getQueryArgument().asSink().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(f.getQueryArgument())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An evaluation of a MongoDB query.
|
||||
*/
|
||||
class ShorthandQueryEvaluation extends DatabaseAccess, DataFlow::InvokeNode {
|
||||
MongooseFunction f;
|
||||
|
||||
ShorthandQueryEvaluation() {
|
||||
this = f.getACall() and
|
||||
// shorthand for execution: provide a callback
|
||||
exists(f.getQueryReturn()) and
|
||||
exists(this.getCallback(this.getNumArgument() - 1))
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// NB: the complete information is not easily accessible for deeply chained calls
|
||||
f.getQueryArgument().asSink() = result
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(this.getNumArgument() - 1).getParameter(1)
|
||||
}
|
||||
}
|
||||
|
||||
class ExplicitQueryEvaluation extends DatabaseAccess, DataFlow::CallNode {
|
||||
string member;
|
||||
|
||||
ExplicitQueryEvaluation() {
|
||||
// explicit execution using a Query method call
|
||||
member = ["exec", "then", "catch"] and
|
||||
Query::getAMongooseQuery().getMember(member).getACall() = this
|
||||
}
|
||||
|
||||
private int resultParamIndex() {
|
||||
member = "then" and result = 0
|
||||
or
|
||||
member = "exec" and result = 1
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(_).getParameter(this.resultParamIndex())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// NB: the complete information is not easily accessible for deeply chained calls
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Minimongo library.
|
||||
*/
|
||||
private module Minimongo {
|
||||
/**
|
||||
* Provides signatures for the Collection methods.
|
||||
*/
|
||||
module CollectionMethodSignatures {
|
||||
/**
|
||||
* Holds if Collection method `name` interprets parameter `queryArgIdx` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int queryArgIdx) {
|
||||
// implements most of the MongoDB interface
|
||||
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, queryArgIdx)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a Minimongo query method. */
|
||||
private class QueryCall extends DatabaseAccess, API::CallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m |
|
||||
this =
|
||||
API::moduleImport("minimongo")
|
||||
.getAMember()
|
||||
.getReturn()
|
||||
.getAMember()
|
||||
.getMember(m)
|
||||
.getACall() and
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(queryArgIdx) }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(this.getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a Minimongo query.
|
||||
*/
|
||||
class Query extends NoSql::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the MarsDB library.
|
||||
*/
|
||||
private module MarsDB {
|
||||
private class MarsDBAccess extends DatabaseAccess, DataFlow::CallNode {
|
||||
string method;
|
||||
|
||||
MarsDBAccess() {
|
||||
this =
|
||||
API::moduleImport("marsdb")
|
||||
.getMember("Collection")
|
||||
.getInstance()
|
||||
.getMember(method)
|
||||
.getACall()
|
||||
}
|
||||
|
||||
string getMethod() { result = method }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
|
||||
/** A call to a MarsDB query method. */
|
||||
private class QueryCall extends MarsDBAccess, API::CallNode {
|
||||
int queryArgIdx;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m |
|
||||
this.getMethod() = m and
|
||||
// implements parts of the Minimongo interface
|
||||
Minimongo::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(this.getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MarsDB query.
|
||||
*/
|
||||
class Query extends NoSql::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the `Node Redis` library.
|
||||
*
|
||||
* Redis is an in-memory key-value store and not a database,
|
||||
* but `Node Redis` can be exploited similarly to a NoSQL database by giving a method an array as argument instead of a string.
|
||||
* As an example the below two invocations of `client.set` are equivalent:
|
||||
*
|
||||
* ```
|
||||
* const redis = require("redis");
|
||||
* const client = redis.createClient();
|
||||
* client.set("key", "value");
|
||||
* client.set(["key", "value"]);
|
||||
* ```
|
||||
*
|
||||
* ioredis is a very similar library. However, ioredis does not support array arguments in the same way, and is therefore not vulnerable to the same kind of type confusion.
|
||||
*/
|
||||
private module Redis {
|
||||
/**
|
||||
* Gets a `Node Redis` client.
|
||||
*/
|
||||
private API::Node client() {
|
||||
result = API::moduleImport("redis").getMember("createClient").getReturn()
|
||||
or
|
||||
result = API::moduleImport("redis").getMember("RedisClient").getInstance()
|
||||
or
|
||||
result = client().getMember("duplicate").getReturn()
|
||||
or
|
||||
result = client().getMember("duplicate").getLastParameter().getParameter(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a (possibly chained) reference to a batch operation object.
|
||||
* These have the same API as a redis client, except the calls are chained, and the sequence is terminated with a `.exec` call.
|
||||
*/
|
||||
private API::Node multi() {
|
||||
result = client().getMember(["multi", "batch"]).getReturn()
|
||||
or
|
||||
result = multi().getAMember().getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Node Redis` client instance. Either a client created using `createClient()`, or a batch operation object.
|
||||
*/
|
||||
private API::Node redis() { result = [client(), multi()] }
|
||||
|
||||
/**
|
||||
* Provides signatures for the query methods from Node Redis.
|
||||
*/
|
||||
module QuerySignatures {
|
||||
/**
|
||||
* Holds if `method` interprets parameter `argIndex` as a key, and a later parameter determines a value/field.
|
||||
* Thereby the method is vulnerable if parameter `argIndex` is unexpectedly an array instead of a string, as an attacker can control arguments to Redis that the attacker was not supposed to control.
|
||||
*
|
||||
* Only setters and similar methods are included.
|
||||
* For getter-like methods it is not generally possible to gain access "outside" of where you are supposed to have access,
|
||||
* it is at most possible to get a Redis call to return more results than expected (e.g. by adding more members to [`geohash`](https://redis.io/commands/geohash)).
|
||||
*/
|
||||
predicate argumentIsAmbiguousKey(string method, int argIndex) {
|
||||
method =
|
||||
[
|
||||
"set", "publish", "append", "bitfield", "decrby", "getset", "hincrby", "hincrbyfloat",
|
||||
"hset", "hsetnx", "incrby", "incrbyfloat", "linsert", "lpush", "lpushx", "lset", "ltrim",
|
||||
"rename", "renamenx", "rpushx", "setbit", "setex", "smove", "zincrby", "zinterstore",
|
||||
"hdel", "lpush", "pfadd", "rpush", "sadd", "sdiffstore", "srem"
|
||||
] and
|
||||
argIndex = 0
|
||||
or
|
||||
method = ["bitop", "hmset", "mset", "msetnx", "geoadd"] and
|
||||
argIndex in [0 .. any(DataFlow::InvokeNode invk).getNumArgument() - 1]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a key in a Node Redis call.
|
||||
*/
|
||||
class RedisKeyArgument extends NoSql::Query {
|
||||
RedisKeyArgument() {
|
||||
exists(string method, int argIndex |
|
||||
QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and
|
||||
this = redis().getMember(method).getParameter(argIndex).asSink().asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a database through redis
|
||||
*/
|
||||
class RedisDatabaseAccess extends DatabaseAccess, DataFlow::CallNode {
|
||||
RedisDatabaseAccess() { this = redis().getMember(_).getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the `ioredis` library.
|
||||
*
|
||||
* ```
|
||||
* import Redis from 'ioredis'
|
||||
* let client = new Redis(...)
|
||||
* ```
|
||||
*/
|
||||
private module IoRedis {
|
||||
/**
|
||||
* Gets an `ioredis` client.
|
||||
*/
|
||||
API::Node ioredis() { result = API::moduleImport("ioredis").getInstance() }
|
||||
|
||||
/**
|
||||
* An access to a database through ioredis
|
||||
*/
|
||||
class IoRedisDatabaseAccess extends DatabaseAccess, DataFlow::CallNode {
|
||||
IoRedisDatabaseAccess() { this = ioredis().getMember(_).getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,56 +493,11 @@ module NodeJSLib {
|
||||
*/
|
||||
module FS {
|
||||
/**
|
||||
* Gets a member `member` from module `fs` or its drop-in replacements `graceful-fs`, `fs-extra`, `original-fs`.
|
||||
* A member `member` from module `fs`.
|
||||
*/
|
||||
DataFlow::SourceNode moduleMember(string member) {
|
||||
result = fsModule(DataFlow::TypeTracker::end()).getAPropertyRead(member)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode fsModule(DataFlow::TypeTracker t) {
|
||||
exists(string moduleName |
|
||||
moduleName = ["mz/fs", "original-fs", "fs-extra", "graceful-fs", "fs"]
|
||||
|
|
||||
result = DataFlow::moduleImport(moduleName)
|
||||
or
|
||||
// extra support for flexible names
|
||||
result.asExpr().(Require).getArgument(0).mayHaveStringValue(moduleName)
|
||||
) and
|
||||
t.start()
|
||||
or
|
||||
t.start() and
|
||||
result = DataFlow::moduleMember("fs", "promises")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = fsModule(t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
t.continue() = t2 and
|
||||
exists(Promisify::PromisifyAllCall promisifyAllCall |
|
||||
result = promisifyAllCall and
|
||||
pred.flowsTo(promisifyAllCall.getArgument(0))
|
||||
)
|
||||
or
|
||||
// const fs = require('fs');
|
||||
// let fs_copy = methods.reduce((obj, method) => {
|
||||
// obj[method] = fs[method];
|
||||
// return obj;
|
||||
// }, {});
|
||||
t.continue() = t2 and
|
||||
exists(
|
||||
DataFlow::MethodCallNode call, DataFlow::ParameterNode obj, DataFlow::SourceNode method
|
||||
|
|
||||
call.getMethodName() = "reduce" and
|
||||
result = call and
|
||||
obj = call.getABoundCallbackParameter(0, 0) and
|
||||
obj.flowsTo(any(DataFlow::FunctionNode f).getAReturn()) and
|
||||
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
|
||||
write = obj.getAPropertyWrite() and
|
||||
method.flowsToExpr(write.getPropertyNameExpr()) and
|
||||
method.flowsToExpr(read.getPropertyNameExpr()) and
|
||||
read.getBase().getALocalSource() = fsModule(t2) and
|
||||
write.getRhs() = maybePromisified(read)
|
||||
)
|
||||
)
|
||||
exists(string moduleName | moduleName = ["fs"] |
|
||||
result = DataFlow::moduleMember(moduleName, member)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -553,7 +508,7 @@ module NodeJSLib {
|
||||
private class NodeJSFileSystemAccess extends FileSystemAccess, DataFlow::CallNode {
|
||||
string methodName;
|
||||
|
||||
NodeJSFileSystemAccess() { this = maybePromisified(FS::moduleMember(methodName)).getACall() }
|
||||
NodeJSFileSystemAccess() { this = FS::moduleMember(methodName).getACall() }
|
||||
|
||||
/**
|
||||
* Gets the name of the called method.
|
||||
|
||||
@@ -32,54 +32,38 @@ module SQL {
|
||||
* Provides classes modeling the (API compatible) `mysql` and `mysql2` packages.
|
||||
*/
|
||||
private module MySql {
|
||||
private string moduleName() { result = ["mysql", "mysql2", "mysql2/promise"] }
|
||||
private DataFlow::SourceNode mysql() { result = DataFlow::moduleImport(["mysql", "mysql2"]) }
|
||||
|
||||
/** Gets the package name `mysql` or `mysql2`. */
|
||||
API::Node mysql() { result = API::moduleImport(moduleName()) }
|
||||
private DataFlow::CallNode createPool() { result = mysql().getAMemberCall("createPool") }
|
||||
|
||||
/** Gets a reference to `mysql.createConnection`. */
|
||||
API::Node createConnection() {
|
||||
result = mysql().getMember(["createConnection", "createConnectionPromise"])
|
||||
/** Gets a reference to a MySQL pool. */
|
||||
private DataFlow::SourceNode pool(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = createPool()
|
||||
}
|
||||
|
||||
/** Gets a reference to `mysql.createPool`. */
|
||||
API::Node createPool() { result = mysql().getMember(["createPool", "createPoolCluster"]) }
|
||||
/** Gets a reference to a MySQL pool. */
|
||||
private DataFlow::SourceNode pool() { result = pool(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a node that contains a MySQL pool created using `mysql.createPool()`. */
|
||||
API::Node pool() {
|
||||
result = createPool().getReturn()
|
||||
or
|
||||
result = pool().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType(moduleName(), ["Pool", "PoolCluster"])
|
||||
}
|
||||
/** Gets a call to `mysql.createConnection`. */
|
||||
DataFlow::CallNode createConnection() { result = mysql().getAMemberCall("createConnection") }
|
||||
|
||||
/** Gets a data flow node that contains a freshly created MySQL connection instance. */
|
||||
API::Node connection() {
|
||||
result = createConnection().getReturn()
|
||||
or
|
||||
result = createConnection().getReturn().getPromised()
|
||||
or
|
||||
result = pool().getMember("getConnection").getParameter(0).getParameter(1)
|
||||
or
|
||||
result = pool().getMember("getConnection").getPromised()
|
||||
or
|
||||
exists(API::CallNode call |
|
||||
call = pool().getMember("on").getACall() and
|
||||
call.getArgument(0).getStringValue() = ["connection", "acquire", "release"] and
|
||||
result = call.getParameter(1).getParameter(0)
|
||||
/** Gets a reference to a MySQL connection instance. */
|
||||
private DataFlow::SourceNode connection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
(
|
||||
result = createConnection()
|
||||
or
|
||||
result = pool().getAMethodCall("getConnection").getABoundCallbackParameter(0, 1)
|
||||
)
|
||||
or
|
||||
result = API::Node::ofType(moduleName(), ["Connection", "PoolConnection"])
|
||||
}
|
||||
|
||||
/** Gets a reference to a MySQL connection instance. */
|
||||
DataFlow::SourceNode connection() { result = connection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to the MySql `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
exists(API::Node recv | recv = pool() or recv = connection() |
|
||||
this = recv.getMember(["query", "execute"]).getACall()
|
||||
)
|
||||
}
|
||||
QueryCall() { this = [pool(), connection()].getAMethodCall("query") }
|
||||
|
||||
override DataFlow::Node getAResult() { result = this.getCallback(_).getParameter(1) }
|
||||
|
||||
@@ -96,7 +80,7 @@ private module MySql {
|
||||
/** A call to the `escape` or `escapeId` method that performs SQL sanitization. */
|
||||
class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr {
|
||||
EscapingSanitizer() {
|
||||
this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall().asExpr() and
|
||||
this = [mysql(), pool(), connection()].getAMethodCall(["escape", "escapeId"]).asExpr() and
|
||||
input = this.getArgument(0) and
|
||||
output = this
|
||||
}
|
||||
@@ -107,9 +91,8 @@ private module MySql {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(API::Node callee, string prop |
|
||||
callee in [createConnection(), createPool()] and
|
||||
this = callee.getParameter(0).getMember(prop).asSink().asExpr() and
|
||||
exists(string prop |
|
||||
this = [createConnection(), createPool()].getOptionArgument(0, prop).asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -126,61 +109,23 @@ private module MySql {
|
||||
* Provides classes modeling the PostgreSQL packages, such as `pg` and `pg-promise`.
|
||||
*/
|
||||
private module Postgres {
|
||||
API::Node pg() {
|
||||
result = API::moduleImport("pg")
|
||||
or
|
||||
result = pgpMain().getMember("pg")
|
||||
}
|
||||
|
||||
/** Gets a reference to the `Client` constructor in the `pg` package, for example `require('pg').Client`. */
|
||||
API::Node newClient() { result = pg().getMember("Client") }
|
||||
|
||||
/** Gets a freshly created Postgres client instance. */
|
||||
API::Node client() {
|
||||
result = newClient().getInstance()
|
||||
or
|
||||
// pool.connect(function(err, client) { ... })
|
||||
result = pool().getMember("connect").getParameter(0).getParameter(1)
|
||||
or
|
||||
// await pool.connect()
|
||||
result = pool().getMember("connect").getReturn().getPromised()
|
||||
or
|
||||
result = pgpConnection().getMember("client")
|
||||
or
|
||||
exists(API::CallNode call |
|
||||
call = pool().getMember("on").getACall() and
|
||||
call.getArgument(0).getStringValue() = ["connect", "acquire"] and
|
||||
result = call.getParameter(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
result = client().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("pg", ["Client", "PoolClient"])
|
||||
}
|
||||
|
||||
/** Gets a constructor that when invoked constructs a new connection pool. */
|
||||
API::Node newPool() {
|
||||
/** Gets an expression that constructs a new connection pool. */
|
||||
DataFlow::InvokeNode newPool() {
|
||||
// new require('pg').Pool()
|
||||
result = pg().getMember("Pool")
|
||||
result = DataFlow::moduleImport("pg").getAConstructorInvocation("Pool")
|
||||
or
|
||||
// new require('pg-pool')
|
||||
result = API::moduleImport("pg-pool")
|
||||
result = DataFlow::moduleImport("pg-pool").getAnInstantiation()
|
||||
}
|
||||
|
||||
/** Gets an API node that refers to a connection pool. */
|
||||
API::Node pool() {
|
||||
result = newPool().getInstance()
|
||||
or
|
||||
result = pgpDatabase().getMember("$pool")
|
||||
or
|
||||
result = pool().getMember("on").getReturn()
|
||||
or
|
||||
result = API::Node::ofType("pg", "Pool")
|
||||
/** Gets a creation of a Postgres client. */
|
||||
DataFlow::InvokeNode newClient() {
|
||||
result = DataFlow::moduleImport("pg").getAConstructorInvocation("Client")
|
||||
}
|
||||
|
||||
/** A call to the Postgres `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [client(), pool()].getMember("query").getACall() }
|
||||
QueryCall() { this = [newClient(), newPool()].getAMethodCall("query") }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
this.getNumArgument() = 2 and
|
||||
@@ -209,11 +154,7 @@ private module Postgres {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop |
|
||||
this = [newClient(), newPool()].getParameter(0).getMember(prop).asSink().asExpr()
|
||||
or
|
||||
this = pgPromise().getParameter(0).getMember(prop).asSink().asExpr()
|
||||
|
|
||||
exists(string prop | this = [newClient(), newPool()].getOptionArgument(0, prop).asExpr() |
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "password" and kind = prop
|
||||
@@ -358,35 +299,30 @@ private module Postgres {
|
||||
*/
|
||||
private module Sqlite {
|
||||
/** Gets a reference to the `sqlite3` module. */
|
||||
API::Node sqlite() {
|
||||
result = API::moduleImport("sqlite3")
|
||||
DataFlow::SourceNode sqlite() {
|
||||
result = DataFlow::moduleImport("sqlite3")
|
||||
or
|
||||
result = sqlite().getMember("verbose").getReturn()
|
||||
result = sqlite().getAMemberCall("verbose")
|
||||
}
|
||||
|
||||
/** Gets an expression that constructs or returns a Sqlite database instance. */
|
||||
API::Node database() {
|
||||
/** Gets an expression that constructs a Sqlite database instance. */
|
||||
DataFlow::SourceNode newDb() {
|
||||
// new require('sqlite3').Database()
|
||||
result = sqlite().getMember("Database").getInstance()
|
||||
or
|
||||
// chained call
|
||||
result = getAChainingQueryCall()
|
||||
or
|
||||
result = API::Node::ofType("sqlite3", "Database")
|
||||
result = sqlite().getAConstructorInvocation("Database")
|
||||
}
|
||||
|
||||
/** Gets a call to a query method on a Sqlite database instance that returns the same instance. */
|
||||
private API::Node getAChainingQueryCall() {
|
||||
result = database().getMember(["all", "each", "exec", "get", "run"]).getReturn()
|
||||
/** Gets a data flow node referring to a Sqlite database instance. */
|
||||
private DataFlow::SourceNode db(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = newDb()
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a Sqlite database instance. */
|
||||
DataFlow::SourceNode db() { result = db(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to a Sqlite query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
this = getAChainingQueryCall().asSource()
|
||||
or
|
||||
this = database().getMember("prepare").getACall()
|
||||
}
|
||||
QueryCall() { this = db().getAMethodCall(["all", "each", "exec", "get", "prepare", "run"]) }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(1).getParameter(1) or
|
||||
@@ -401,201 +337,3 @@ private module Sqlite {
|
||||
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the `mssql` package.
|
||||
*/
|
||||
private module MsSql {
|
||||
/** Gets a reference to the `mssql` module. */
|
||||
API::Node mssql() { result = API::moduleImport("mssql") }
|
||||
|
||||
/** Gets a node referring to an instance of the given class. */
|
||||
API::Node mssqlClass(string name) {
|
||||
result = mssql().getMember(name).getInstance()
|
||||
or
|
||||
result = API::Node::ofType("mssql", name)
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a Request object. */
|
||||
API::Node request() {
|
||||
result = mssqlClass("Request")
|
||||
or
|
||||
result = request().getMember(["input", "replaceInput", "output", "replaceOutput"]).getReturn()
|
||||
or
|
||||
result = [transaction(), pool()].getMember("request").getReturn()
|
||||
}
|
||||
|
||||
/** Gets an API node referring to a Transaction object. */
|
||||
API::Node transaction() {
|
||||
result = mssqlClass("Transaction")
|
||||
or
|
||||
result = pool().getMember("transaction").getReturn()
|
||||
}
|
||||
|
||||
/** Gets a API node referring to a ConnectionPool object. */
|
||||
API::Node pool() { result = mssqlClass("ConnectionPool") }
|
||||
|
||||
/** A tagged template evaluated as a query. */
|
||||
private class QueryTemplateExpr extends DatabaseAccess, DataFlow::ValueNode, DataFlow::SourceNode {
|
||||
override TaggedTemplateExpr astNode;
|
||||
|
||||
QueryTemplateExpr() {
|
||||
mssql().getMember("query").getAValueReachableFromSource() =
|
||||
DataFlow::valueNode(astNode.getTag())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
result = DataFlow::valueNode(astNode.getTemplate().getAnElement())
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a MsSql query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [mssql(), request()].getMember(["query", "batch"]).getACall() }
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = this.getCallback(1).getParameter(1)
|
||||
or
|
||||
PromiseFlow::loadStep(this.getALocalUse(), result, Promises::valueProp())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/** An expression that is passed to a method that interprets it as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
exists(DatabaseAccess dba | dba instanceof QueryTemplateExpr or dba instanceof QueryCall |
|
||||
this = dba.getAQueryArgument().asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An element of a query template, which is automatically sanitized. */
|
||||
class QueryTemplateSanitizer extends SQL::SqlSanitizer {
|
||||
QueryTemplateSanitizer() {
|
||||
this = any(QueryTemplateExpr qte).getAQueryArgument().asExpr() and
|
||||
input = this and
|
||||
output = this
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that is passed as user name or password when creating a client or a pool. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(API::Node callee, string prop |
|
||||
(
|
||||
callee = mssql().getMember("connect")
|
||||
or
|
||||
callee = mssql().getMember("ConnectionPool")
|
||||
) and
|
||||
this = callee.getParameter(0).getMember(prop).asSink().asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "password" and kind = prop
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = kind }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the `sequelize` package.
|
||||
*/
|
||||
private module Sequelize {
|
||||
class SequelizeModel extends ModelInput::TypeModelCsv {
|
||||
override predicate row(string row) {
|
||||
// package1;type1;package2;type2;path
|
||||
row =
|
||||
[
|
||||
"sequelize;;sequelize-typescript;;", //
|
||||
"sequelize;Sequelize;sequelize;default;", //
|
||||
"sequelize;Sequelize;sequelize;;Instance",
|
||||
"sequelize;Sequelize;sequelize;;Member[Sequelize].Instance",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class SequelizeSink extends ModelInput::SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"sequelize;Sequelize;Member[query].Argument[0];sql-injection",
|
||||
"sequelize;Sequelize;Member[query].Argument[0].Member[query];sql-injection",
|
||||
"sequelize;;Member[literal,asIs].Argument[0];sql-injection",
|
||||
"sequelize;;Argument[1];credentials[user name]",
|
||||
"sequelize;;Argument[2];credentials[password]",
|
||||
"sequelize;;Argument[0..].Member[username];credentials[user name]",
|
||||
"sequelize;;Argument[0..].Member[password];credentials[password]"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class SequelizeSource extends ModelInput::SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row = "sequelize;Sequelize;Member[query].ReturnValue.Awaited;database-access-result"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private module SpannerCsv {
|
||||
class SpannerTypes extends ModelInput::TypeModelCsv {
|
||||
override predicate row(string row) {
|
||||
// package1; type1; package2; type2; path
|
||||
row =
|
||||
[
|
||||
"@google-cloud/spanner;;@google-cloud/spanner;;Member[Spanner]",
|
||||
"@google-cloud/spanner;Database;@google-cloud/spanner;;ReturnValue.Member[instance].ReturnValue.Member[database].ReturnValue",
|
||||
"@google-cloud/spanner;v1.SpannerClient;@google-cloud/spanner;;Member[v1].Member[SpannerClient].Instance",
|
||||
"@google-cloud/spanner;Transaction;@google-cloud/spanner;Database;Member[runTransaction,runTransactionAsync,getTransaction].Argument[0..1].Parameter[1]",
|
||||
"@google-cloud/spanner;Transaction;@google-cloud/spanner;Database;Member[getTransaction].ReturnValue.Awaited",
|
||||
"@google-cloud/spanner;Snapshot;@google-cloud/spanner;Database;Member[getSnapshot].Argument[0..1].Parameter[1]",
|
||||
"@google-cloud/spanner;Snapshot;@google-cloud/spanner;Database;Member[getSnapshot].ReturnValue.Awaited",
|
||||
"@google-cloud/spanner;BatchTransaction;@google-cloud/spanner;Database;Member[batchTransaction].ReturnValue",
|
||||
"@google-cloud/spanner;BatchTransaction;@google-cloud/spanner;Database;Member[createBatchTransaction].ReturnValue.Awaited",
|
||||
"@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;Database;Member[run,runPartitionedUpdate,runStream]",
|
||||
"@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;Transaction;Member[run,runStream,runUpdate]",
|
||||
"@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;BatchTransaction;Member[createQueryPartitions]",
|
||||
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;v1.SpannerClient;",
|
||||
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;Database;",
|
||||
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;Transaction;",
|
||||
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;Snapshot;",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class SpannerSinks extends ModelInput::SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
// package; type; path; kind
|
||||
row =
|
||||
[
|
||||
"@google-cloud/spanner;~SqlExecutorDirect;Argument[0];sql-injection",
|
||||
"@google-cloud/spanner;~SqlExecutorDirect;Argument[0].Member[sql];sql-injection",
|
||||
"@google-cloud/spanner;Transaction;Member[batchUpdate].Argument[0];sql-injection",
|
||||
"@google-cloud/spanner;Transaction;Member[batchUpdate].Argument[0].ArrayElement.Member[sql];sql-injection",
|
||||
"@google-cloud/spanner;v1.SpannerClient;Member[executeSql,executeStreamingSql].Argument[0].Member[sql];sql-injection",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class SpannerSources extends ModelInput::SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"@google-cloud/spanner;~SpannerObject;Member[executeSql].Argument[0..].Parameter[1];database-access-result",
|
||||
"@google-cloud/spanner;~SpannerObject;Member[executeSql].ReturnValue.Awaited.Member[0];database-access-result",
|
||||
"@google-cloud/spanner;~SpannerObject;Member[run].ReturnValue.Awaited;database-access-result",
|
||||
"@google-cloud/spanner;~SpannerObject;Member[run].Argument[0..].Parameter[1];database-access-result",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,25 +36,8 @@ module ParseTorrent {
|
||||
/**
|
||||
* An access to user-controlled torrent information.
|
||||
*/
|
||||
class UserControlledTorrentInfo extends RemoteFlowSource instanceof DataFlow::PropRead {
|
||||
UserControlledTorrentInfo() {
|
||||
exists(API::Node read |
|
||||
read = any(ParsedTorrent t).asApiNode().getAMember() and
|
||||
this = read.asSource()
|
||||
|
|
||||
exists(string prop |
|
||||
not (
|
||||
prop = "private" or
|
||||
prop = "infoHash" or
|
||||
prop = "length"
|
||||
// "pieceLength" and "lastPieceLength" are not guaranteed to be numbers as of commit ae3ad15d
|
||||
) and
|
||||
super.getPropertyName() = prop
|
||||
)
|
||||
or
|
||||
not exists(super.getPropertyName())
|
||||
)
|
||||
}
|
||||
class UserControlledTorrentInfo extends RemoteFlowSource {
|
||||
UserControlledTorrentInfo() { none() }
|
||||
|
||||
override string getSourceType() { result = "torrent information" }
|
||||
}
|
||||
|
||||
@@ -429,8 +429,6 @@ module JQuery {
|
||||
private DataFlow::SourceNode dollar(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = dollarSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = dollar(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,14 +462,6 @@ module JQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `this` node in a JQuery plugin function, which is a JQuery object.
|
||||
*/
|
||||
private class JQueryPluginThisObject extends Range {
|
||||
JQueryPluginThisObject() {
|
||||
this = DataFlow::thisNode(any(JQueryPluginMethod method).getFunction())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a source of jQuery objects from the AST-based `JQueryObject` class. */
|
||||
|
||||
@@ -55,7 +55,7 @@ module TaintedPath {
|
||||
* There are currently four flow labels, representing the different combinations of
|
||||
* normalization and absoluteness.
|
||||
*/
|
||||
abstract class PosixPath extends DataFlow::FlowLabel {
|
||||
class PosixPath extends DataFlow::FlowLabel {
|
||||
Normalization normalization;
|
||||
Relativeness relativeness;
|
||||
|
||||
@@ -113,7 +113,7 @@ module TaintedPath {
|
||||
/**
|
||||
* A flow label representing an array of path elements that may include "..".
|
||||
*/
|
||||
abstract class SplitPath extends DataFlow::FlowLabel {
|
||||
class SplitPath extends DataFlow::FlowLabel {
|
||||
SplitPath() { this = "splitPath" }
|
||||
}
|
||||
}
|
||||
@@ -218,12 +218,12 @@ module TaintedPath {
|
||||
output = this
|
||||
or
|
||||
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
|
||||
this instanceof StringReplaceCall and
|
||||
input = this.getReceiver() and
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
not exists(RegExpLiteral literal, RegExpTerm term |
|
||||
this.(StringReplaceCall).getRegExp().asExpr() = literal and
|
||||
this.(StringReplaceCall).isGlobal() and
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
literal.isGlobal() and
|
||||
literal.getRoot() = term
|
||||
|
|
||||
term.getAMatchedString() = "/" or
|
||||
@@ -247,15 +247,16 @@ module TaintedPath {
|
||||
/**
|
||||
* A call that removes all instances of "../" in the prefix of the string.
|
||||
*/
|
||||
class DotDotSlashPrefixRemovingReplace extends StringReplaceCall {
|
||||
class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
DotDotSlashPrefixRemovingReplace() {
|
||||
input = this.getReceiver() and
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
exists(RegExpLiteral literal, RegExpTerm term |
|
||||
this.getRegExp().asExpr() = literal and
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
(term instanceof RegExpStar or term instanceof RegExpPlus) and
|
||||
term.getChild(0) = getADotDotSlashMatcher()
|
||||
|
|
||||
@@ -297,16 +298,17 @@ module TaintedPath {
|
||||
/**
|
||||
* A call that removes all "." or ".." from a path, without also removing all forward slashes.
|
||||
*/
|
||||
class DotRemovingReplaceCall extends StringReplaceCall {
|
||||
class DotRemovingReplaceCall extends DataFlow::CallNode {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
DotRemovingReplaceCall() {
|
||||
input = this.getReceiver() and
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
this.isGlobal() and
|
||||
exists(RegExpLiteral literal, RegExpTerm term |
|
||||
this.getRegExp().asExpr() = literal and
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
literal.isGlobal() and
|
||||
literal.getRoot() = term and
|
||||
not term.getAMatchedString() = "/"
|
||||
|
|
||||
@@ -622,8 +624,6 @@ module TaintedPath {
|
||||
(
|
||||
this = fileSystemAccess.getAPathArgument() and
|
||||
not exists(fileSystemAccess.getRootPathArgument())
|
||||
or
|
||||
this = fileSystemAccess.getRootPathArgument()
|
||||
) and
|
||||
not this = any(ResolvingPathCall call).getInput()
|
||||
}
|
||||
@@ -664,74 +664,6 @@ module TaintedPath {
|
||||
AngularJSTemplateUrlSink() { this = any(AngularJS::CustomDirective d).getMember("templateUrl") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The path argument of a [send](https://www.npmjs.com/package/send) call, viewed as a sink.
|
||||
*/
|
||||
class SendPathSink extends Sink, DataFlow::ValueNode {
|
||||
SendPathSink() { this = DataFlow::moduleImport("send").getACall().getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A path argument given to a `Page` in puppeteer, specifying where a pdf/screenshot should be saved.
|
||||
*/
|
||||
private class PuppeteerPath extends TaintedPath::Sink {
|
||||
PuppeteerPath() {
|
||||
this =
|
||||
Puppeteer::page()
|
||||
.getMember(["pdf", "screenshot"])
|
||||
.getParameter(0)
|
||||
.getMember("path")
|
||||
.asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument given to the `prettier` library specifying the location of a config file.
|
||||
*/
|
||||
private class PrettierFileSink extends TaintedPath::Sink {
|
||||
PrettierFileSink() {
|
||||
this =
|
||||
API::moduleImport("prettier")
|
||||
.getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
|
||||
.getACall()
|
||||
.getArgument(0)
|
||||
or
|
||||
this =
|
||||
API::moduleImport("prettier")
|
||||
.getMember("resolveConfig")
|
||||
.getACall()
|
||||
.getParameter(1)
|
||||
.getMember("config")
|
||||
.asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `cwd` option for the `read-pkg` library.
|
||||
*/
|
||||
private class ReadPkgCwdSink extends TaintedPath::Sink {
|
||||
ReadPkgCwdSink() {
|
||||
this =
|
||||
API::moduleImport("read-pkg")
|
||||
.getMember(["readPackageAsync", "readPackageSync"])
|
||||
.getParameter(0)
|
||||
.getMember("cwd")
|
||||
.asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `cwd` option to a shell execution.
|
||||
*/
|
||||
private class ShellCwdSink extends TaintedPath::Sink {
|
||||
ShellCwdSink() {
|
||||
exists(SystemCommandExecution sys, API::Node opts |
|
||||
opts.asSink() = sys.getOptionsArg() and // assuming that an API::Node exists here.
|
||||
this = opts.getMember("cwd").asSink()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user