mirror of
https://github.com/github/codeql.git
synced 2026-06-15 18:01:10 +02:00
Merge branch 'main' into codeql-ci/js/ml-powered-pack-release-0.3.5
This commit is contained in:
@@ -1,227 +0,0 @@
|
||||
Summary-based information flow analysis
|
||||
=======================================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
This document presents an approach for running information flow analyses (such as the standard
|
||||
security queries) on an application that depends on one or more npm packages. Instead of
|
||||
installing the npm packages during the snapshot build and analyzing them together with application
|
||||
code, we analyze each package in isolation and compute *flow summaries* that record information
|
||||
about any sources, sinks and flow steps contributed by the package's API. These flow summaries
|
||||
are then imported when building a snapshot of the application (usually in the form of CSV files
|
||||
added as external data), and are picked up by the standard security queries, allowing them to reason
|
||||
about flow into, out of and through the npm packages as though they had been included as part of the
|
||||
build.
|
||||
|
||||
Note that flow summaries are an experimental technology, and not ready to be used in production
|
||||
queries or libraries. Also note that flow summaries do not currently work with CodeQL, but require
|
||||
the legacy Semmle Core toolchain.
|
||||
|
||||
Motivating example
|
||||
------------------
|
||||
|
||||
Let us take the `mkdirp <https://www.npmjs.com/package/mkdirp>`_ package as an example. It exports
|
||||
a function that takes as its first argument a file system path, and creates a folder with that
|
||||
path, as well as any parent folders that do not exist yet. As further arguments, the function
|
||||
accepts an optional configuration object and a callback to invoke once the folder has been
|
||||
created.
|
||||
|
||||
An application might use this package as follows:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
const mkdirp = require('mkdirp');
|
||||
// ...
|
||||
mkdirp(p, opts, function cb(err) {
|
||||
// ...
|
||||
});
|
||||
|
||||
If the value of ``p`` can be controlled by an untrusted user, this would allow them to create arbitrary
|
||||
folders, which may not be desirable.
|
||||
|
||||
By analyzing the application code base together with the source code for the ``mkdirp`` package,
|
||||
the default path injection analysis would be able to track taint through the call to ``mkdirp`` into its
|
||||
implementation, which ultimately uses built-in Node.js file system APIs to create the folder. Since
|
||||
the path injection analysis has built-in models of these APIs it would then be able to spot and flag this
|
||||
vulnerability.
|
||||
|
||||
However, analyzing ``mkdirp`` from scratch for every client application is wasteful. Moreover, it would
|
||||
in this case be undesirable to flag the location inside ``mkdirp`` where the folder is actually created
|
||||
as part of the alert: the developer of the client application did not write that code and hence will
|
||||
have a hard time understanding why it is being flagged.
|
||||
|
||||
Both of these concerns can be addressed by treating the first argument to ``mkdirp`` as a path injection
|
||||
sink in its own right: the analysis no longer needs to track flow into the implementation of ``mkdirp``,
|
||||
so we would no longer need to include its source code in the analysis, and the alert would flag the call
|
||||
to ``mkdirp`` in application code, not its implementation in library code.
|
||||
|
||||
The information that the first parameter of ``mkdirp`` is interpreted as a file system path and hence should
|
||||
be considered a path injection sink is an example of a *flow summary*, or more precisely a *sink summary*.
|
||||
Besides sink summaries, we also consider *source summaries* and *flow-step summaries*.
|
||||
|
||||
In general, a sink summary states that some API interface point (such as a function parameter) should
|
||||
be considered a sink for a certain analysis, so if data from a known source reaches this point without
|
||||
undergoing appropriate sanitization, it should be flagged with an alert. A sink summary may also
|
||||
specify which taint kind the data needs to have in order for the sink to be problematic.
|
||||
|
||||
Conversely, a source summary identifies some API (such as the return value of a function) as a source
|
||||
of tainted data for a certain analysis, again optionally specifying a taint kind.
|
||||
|
||||
Finally, a flow-step summary records the fact that data that flows into the package at some point
|
||||
may propagate to another point (for example, from a function parameter to its return value).
|
||||
In this case, there are two relevant taint kinds, one describing the kind of taint data has that
|
||||
enters, and one describing the taint of the data that emerges. In general, flow steps (like sources
|
||||
and sinks) are analysis-specific, since we need to know about sanitizers.
|
||||
|
||||
In what follows we will first discuss how summaries are generated from a snapshot of an npm package,
|
||||
and then how they are imported when analyzing client code. Finally, we will discuss the format in which
|
||||
flow summaries are stored.
|
||||
|
||||
Note that flow summaries are considered an experimental feature at this point. Using them involves
|
||||
some manual configuration, and we make no guarantee that the API will remain stable.
|
||||
|
||||
Generating summaries
|
||||
--------------------
|
||||
|
||||
Flow summaries of an npm package can be generated by running special summary extraction queries
|
||||
either on a snapshot of the package itself, or on a snapshot of a hand-written model of the
|
||||
package. (Note that this requires a working installation of Semmle Core.)
|
||||
|
||||
There are three default summary extraction queries:
|
||||
|
||||
- Extract flow step summaries (``js/step-summary-extraction``,
|
||||
``experimental/Summaries/ExtractSourceSummaries.ql``)
|
||||
- Extract sink summaries (``js/sink-summary-extraction``,
|
||||
``experimental/Summaries/ExtractSinkSummaries.ql``)
|
||||
- Extract source summaries (``js/source-summary-extraction``,
|
||||
``experimental/Summaries/ExtractSourceSummaries.ql``)
|
||||
|
||||
You can run these queries individually against a snapshot of the npm package you want to create
|
||||
flow summaries for using ``odasa runQuery``, and store the output as CSV files named
|
||||
``additional-steps.csv``, ``additional-sinks.csv`` and ``additional-sources.csv``, respectively.
|
||||
|
||||
For example, assuming that folder ``mkdirp-snapshot`` contains a snapshot of the ``mkdirp``
|
||||
project, we can extract sink summaries using the command
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
odasa runQuery \
|
||||
--query $SEMMLE_DIST/queries/semmlecode-javascript-queries/experimental/Summaries/ExtractSinkSummaries.ql \
|
||||
--output-file additional-sinks.csv --snapshot mkdirp-snapshot
|
||||
|
||||
|
||||
Instead of generating summaries directly from the package source code, you can also generate
|
||||
them from a hand-written model of the package. The model should contain a ``package.json`` file
|
||||
giving the correct package name, and models for the relevant API entry points. The models are
|
||||
plain JavaScript with special comments annotating certain expressions as sources or sinks.
|
||||
|
||||
For example, a model of ``mkdirp`` might look like this:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
module.exports = function mkdirp(path) {
|
||||
path /* Semmle: sink: taint, TaintedPath */
|
||||
};
|
||||
|
||||
Annotation comments start with ``Semmle:``, and contain ``source`` and ``sink`` specifications.
|
||||
Each such specification lists a flow label (in this case, ``taint``) and a configuration to which
|
||||
the specification applies (in this case, ``TaintedPath``).
|
||||
|
||||
A source specification annotates an expression as being a source of flow with the given label
|
||||
for the purposes of the given configuration, and similar for sinks. Annotation comments apply to
|
||||
any expression (and more generally any data flow node) whose source location ends on the line
|
||||
where the comment starts.
|
||||
|
||||
Using summaries
|
||||
---------------
|
||||
|
||||
Once you have created summaries using the approach outlined above, you have two options for
|
||||
including them in the analysis of a client application.
|
||||
|
||||
External data
|
||||
:::::::::::::
|
||||
|
||||
Firstly, you can include the CSV files generated by running the extraction queries as external
|
||||
data when building a snapshot of the client application by copying them into the
|
||||
``$snapshot/external/data`` folder. This is typically done by including a command like this
|
||||
in your ``project`` file:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<build>cp /path/to/additional-sinks.csv ${snapshot}/external/data</build>
|
||||
|
||||
If you want to include summaries for multiple libraries, you have to concatenate the
|
||||
corresponding CSV files before copying them into the external data folder.
|
||||
|
||||
Additionally, you need to import the library ``Security.Summaries.ImportFromCsv`` in your
|
||||
``javascript.qll``, which will pick up the summaries from external data and interpret them
|
||||
as additional sources, sinks and flow steps:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import Security.Summaries.ImportFromCsv
|
||||
|
||||
After these preparatory steps, you can run your analysis without any further changes.
|
||||
|
||||
External predicates
|
||||
:::::::::::::::::::
|
||||
|
||||
The second method for including flow summaries is by including the
|
||||
``Security.Summaries.ImportFromExternalPredicates`` library in your analysis, which declares
|
||||
three external predicates ``additionalSteps``, ``additionalSinks`` and ``additionalSources`` that
|
||||
need to be instantiated with the flow summary CSV data.
|
||||
|
||||
This is most easily done in QL for Eclipse, which will prompt you for CSV files to populate
|
||||
the three predicates.
|
||||
|
||||
This approach has the advantage that you do not need to include the CSV files during the
|
||||
snapshot build, so you can use an existing snapshot, for example as downloaded from LGTM.com.
|
||||
|
||||
Summary format
|
||||
--------------
|
||||
|
||||
Source and sink summaries are specified as tuples of the form ``(portal, kind, configuration)``,
|
||||
where ``portal`` is a description of the API element being marked as a source or sink, ``kind``
|
||||
is a flow label (also known as "taint kind") describing the kind of information being generated
|
||||
or consumed, and ``configuration`` specifies which flow configuration the summary applies to.
|
||||
|
||||
If ``kind`` is empty, it defaults to ``data`` for sources and either ``data`` or ``taint`` for sinks.
|
||||
If ``configuration`` is empty, the specification applies to all configurations.
|
||||
The default extraction queries never produce empty ``kind`` or ``configuration`` columns.
|
||||
|
||||
Similarly, step summaries are tuples of the form
|
||||
``(inPortal, inKind, outPortal, outKind, configuration)``, stating that information with label
|
||||
``inKind`` that flows into ``inPortal`` resurfaces from ``outPortal``, now having kind ``outKind``.
|
||||
As before, ``configuration`` specifies which configuration this information applies to.
|
||||
|
||||
In all of the above, ``portal`` is an S-expression that abstractly describes a *portal*, that is,
|
||||
an API interface point by which data may enter or leave the npm package being analyzed.
|
||||
|
||||
Currently, we model five kinds of portals:
|
||||
|
||||
- ``(root <uri>)``, representing the ``module`` object of the main module of the npm package
|
||||
described by ``<uri>``, which is a URL of the form ``https://www.npmjs.com/package/<pkg>``;
|
||||
- ``(member <name> <base>)``, representing property ``<name>`` of an object described by
|
||||
portal ``<base>``;
|
||||
- ``(instance <base>)``, representing an instance of a (constructor) function or class
|
||||
described by portal ``base``;
|
||||
- ``(parameter <i> <base>)``, representing the ``i`` th parameter of a function described by
|
||||
portal ``base``;
|
||||
- ``(return <base>)``, representing the return value of a function described by portal ``base``.
|
||||
|
||||
In our example above, the first parameter of the default export of package ``mkdirp`` is
|
||||
described by the portal
|
||||
|
||||
.. code-block:: lisp
|
||||
|
||||
(parameter 0 (member default (root https://www.npmjs.com/package/mkdirp))
|
||||
|
||||
As a more complicated example,
|
||||
|
||||
.. code-block:: lisp
|
||||
|
||||
(parameter 0 (parameter 1 (member then (instance (member Promise (root https://www.npmjs.com/package/bluebird))))))
|
||||
|
||||
describes the first parameter of a function passed as second argument to the ``then`` method of
|
||||
the ``Promise`` constructor exported by package ``bluebird``.
|
||||
@@ -0,0 +1,11 @@
|
||||
class AstNodeWithSymbol extends @ast_node_with_symbol {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Symbol extends @symbol {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AstNodeWithSymbol node, Symbol symbol
|
||||
where ast_node_symbol(node, symbol) and not node instanceof @external_module_declaration
|
||||
select node, symbol
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
description: Associate symbols with external module declarations
|
||||
compatibility: backwards
|
||||
|
||||
ast_node_symbol.rel: run ast_node_symbol.qlo
|
||||
1217
javascript/downgrades/initial/semmlecode.javascript.dbscheme
Normal file
1217
javascript/downgrades/initial/semmlecode.javascript.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
4
javascript/downgrades/qlpack.yml
Normal file
4
javascript/downgrades/qlpack.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: codeql/javascript-downgrades
|
||||
groups: javascript
|
||||
downgrades: .
|
||||
library: true
|
||||
@@ -1696,4 +1696,3 @@ module.exports.R_OK = fs.R_OK;
|
||||
module.exports.W_OK = fs.W_OK;
|
||||
|
||||
module.exports.X_OK = fs.X_OK;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "typescript-parser-wrapper",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"typescript": "4.6.2"
|
||||
"typescript": "4.8.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.json",
|
||||
|
||||
@@ -168,20 +168,9 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
|
||||
}
|
||||
}
|
||||
|
||||
// Number of conditional type expressions the visitor is currently inside.
|
||||
// We disable type extraction inside such type expressions, to avoid complications
|
||||
// with `infer` types.
|
||||
let insideConditionalTypes = 0;
|
||||
|
||||
visitAstNode(ast);
|
||||
function visitAstNode(node: AugmentedNode) {
|
||||
if (node.kind === ts.SyntaxKind.ConditionalType) {
|
||||
++insideConditionalTypes;
|
||||
}
|
||||
ts.forEachChild(node, visitAstNode);
|
||||
if (node.kind === ts.SyntaxKind.ConditionalType) {
|
||||
--insideConditionalTypes;
|
||||
}
|
||||
|
||||
// fill in line/column info
|
||||
if ("pos" in node) {
|
||||
@@ -202,7 +191,7 @@ export function augmentAst(ast: AugmentedSourceFile, code: string, project: Proj
|
||||
}
|
||||
}
|
||||
|
||||
if (typeChecker != null && insideConditionalTypes === 0) {
|
||||
if (typeChecker != null) {
|
||||
if (isTypedNode(node)) {
|
||||
let contextualType = isContextuallyTypedNode(node)
|
||||
? typeChecker.getContextualType(node)
|
||||
|
||||
@@ -241,7 +241,7 @@ const astProperties: string[] = [
|
||||
"constructor",
|
||||
"declarationList",
|
||||
"declarations",
|
||||
"decorators",
|
||||
"illegalDecorators",
|
||||
"default",
|
||||
"delete",
|
||||
"dotDotDotToken",
|
||||
@@ -670,6 +670,12 @@ function handleOpenProjectCommand(command: OpenProjectCommand) {
|
||||
if (file.endsWith(".d.ts")) {
|
||||
return pathlib.basename(file, ".d.ts");
|
||||
}
|
||||
if (file.endsWith(".d.mts") || file.endsWith(".d.cts")) {
|
||||
// We don't extract d.mts or d.cts files, but their symbol can coincide with that of a d.ts file,
|
||||
// which means any module bindings we generate for it will ultimately be visible in QL.
|
||||
let base = pathlib.basename(file);
|
||||
return base.substring(0, base.length - '.d.mts'.length);
|
||||
}
|
||||
let base = pathlib.basename(file);
|
||||
let dot = base.lastIndexOf('.');
|
||||
return dot === -1 || dot === 0 ? base : base.substring(0, dot);
|
||||
|
||||
@@ -947,7 +947,7 @@ export class TypeTable {
|
||||
* Returns a unique string for the given call/constructor signature.
|
||||
*/
|
||||
private getSignatureString(kind: ts.SignatureKind, signature: AugmentedSignature): string {
|
||||
let modifiers : ts.ModifiersArray = signature.getDeclaration()?.modifiers;
|
||||
let modifiers = signature.getDeclaration()?.modifiers;
|
||||
let isAbstract = modifiers && modifiers.filter(modifier => modifier.kind == ts.SyntaxKind.AbstractKeyword).length > 0
|
||||
|
||||
let parameters = signature.getParameters();
|
||||
@@ -1068,6 +1068,7 @@ export class TypeTable {
|
||||
let superType = this.typeChecker.getTypeFromTypeNode(typeExpr);
|
||||
if (superType == null) continue;
|
||||
let baseTypeSymbol = superType.symbol;
|
||||
baseTypeSymbol = (baseTypeSymbol as any)?.type?.symbol ?? baseTypeSymbol;
|
||||
if (baseTypeSymbol == null) continue;
|
||||
let baseId = this.getSymbolId(baseTypeSymbol);
|
||||
// Note: take care not to perform a recursive call between the two `push` calls.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
version "12.7.11"
|
||||
resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446
|
||||
|
||||
typescript@4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4"
|
||||
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==
|
||||
typescript@4.8.2:
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790"
|
||||
integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==
|
||||
|
||||
@@ -448,7 +448,11 @@ public class ESNextParser extends JSXParser {
|
||||
protected Statement parseForStatement(Position startLoc) {
|
||||
int startPos = this.start;
|
||||
boolean isAwait = false;
|
||||
if (this.inAsync && this.eatContextual("await")) isAwait = true;
|
||||
if (this.inAsync || (options.esnext() && !this.inFunction)) {
|
||||
if (this.eatContextual("await")) {
|
||||
isAwait = true;
|
||||
}
|
||||
}
|
||||
Statement forStmt = super.parseForStatement(startLoc);
|
||||
if (isAwait) {
|
||||
if (forStmt instanceof ForOfStatement) ((ForOfStatement) forStmt).setAwait(true);
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Options {
|
||||
}
|
||||
|
||||
private boolean allowHashBang, allowReturnOutsideFunction, allowImportExportEverywhere, allowGeneratedCodeExprs;
|
||||
private boolean preserveParens, mozExtensions, jscript, esnext, v8Extensions, e4x;
|
||||
private boolean preserveParens, mozExtensions, jscript, esnext, v8Extensions, e4x, allowFlowTypes;
|
||||
private int ecmaVersion;
|
||||
private AllowReserved allowReserved;
|
||||
private String sourceType;
|
||||
@@ -70,6 +70,7 @@ public class Options {
|
||||
this.v8Extensions = false;
|
||||
this.e4x = false;
|
||||
this.onRecoverableError = null;
|
||||
this.allowFlowTypes = false;
|
||||
}
|
||||
|
||||
public Options(Options that) {
|
||||
@@ -92,6 +93,7 @@ public class Options {
|
||||
this.onComment = that.onComment;
|
||||
this.program = that.program;
|
||||
this.onRecoverableError = that.onRecoverableError;
|
||||
this.allowFlowTypes = that.allowFlowTypes;
|
||||
}
|
||||
|
||||
public boolean allowHashBang() {
|
||||
@@ -130,6 +132,10 @@ public class Options {
|
||||
return v8Extensions;
|
||||
}
|
||||
|
||||
public boolean allowFlowTypes() {
|
||||
return allowFlowTypes;
|
||||
}
|
||||
|
||||
public boolean e4x() {
|
||||
return e4x;
|
||||
}
|
||||
@@ -202,6 +208,10 @@ public class Options {
|
||||
this.v8Extensions = v8Extensions;
|
||||
}
|
||||
|
||||
public void allowFlowTypes(boolean allowFlowTypes) {
|
||||
this.allowFlowTypes = allowFlowTypes;
|
||||
}
|
||||
|
||||
public void e4x(boolean e4x) {
|
||||
this.e4x = e4x;
|
||||
}
|
||||
|
||||
@@ -830,7 +830,7 @@ public class FlowParser extends ESNextParser {
|
||||
|
||||
/** Should Flow syntax be allowed? */
|
||||
private boolean flow() {
|
||||
return options.esnext();
|
||||
return options.allowFlowTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -141,8 +141,9 @@ public class Fetcher {
|
||||
entryPath = entryPath.subpath(1, entryPath.getNameCount());
|
||||
|
||||
String filename = entryPath.getFileName().toString();
|
||||
if (!filename.endsWith(".d.ts") && !filename.equals("package.json")) {
|
||||
continue; // Only extract .d.ts files and package.json
|
||||
if (!filename.endsWith(".d.ts") && !filename.endsWith(".d.mts") && !filename.endsWith(".d.cts")
|
||||
&& !filename.equals("package.json")) {
|
||||
continue; // Only extract .d.ts, .d.mts, .d.cts files, and package.json
|
||||
}
|
||||
relativePaths.add(entryPath);
|
||||
Path outputFile = destDir.resolve(entryPath);
|
||||
|
||||
@@ -590,7 +590,7 @@ public class ASTExtractor {
|
||||
trapwriter.addTuple("literals", valueString, source, key);
|
||||
Position start = nd.getLoc().getStart();
|
||||
com.semmle.util.locations.Position startPos = new com.semmle.util.locations.Position(start.getLine(), start.getColumn() + 1 /* Convert from 0-based to 1-based. */, start.getOffset());
|
||||
|
||||
|
||||
if (nd.isRegExp()) {
|
||||
OffsetTranslation offsets = new OffsetTranslation();
|
||||
offsets.set(0, 1); // skip the initial '/'
|
||||
@@ -622,7 +622,7 @@ public class ASTExtractor {
|
||||
/**
|
||||
* Constant-folds simple string concatenations in `exp` while keeping an offset translation
|
||||
* that tracks back to the original source.
|
||||
*/
|
||||
*/
|
||||
private Pair<String, OffsetTranslation> getStringConcatResult(Expression exp) {
|
||||
if (exp instanceof BinaryExpression) {
|
||||
BinaryExpression be = (BinaryExpression) exp;
|
||||
@@ -636,7 +636,7 @@ public class ASTExtractor {
|
||||
if (str.length() > 1000) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
int delta = be.getRight().getLoc().getStart().getOffset() - be.getLeft().getLoc().getStart().getOffset();
|
||||
int offset = left.fst().length();
|
||||
return Pair.make(str, left.snd().append(right.snd(), offset, delta));
|
||||
@@ -747,7 +747,7 @@ public class ASTExtractor {
|
||||
visit(nd.getProperty(), key, 1, IdContext.TYPE_LABEL);
|
||||
} else {
|
||||
IdContext baseIdContext =
|
||||
c.idcontext == IdContext.EXPORT ? IdContext.EXPORT_BASE : IdContext.VAR_BIND;
|
||||
(c.idcontext == IdContext.EXPORT || c.idcontext == IdContext.EXPORT_BASE) ? IdContext.EXPORT_BASE : IdContext.VAR_BIND;
|
||||
visit(nd.getObject(), key, 0, baseIdContext);
|
||||
visit(nd.getProperty(), key, 1, nd.isComputed() ? IdContext.VAR_BIND : IdContext.LABEL);
|
||||
}
|
||||
@@ -848,14 +848,14 @@ public class ASTExtractor {
|
||||
public Label visit(BinaryExpression nd, Context c) {
|
||||
Label key = super.visit(nd, c);
|
||||
if (nd.getOperator().equals("in") && nd.getLeft() instanceof Identifier && ((Identifier)nd.getLeft()).getName().startsWith("#")) {
|
||||
// this happens with Ergonomic brand checks for Private Fields (see https://github.com/tc39/proposal-private-fields-in-in).
|
||||
// this happens with Ergonomic brand checks for Private Fields (see https://github.com/tc39/proposal-private-fields-in-in).
|
||||
// it's the only case where private field identifiers are used not as a field.
|
||||
visit(nd.getLeft(), key, 0, IdContext.LABEL, true);
|
||||
} else {
|
||||
visit(nd.getLeft(), key, 0, true);
|
||||
}
|
||||
visit(nd.getRight(), key, 1, true);
|
||||
|
||||
|
||||
extractRegxpFromBinop(nd, c);
|
||||
return key;
|
||||
}
|
||||
@@ -1815,7 +1815,7 @@ public class ASTExtractor {
|
||||
visit(nd.getLocal(), lbl, 1, nd.hasTypeKeyword() ? IdContext.TYPE_ONLY_IMPORT : c.idcontext);
|
||||
if (nd.hasTypeKeyword()) {
|
||||
trapwriter.addTuple("has_type_keyword", lbl);
|
||||
}
|
||||
}
|
||||
return lbl;
|
||||
}
|
||||
|
||||
@@ -2191,6 +2191,7 @@ public class ASTExtractor {
|
||||
visitAll(nd.getBody(), key);
|
||||
contextManager.leaveContainer();
|
||||
scopeManager.leaveScope();
|
||||
emitNodeSymbol(nd, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,6 @@ import com.semmle.util.trap.TrapWriter;
|
||||
* <code>XML</code> is also supported
|
||||
* <li><code>LGTM_INDEX_XML_MODE</code>: whether to extract XML files
|
||||
* <li><code>LGTM_THREADS</code>: the maximum number of files to extract in parallel
|
||||
* <li><code>LGTM_TRAP_CACHE</code>: the path of a directory to use for trap caching
|
||||
* <li><code>LGTM_TRAP_CACHE_BOUND</code>: the size to bound the trap cache to
|
||||
* </ul>
|
||||
*
|
||||
* <p>It extracts the following:
|
||||
@@ -220,7 +218,7 @@ public class AutoBuild {
|
||||
this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC"));
|
||||
this.SEMMLE_DIST = Paths.get(EnvironmentVariables.getExtractorRoot());
|
||||
this.outputConfig = new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT);
|
||||
this.trapCache = mkTrapCache();
|
||||
this.trapCache = ITrapCache.fromExtractorOptions();
|
||||
this.typeScriptMode =
|
||||
getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL);
|
||||
this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING");
|
||||
@@ -281,28 +279,6 @@ public class AutoBuild {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up TRAP cache based on environment variables <code>LGTM_TRAP_CACHE</code> and <code>
|
||||
* LGTM_TRAP_CACHE_BOUND</code>.
|
||||
*/
|
||||
private ITrapCache mkTrapCache() {
|
||||
ITrapCache trapCache;
|
||||
String trapCachePath = getEnvVar("LGTM_TRAP_CACHE");
|
||||
if (trapCachePath != null) {
|
||||
Long sizeBound = null;
|
||||
String trapCacheBound = getEnvVar("LGTM_TRAP_CACHE_BOUND");
|
||||
if (trapCacheBound != null) {
|
||||
sizeBound = DefaultTrapCache.asFileSize(trapCacheBound);
|
||||
if (sizeBound == null)
|
||||
throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound);
|
||||
}
|
||||
trapCache = new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION);
|
||||
} else {
|
||||
trapCache = new DummyTrapCache();
|
||||
}
|
||||
return trapCache;
|
||||
}
|
||||
|
||||
private void setupFileTypes() {
|
||||
for (String spec : Main.NEWLINE.split(getEnvVar("LGTM_INDEX_FILETYPES", ""))) {
|
||||
spec = spec.trim();
|
||||
@@ -513,14 +489,13 @@ public class AutoBuild {
|
||||
SEMMLE_DIST.resolve(".cache").resolve("trap-cache").resolve("javascript");
|
||||
if (Files.isDirectory(trapCachePath)) {
|
||||
trapCache =
|
||||
new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION) {
|
||||
new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION, false) {
|
||||
boolean warnedAboutCacheMiss = false;
|
||||
|
||||
@Override
|
||||
public File lookup(String source, ExtractorConfig config, FileType type) {
|
||||
File f = super.lookup(source, config, type);
|
||||
// only return `f` if it exists; this has the effect of making the cache read-only
|
||||
if (f.exists()) return f;
|
||||
if (f != null) return f;
|
||||
// warn on first failed lookup
|
||||
if (!warnedAboutCacheMiss) {
|
||||
warn("Trap cache lookup for externs failed.");
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.semmle.js.extractor;
|
||||
|
||||
import com.semmle.util.process.Env;
|
||||
|
||||
public class ExtractorOptionsUtil {
|
||||
public static String readExtractorOption(String... option) {
|
||||
StringBuilder name = new StringBuilder("CODEQL_EXTRACTOR_JAVASCRIPT_OPTION");
|
||||
for (String segment : option)
|
||||
name.append("_").append(segment.toUpperCase());
|
||||
return Env.systemEnv().getNonEmpty(name.toString());
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ public class FileExtractor {
|
||||
}
|
||||
},
|
||||
|
||||
TYPESCRIPT(".ts", ".tsx") {
|
||||
TYPESCRIPT(".ts", ".tsx", ".mts", ".cts") {
|
||||
@Override
|
||||
protected boolean contains(File f, String lcExt, ExtractorConfig config) {
|
||||
if (config.getTypeScriptMode() == TypeScriptMode.NONE) return false;
|
||||
@@ -217,9 +217,6 @@ public class FileExtractor {
|
||||
}
|
||||
|
||||
private boolean hasBadFileHeader(File f, String lcExt, ExtractorConfig config) {
|
||||
if (!".ts".equals(lcExt)) {
|
||||
return false;
|
||||
}
|
||||
try (FileInputStream fis = new FileInputStream(f)) {
|
||||
byte[] bytes = new byte[fileHeaderSize];
|
||||
int length = fis.read(bytes);
|
||||
|
||||
@@ -40,7 +40,8 @@ public class HTMLExtractor implements IExtractor {
|
||||
this.textualExtractor = textualExtractor;
|
||||
|
||||
this.scopeManager =
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion(), true);
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion(),
|
||||
ScopeManager.FileKind.TEMPLATE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -425,7 +426,7 @@ public class HTMLExtractor implements IExtractor {
|
||||
extractSnippet(
|
||||
TopLevelKind.ANGULAR_STYLE_TEMPLATE,
|
||||
config.withSourceType(SourceType.ANGULAR_STYLE_TEMPLATE),
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2020, true),
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2020, ScopeManager.FileKind.TEMPLATE),
|
||||
textualExtractor,
|
||||
m.group(bodyGroup),
|
||||
m.start(bodyGroup),
|
||||
|
||||
@@ -15,8 +15,6 @@ import com.semmle.extractor.html.HtmlPopulator;
|
||||
import com.semmle.js.extractor.ExtractorConfig.Platform;
|
||||
import com.semmle.js.extractor.ExtractorConfig.SourceType;
|
||||
import com.semmle.js.extractor.FileExtractor.FileType;
|
||||
import com.semmle.js.extractor.trapcache.DefaultTrapCache;
|
||||
import com.semmle.js.extractor.trapcache.DummyTrapCache;
|
||||
import com.semmle.js.extractor.trapcache.ITrapCache;
|
||||
import com.semmle.js.parser.ParsedProject;
|
||||
import com.semmle.ts.extractor.TypeExtractor;
|
||||
@@ -43,7 +41,7 @@ public class Main {
|
||||
* A version identifier that should be updated every time the extractor changes in such a way that
|
||||
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
|
||||
*/
|
||||
public static final String EXTRACTOR_VERSION = "2022-02-22";
|
||||
public static final String EXTRACTOR_VERSION = "2022-09-19";
|
||||
|
||||
public static final Pattern NEWLINE = Pattern.compile("\n");
|
||||
|
||||
@@ -61,8 +59,6 @@ public class Main {
|
||||
private static final String P_PLATFORM = "--platform";
|
||||
private static final String P_QUIET = "--quiet";
|
||||
private static final String P_SOURCE_TYPE = "--source-type";
|
||||
private static final String P_TRAP_CACHE = "--trap-cache";
|
||||
private static final String P_TRAP_CACHE_BOUND = "--trap-cache-bound";
|
||||
private static final String P_TYPESCRIPT = "--typescript";
|
||||
private static final String P_TYPESCRIPT_FULL = "--typescript-full";
|
||||
private static final String P_TYPESCRIPT_RAM = "--typescript-ram";
|
||||
@@ -112,22 +108,7 @@ public class Main {
|
||||
ap.parse();
|
||||
|
||||
extractorConfig = parseJSOptions(ap);
|
||||
ITrapCache trapCache;
|
||||
if (ap.has(P_TRAP_CACHE)) {
|
||||
Long sizeBound = null;
|
||||
if (ap.has(P_TRAP_CACHE_BOUND)) {
|
||||
String tcb = ap.getString(P_TRAP_CACHE_BOUND);
|
||||
sizeBound = DefaultTrapCache.asFileSize(tcb);
|
||||
if (sizeBound == null) ap.error("Invalid TRAP cache size bound: " + tcb);
|
||||
}
|
||||
trapCache = new DefaultTrapCache(ap.getString(P_TRAP_CACHE), sizeBound, EXTRACTOR_VERSION);
|
||||
} else {
|
||||
if (ap.has(P_TRAP_CACHE_BOUND))
|
||||
ap.error(
|
||||
P_TRAP_CACHE_BOUND + " should only be specified together with " + P_TRAP_CACHE + ".");
|
||||
trapCache = new DummyTrapCache();
|
||||
}
|
||||
fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, trapCache);
|
||||
fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, ITrapCache.fromExtractorOptions());
|
||||
|
||||
setupMatchers(ap);
|
||||
|
||||
@@ -153,7 +134,7 @@ public class Main {
|
||||
ensureFileIsExtracted(file, ap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TypeScriptParser tsParser = extractorState.getTypeScriptParser();
|
||||
tsParser.setTypescriptRam(extractorConfig.getTypeScriptRam());
|
||||
if (containsTypeScriptFiles()) {
|
||||
@@ -432,12 +413,6 @@ public class Main {
|
||||
argsParser.addToleratedFlag(P_TOLERATE_PARSE_ERRORS, 0);
|
||||
argsParser.addFlag(
|
||||
P_ABORT_ON_PARSE_ERRORS, 0, "Abort extraction if a parse error is encountered.");
|
||||
argsParser.addFlag(P_TRAP_CACHE, 1, "Use the given directory as the TRAP cache.");
|
||||
argsParser.addFlag(
|
||||
P_TRAP_CACHE_BOUND,
|
||||
1,
|
||||
"A (soft) upper limit on the size of the TRAP cache, "
|
||||
+ "in standard size units (e.g., 'g' for gigabytes).");
|
||||
argsParser.addFlag(P_DEFAULT_ENCODING, 1, "The encoding to use; default is UTF-8.");
|
||||
argsParser.addFlag(P_TYPESCRIPT, 0, "Enable basic TypesScript support.");
|
||||
argsParser.addFlag(
|
||||
@@ -460,7 +435,7 @@ public class Main {
|
||||
if (ap.has(P_TYPESCRIPT)) return TypeScriptMode.BASIC;
|
||||
return TypeScriptMode.NONE;
|
||||
}
|
||||
|
||||
|
||||
private Path inferSourceRoot(ArgsParser ap) {
|
||||
List<File> files = getFilesArg(ap);
|
||||
Path sourceRoot = files.iterator().next().toPath().toAbsolutePath().getParent();
|
||||
|
||||
@@ -97,20 +97,31 @@ public class ScopeManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static enum FileKind {
|
||||
/** Any file not specific to one of the other file kinds. */
|
||||
PLAIN,
|
||||
|
||||
/** A file potentially containing template tags. */
|
||||
TEMPLATE,
|
||||
|
||||
/** A d.ts file, containing TypeScript ambient declarations. */
|
||||
TYPESCRIPT_DECLARATION,
|
||||
}
|
||||
|
||||
private final TrapWriter trapWriter;
|
||||
private Scope curScope;
|
||||
private final Scope toplevelScope;
|
||||
private final ECMAVersion ecmaVersion;
|
||||
private final Set<String> implicitGlobals = new LinkedHashSet<String>();
|
||||
private Scope implicitVariableScope;
|
||||
private final boolean isInTemplateScope;
|
||||
private final FileKind fileKind;
|
||||
|
||||
public ScopeManager(TrapWriter trapWriter, ECMAVersion ecmaVersion, boolean isInTemplateScope) {
|
||||
public ScopeManager(TrapWriter trapWriter, ECMAVersion ecmaVersion, FileKind fileKind) {
|
||||
this.trapWriter = trapWriter;
|
||||
this.toplevelScope = enterScope(ScopeKind.GLOBAL, trapWriter.globalID("global_scope"), null);
|
||||
this.ecmaVersion = ecmaVersion;
|
||||
this.implicitVariableScope = toplevelScope;
|
||||
this.isInTemplateScope = isInTemplateScope;
|
||||
this.implicitVariableScope = toplevelScope;
|
||||
this.fileKind = fileKind;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +129,11 @@ public class ScopeManager {
|
||||
* relevant template tags.
|
||||
*/
|
||||
public boolean isInTemplateFile() {
|
||||
return isInTemplateScope;
|
||||
return this.fileKind == FileKind.TEMPLATE;
|
||||
}
|
||||
|
||||
public boolean isInTypeScriptDeclarationFile() {
|
||||
return this.fileKind == FileKind.TYPESCRIPT_DECLARATION;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,7 +236,7 @@ public class ScopeManager {
|
||||
|
||||
/**
|
||||
* Get the label for a given variable in the current scope; if it cannot be found, add it to the
|
||||
* implicit variable scope (usually the global scope).
|
||||
* implicit variable scope (usually the global scope).
|
||||
*/
|
||||
public Label getVarKey(String name) {
|
||||
Label lbl = curScope.lookupVariable(name);
|
||||
@@ -411,7 +426,7 @@ public class ScopeManager {
|
||||
// cases where we turn on the 'declKind' flags
|
||||
@Override
|
||||
public Void visit(FunctionDeclaration nd, Void v) {
|
||||
if (nd.hasDeclareKeyword()) return null;
|
||||
if (nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile()) return null;
|
||||
// strict mode functions are block-scoped, non-strict mode ones aren't
|
||||
if (blockscope == isStrict) visit(nd.getId(), DeclKind.var);
|
||||
return null;
|
||||
@@ -419,7 +434,7 @@ public class ScopeManager {
|
||||
|
||||
@Override
|
||||
public Void visit(ClassDeclaration nd, Void c) {
|
||||
if (nd.hasDeclareKeyword()) return null;
|
||||
if (nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile()) return null;
|
||||
if (blockscope) visit(nd.getClassDef().getId(), DeclKind.varAndType);
|
||||
return null;
|
||||
}
|
||||
@@ -468,7 +483,7 @@ public class ScopeManager {
|
||||
|
||||
@Override
|
||||
public Void visit(VariableDeclaration nd, Void v) {
|
||||
if (nd.hasDeclareKeyword()) return null;
|
||||
if (nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile()) return null;
|
||||
// in block scoping mode, only process 'let'; in non-block scoping
|
||||
// mode, only process non-'let'
|
||||
if (blockscope == nd.isBlockScoped(ecmaVersion)) visit(nd.getDeclarations());
|
||||
@@ -503,7 +518,8 @@ public class ScopeManager {
|
||||
@Override
|
||||
public Void visit(NamespaceDeclaration nd, Void c) {
|
||||
if (blockscope) return null;
|
||||
boolean hasVariable = nd.isInstantiated() && !nd.hasDeclareKeyword();
|
||||
boolean isAmbientOutsideDtsFile = nd.hasDeclareKeyword() && !isInTypeScriptDeclarationFile();
|
||||
boolean hasVariable = nd.isInstantiated() && !isAmbientOutsideDtsFile;
|
||||
visit(nd.getName(), hasVariable ? DeclKind.varAndNamespace : DeclKind.namespace);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ScriptExtractor implements IExtractor {
|
||||
}
|
||||
|
||||
ScopeManager scopeManager =
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion(), false);
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion(), ScopeManager.FileKind.PLAIN);
|
||||
Label toplevelLabel = null;
|
||||
LoCInfo loc;
|
||||
try {
|
||||
|
||||
@@ -22,8 +22,10 @@ public class TypeScriptExtractor implements IExtractor {
|
||||
String source = textualExtractor.getSource();
|
||||
File sourceFile = textualExtractor.getExtractedFile();
|
||||
Result res = state.getTypeScriptParser().parse(sourceFile, source, textualExtractor.getMetrics());
|
||||
ScopeManager scopeManager =
|
||||
new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2017, false);
|
||||
ScopeManager.FileKind fileKind = sourceFile.getName().endsWith(".d.ts")
|
||||
? ScopeManager.FileKind.TYPESCRIPT_DECLARATION
|
||||
: ScopeManager.FileKind.PLAIN;
|
||||
ScopeManager scopeManager = new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2017, fileKind);
|
||||
try {
|
||||
FileSnippet snippet = state.getSnippets().get(sourceFile.toPath());
|
||||
SourceType sourceType = snippet != null ? snippet.getSourceType() : jsExtractor.establishSourceType(source, false);
|
||||
|
||||
@@ -26,9 +26,15 @@ public class DefaultTrapCache implements ITrapCache {
|
||||
*/
|
||||
private final String extractorVersion;
|
||||
|
||||
public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion) {
|
||||
/**
|
||||
* Whether this cache supports write operations.
|
||||
*/
|
||||
private final boolean writeable;
|
||||
|
||||
public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion, boolean writeable) {
|
||||
this.trapCache = new File(trapCache);
|
||||
this.extractorVersion = extractorVersion;
|
||||
this.writeable = writeable;
|
||||
try {
|
||||
initCache(sizeBound);
|
||||
} catch (ResourceError | SecurityException e) {
|
||||
@@ -135,6 +141,8 @@ public class DefaultTrapCache implements ITrapCache {
|
||||
digestor.write(type.toString());
|
||||
digestor.write(config);
|
||||
digestor.write(source);
|
||||
return new File(trapCache, digestor.getDigest() + ".trap.gz");
|
||||
File result = new File(trapCache, digestor.getDigest() + ".trap.gz");
|
||||
if (!writeable && !result.exists()) return null; // If the cache isn't writable, only return the file if it exists
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.semmle.js.extractor.trapcache;
|
||||
|
||||
import static com.semmle.js.extractor.ExtractorOptionsUtil.readExtractorOption;
|
||||
|
||||
import com.semmle.js.extractor.ExtractorConfig;
|
||||
import com.semmle.js.extractor.FileExtractor;
|
||||
import com.semmle.js.extractor.Main;
|
||||
import com.semmle.util.exception.UserError;
|
||||
import java.io.File;
|
||||
|
||||
/** Generic TRAP cache interface. */
|
||||
@@ -18,4 +22,29 @@ public interface ITrapCache {
|
||||
* cached information), or does not yet exist (and should be populated by the extractor)
|
||||
*/
|
||||
public File lookup(String source, ExtractorConfig config, FileExtractor.FileType type);
|
||||
|
||||
/**
|
||||
* Build a TRAP cache as defined by the extractor options, which are read from the corresponding
|
||||
* environment variables as defined in
|
||||
* https://github.com/github/codeql-core/blob/main/design/spec/codeql-extractors.md
|
||||
*
|
||||
* @return a TRAP cache
|
||||
*/
|
||||
public static ITrapCache fromExtractorOptions() {
|
||||
String trapCachePath = readExtractorOption("trap", "cache", "dir");
|
||||
if (trapCachePath != null) {
|
||||
Long sizeBound = null;
|
||||
String trapCacheBound = readExtractorOption("trap", "cache", "bound");
|
||||
if (trapCacheBound != null) {
|
||||
sizeBound = DefaultTrapCache.asFileSize(trapCacheBound);
|
||||
if (sizeBound == null)
|
||||
throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound);
|
||||
}
|
||||
boolean writeable = true;
|
||||
String trapCacheWrite = readExtractorOption("trap", "cache", "write");
|
||||
if (trapCacheWrite != null) writeable = trapCacheWrite.equalsIgnoreCase("TRUE");
|
||||
return new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION, writeable);
|
||||
}
|
||||
return new DummyTrapCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,20 +28,29 @@ public class JcornWrapper {
|
||||
.onToken(tokens)
|
||||
.preserveParens(true)
|
||||
.allowReturnOutsideFunction(true);
|
||||
if (config.isMozExtensions()) options.mozExtensions(true);
|
||||
if (config.isJscript()) options.jscript(true);
|
||||
if (config.isJsx()) options = new JSXOptions(options);
|
||||
if (config.isEsnext()) options.esnext(true);
|
||||
if (config.isV8Extensions()) options.v8Extensions(true);
|
||||
if (config.isE4X()) options.e4x(true);
|
||||
|
||||
Program program = null;
|
||||
List<ParseError> errors = new ArrayList<>();
|
||||
|
||||
try {
|
||||
if (config.isTolerateParseErrors())
|
||||
options.onRecoverableError((err) -> errors.add(mkParseError(err)));
|
||||
|
||||
program = sourceType.createParser(options, source, 0).parse();
|
||||
try {
|
||||
// First try to parse as a regular JavaScript program.
|
||||
program = sourceType.createParser(options, source, 0).parse();
|
||||
} catch (SyntaxError e) {
|
||||
// If that fails, try to enable all the extensions that we support.
|
||||
if (config.isTolerateParseErrors())
|
||||
options.onRecoverableError((err) -> errors.add(mkParseError(err)));
|
||||
comments.clear();
|
||||
tokens.clear();
|
||||
if (config.isMozExtensions()) options.mozExtensions(true);
|
||||
if (config.isJscript()) options.jscript(true);
|
||||
if (config.isJsx()) options = new JSXOptions(options);
|
||||
if (config.isV8Extensions()) options.v8Extensions(true);
|
||||
if (config.isE4X()) options.e4x(true);
|
||||
if (config.isEsnext()) options.allowFlowTypes(true); // allow the flow-parser to parse types.
|
||||
program = sourceType.createParser(options, source, 0).parse();
|
||||
}
|
||||
} catch (SyntaxError e) {
|
||||
errors.add(mkParseError(e));
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** A statement of form <code>declare module "X" {...}</code>. */
|
||||
public class ExternalModuleDeclaration extends Statement {
|
||||
public class ExternalModuleDeclaration extends Statement implements INodeWithSymbol {
|
||||
private final Literal name;
|
||||
private final List<Statement> body;
|
||||
private int symbol = -1;
|
||||
|
||||
public ExternalModuleDeclaration(SourceLocation loc, Literal name, List<Statement> body) {
|
||||
super("ExternalModuleDeclaration", loc);
|
||||
@@ -29,4 +30,14 @@ public class ExternalModuleDeclaration extends Statement {
|
||||
public List<Statement> getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSymbol() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSymbol(int symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,7 +591,7 @@ public class TypeScriptASTConverter {
|
||||
return convertTryStatement(node, loc);
|
||||
case "TupleType":
|
||||
return convertTupleType(node, loc);
|
||||
case "NamedTupleMember":
|
||||
case "NamedTupleMember":
|
||||
return convertNamedTupleMember(node, loc);
|
||||
case "TypeAliasDeclaration":
|
||||
return convertTypeAliasDeclaration(node, loc);
|
||||
@@ -976,8 +976,9 @@ public class TypeScriptASTConverter {
|
||||
hasDeclareKeyword,
|
||||
hasAbstractKeyword);
|
||||
attachSymbolInformation(classDecl.getClassDef(), node);
|
||||
if (node.has("decorators")) {
|
||||
classDecl.addDecorators(convertChildren(node, "decorators"));
|
||||
List<Decorator> decorators = getDecorators(node);
|
||||
if (!decorators.isEmpty()) {
|
||||
classDecl.addDecorators(decorators);
|
||||
advanceUntilAfter(loc, classDecl.getDecorators());
|
||||
}
|
||||
Node exportedDecl = fixExports(loc, classDecl);
|
||||
@@ -989,6 +990,17 @@ public class TypeScriptASTConverter {
|
||||
return exportedDecl;
|
||||
}
|
||||
|
||||
List<Decorator> getDecorators(JsonObject node) throws ParseError {
|
||||
List<Decorator> result = new ArrayList<>();
|
||||
for (JsonElement elt : getChildIterable(node, "modifiers")) {
|
||||
JsonObject modifier = elt.getAsJsonObject();
|
||||
if (hasKind(modifier, "Decorator")) {
|
||||
result.add((Decorator) convertNode(modifier));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node convertCommaListExpression(JsonObject node, SourceLocation loc) throws ParseError {
|
||||
return new SequenceExpression(loc, convertChildren(node, "elements"));
|
||||
}
|
||||
@@ -1041,7 +1053,7 @@ public class TypeScriptASTConverter {
|
||||
private List<DecoratorList> convertParameterDecorators(JsonObject function) throws ParseError {
|
||||
List<DecoratorList> decoratorLists = new ArrayList<>();
|
||||
for (JsonElement parameter : getProperParameters(function)) {
|
||||
decoratorLists.add(makeDecoratorList(parameter.getAsJsonObject().get("decorators")));
|
||||
decoratorLists.add(makeDecoratorList(parameter.getAsJsonObject().get("modifiers")));
|
||||
}
|
||||
return decoratorLists;
|
||||
}
|
||||
@@ -1139,7 +1151,7 @@ public class TypeScriptASTConverter {
|
||||
loc,
|
||||
hasModifier(node, "ConstKeyword"),
|
||||
hasModifier(node, "DeclareKeyword"),
|
||||
convertChildrenNotNull(node, "decorators"),
|
||||
convertChildrenNotNull(node, "illegalDecorators"), // as of https://github.com/microsoft/TypeScript/pull/50343/ the property is called `illegalDecorators` instead of `decorators`
|
||||
convertChild(node, "name"),
|
||||
convertChildren(node, "members"));
|
||||
attachSymbolInformation(enumDeclaration, node);
|
||||
@@ -1664,8 +1676,9 @@ public class TypeScriptASTConverter {
|
||||
FunctionExpression method = convertImplicitFunction(node, loc);
|
||||
MethodDefinition methodDefinition =
|
||||
new MethodDefinition(loc, flags, methodKind, convertChild(node, "name"), method);
|
||||
if (node.has("decorators")) {
|
||||
methodDefinition.addDecorators(convertChildren(node, "decorators"));
|
||||
List<Decorator> decorators = getDecorators(node);
|
||||
if (!decorators.isEmpty()) {
|
||||
methodDefinition.addDecorators(decorators);
|
||||
advanceUntilAfter(loc, methodDefinition.getDecorators());
|
||||
}
|
||||
return methodDefinition;
|
||||
@@ -1710,7 +1723,9 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
if (nameNode instanceof Literal) {
|
||||
// Declaration of form: declare module "X" {...}
|
||||
return new ExternalModuleDeclaration(loc, (Literal) nameNode, body);
|
||||
ExternalModuleDeclaration decl = new ExternalModuleDeclaration(loc, (Literal) nameNode, body);
|
||||
attachSymbolInformation(decl, node);
|
||||
return decl;
|
||||
}
|
||||
if (hasFlag(node, "GlobalAugmentation")) {
|
||||
// Declaration of form: declare global {...}
|
||||
@@ -2077,8 +2092,9 @@ public class TypeScriptASTConverter {
|
||||
convertChild(node, "name"),
|
||||
convertChild(node, "initializer"),
|
||||
convertChildAsType(node, "type"));
|
||||
if (node.has("decorators")) {
|
||||
fieldDefinition.addDecorators(convertChildren(node, "decorators"));
|
||||
List<Decorator> decorators = getDecorators(node);
|
||||
if (!decorators.isEmpty()) {
|
||||
fieldDefinition.addDecorators(decorators);
|
||||
advanceUntilAfter(loc, fieldDefinition.getDecorators());
|
||||
}
|
||||
return fieldDefinition;
|
||||
|
||||
9
javascript/extractor/tests/esnext/input/for-await.js
Normal file
9
javascript/extractor/tests/esnext/input/for-await.js
Normal file
@@ -0,0 +1,9 @@
|
||||
async function foo() {
|
||||
for await (const call of calls) {
|
||||
call();
|
||||
}
|
||||
}
|
||||
|
||||
for await (const call of calls) {
|
||||
call();
|
||||
}
|
||||
450
javascript/extractor/tests/esnext/output/trap/for-await.js.trap
Normal file
450
javascript/extractor/tests/esnext/output/trap/for-await.js.trap
Normal file
@@ -0,0 +1,450 @@
|
||||
#10000=@"/for-await.js;sourcefile"
|
||||
files(#10000,"/for-await.js")
|
||||
#10001=@"/;folder"
|
||||
folders(#10001,"/")
|
||||
containerparent(#10001,#10000)
|
||||
#10002=@"loc,{#10000},0,0,0,0"
|
||||
locations_default(#10002,#10000,0,0,0,0)
|
||||
hasLocation(#10000,#10002)
|
||||
#20000=@"global_scope"
|
||||
scopes(#20000,0)
|
||||
#20001=@"script;{#10000},1,1"
|
||||
#20002=*
|
||||
lines(#20002,#20001,"async function foo() {","
|
||||
")
|
||||
#20003=@"loc,{#10000},1,1,1,22"
|
||||
locations_default(#20003,#10000,1,1,1,22)
|
||||
hasLocation(#20002,#20003)
|
||||
#20004=*
|
||||
lines(#20004,#20001," for await (const call of calls) {","
|
||||
")
|
||||
#20005=@"loc,{#10000},2,1,2,37"
|
||||
locations_default(#20005,#10000,2,1,2,37)
|
||||
hasLocation(#20004,#20005)
|
||||
indentation(#10000,2," ",4)
|
||||
#20006=*
|
||||
lines(#20006,#20001," call(); ","
|
||||
")
|
||||
#20007=@"loc,{#10000},3,1,3,19"
|
||||
locations_default(#20007,#10000,3,1,3,19)
|
||||
hasLocation(#20006,#20007)
|
||||
indentation(#10000,3," ",8)
|
||||
#20008=*
|
||||
lines(#20008,#20001," }","
|
||||
")
|
||||
#20009=@"loc,{#10000},4,1,4,5"
|
||||
locations_default(#20009,#10000,4,1,4,5)
|
||||
hasLocation(#20008,#20009)
|
||||
indentation(#10000,4," ",4)
|
||||
#20010=*
|
||||
lines(#20010,#20001,"}","
|
||||
")
|
||||
#20011=@"loc,{#10000},5,1,5,1"
|
||||
locations_default(#20011,#10000,5,1,5,1)
|
||||
hasLocation(#20010,#20011)
|
||||
#20012=*
|
||||
lines(#20012,#20001,"","
|
||||
")
|
||||
#20013=@"loc,{#10000},6,1,6,0"
|
||||
locations_default(#20013,#10000,6,1,6,0)
|
||||
hasLocation(#20012,#20013)
|
||||
#20014=*
|
||||
lines(#20014,#20001,"for await (const call of calls) {","
|
||||
")
|
||||
#20015=@"loc,{#10000},7,1,7,33"
|
||||
locations_default(#20015,#10000,7,1,7,33)
|
||||
hasLocation(#20014,#20015)
|
||||
#20016=*
|
||||
lines(#20016,#20001," call();","
|
||||
")
|
||||
#20017=@"loc,{#10000},8,1,8,11"
|
||||
locations_default(#20017,#10000,8,1,8,11)
|
||||
hasLocation(#20016,#20017)
|
||||
indentation(#10000,8," ",4)
|
||||
#20018=*
|
||||
lines(#20018,#20001,"}","")
|
||||
#20019=@"loc,{#10000},9,1,9,1"
|
||||
locations_default(#20019,#10000,9,1,9,1)
|
||||
hasLocation(#20018,#20019)
|
||||
numlines(#20001,9,8,0)
|
||||
#20020=*
|
||||
tokeninfo(#20020,6,#20001,0,"async")
|
||||
#20021=@"loc,{#10000},1,1,1,5"
|
||||
locations_default(#20021,#10000,1,1,1,5)
|
||||
hasLocation(#20020,#20021)
|
||||
#20022=*
|
||||
tokeninfo(#20022,7,#20001,1,"function")
|
||||
#20023=@"loc,{#10000},1,7,1,14"
|
||||
locations_default(#20023,#10000,1,7,1,14)
|
||||
hasLocation(#20022,#20023)
|
||||
#20024=*
|
||||
tokeninfo(#20024,6,#20001,2,"foo")
|
||||
#20025=@"loc,{#10000},1,16,1,18"
|
||||
locations_default(#20025,#10000,1,16,1,18)
|
||||
hasLocation(#20024,#20025)
|
||||
#20026=*
|
||||
tokeninfo(#20026,8,#20001,3,"(")
|
||||
#20027=@"loc,{#10000},1,19,1,19"
|
||||
locations_default(#20027,#10000,1,19,1,19)
|
||||
hasLocation(#20026,#20027)
|
||||
#20028=*
|
||||
tokeninfo(#20028,8,#20001,4,")")
|
||||
#20029=@"loc,{#10000},1,20,1,20"
|
||||
locations_default(#20029,#10000,1,20,1,20)
|
||||
hasLocation(#20028,#20029)
|
||||
#20030=*
|
||||
tokeninfo(#20030,8,#20001,5,"{")
|
||||
#20031=@"loc,{#10000},1,22,1,22"
|
||||
locations_default(#20031,#10000,1,22,1,22)
|
||||
hasLocation(#20030,#20031)
|
||||
#20032=*
|
||||
tokeninfo(#20032,7,#20001,6,"for")
|
||||
#20033=@"loc,{#10000},2,5,2,7"
|
||||
locations_default(#20033,#10000,2,5,2,7)
|
||||
hasLocation(#20032,#20033)
|
||||
#20034=*
|
||||
tokeninfo(#20034,6,#20001,7,"await")
|
||||
#20035=@"loc,{#10000},2,9,2,13"
|
||||
locations_default(#20035,#10000,2,9,2,13)
|
||||
hasLocation(#20034,#20035)
|
||||
#20036=*
|
||||
tokeninfo(#20036,8,#20001,8,"(")
|
||||
#20037=@"loc,{#10000},2,15,2,15"
|
||||
locations_default(#20037,#10000,2,15,2,15)
|
||||
hasLocation(#20036,#20037)
|
||||
#20038=*
|
||||
tokeninfo(#20038,7,#20001,9,"const")
|
||||
#20039=@"loc,{#10000},2,16,2,20"
|
||||
locations_default(#20039,#10000,2,16,2,20)
|
||||
hasLocation(#20038,#20039)
|
||||
#20040=*
|
||||
tokeninfo(#20040,6,#20001,10,"call")
|
||||
#20041=@"loc,{#10000},2,22,2,25"
|
||||
locations_default(#20041,#10000,2,22,2,25)
|
||||
hasLocation(#20040,#20041)
|
||||
#20042=*
|
||||
tokeninfo(#20042,6,#20001,11,"of")
|
||||
#20043=@"loc,{#10000},2,27,2,28"
|
||||
locations_default(#20043,#10000,2,27,2,28)
|
||||
hasLocation(#20042,#20043)
|
||||
#20044=*
|
||||
tokeninfo(#20044,6,#20001,12,"calls")
|
||||
#20045=@"loc,{#10000},2,30,2,34"
|
||||
locations_default(#20045,#10000,2,30,2,34)
|
||||
hasLocation(#20044,#20045)
|
||||
#20046=*
|
||||
tokeninfo(#20046,8,#20001,13,")")
|
||||
#20047=@"loc,{#10000},2,35,2,35"
|
||||
locations_default(#20047,#10000,2,35,2,35)
|
||||
hasLocation(#20046,#20047)
|
||||
#20048=*
|
||||
tokeninfo(#20048,8,#20001,14,"{")
|
||||
#20049=@"loc,{#10000},2,37,2,37"
|
||||
locations_default(#20049,#10000,2,37,2,37)
|
||||
hasLocation(#20048,#20049)
|
||||
#20050=*
|
||||
tokeninfo(#20050,6,#20001,15,"call")
|
||||
#20051=@"loc,{#10000},3,9,3,12"
|
||||
locations_default(#20051,#10000,3,9,3,12)
|
||||
hasLocation(#20050,#20051)
|
||||
#20052=*
|
||||
tokeninfo(#20052,8,#20001,16,"(")
|
||||
#20053=@"loc,{#10000},3,13,3,13"
|
||||
locations_default(#20053,#10000,3,13,3,13)
|
||||
hasLocation(#20052,#20053)
|
||||
#20054=*
|
||||
tokeninfo(#20054,8,#20001,17,")")
|
||||
#20055=@"loc,{#10000},3,14,3,14"
|
||||
locations_default(#20055,#10000,3,14,3,14)
|
||||
hasLocation(#20054,#20055)
|
||||
#20056=*
|
||||
tokeninfo(#20056,8,#20001,18,";")
|
||||
#20057=@"loc,{#10000},3,15,3,15"
|
||||
locations_default(#20057,#10000,3,15,3,15)
|
||||
hasLocation(#20056,#20057)
|
||||
#20058=*
|
||||
tokeninfo(#20058,8,#20001,19,"}")
|
||||
#20059=@"loc,{#10000},4,5,4,5"
|
||||
locations_default(#20059,#10000,4,5,4,5)
|
||||
hasLocation(#20058,#20059)
|
||||
#20060=*
|
||||
tokeninfo(#20060,8,#20001,20,"}")
|
||||
hasLocation(#20060,#20011)
|
||||
#20061=*
|
||||
tokeninfo(#20061,7,#20001,21,"for")
|
||||
#20062=@"loc,{#10000},7,1,7,3"
|
||||
locations_default(#20062,#10000,7,1,7,3)
|
||||
hasLocation(#20061,#20062)
|
||||
#20063=*
|
||||
tokeninfo(#20063,6,#20001,22,"await")
|
||||
#20064=@"loc,{#10000},7,5,7,9"
|
||||
locations_default(#20064,#10000,7,5,7,9)
|
||||
hasLocation(#20063,#20064)
|
||||
#20065=*
|
||||
tokeninfo(#20065,8,#20001,23,"(")
|
||||
#20066=@"loc,{#10000},7,11,7,11"
|
||||
locations_default(#20066,#10000,7,11,7,11)
|
||||
hasLocation(#20065,#20066)
|
||||
#20067=*
|
||||
tokeninfo(#20067,7,#20001,24,"const")
|
||||
#20068=@"loc,{#10000},7,12,7,16"
|
||||
locations_default(#20068,#10000,7,12,7,16)
|
||||
hasLocation(#20067,#20068)
|
||||
#20069=*
|
||||
tokeninfo(#20069,6,#20001,25,"call")
|
||||
#20070=@"loc,{#10000},7,18,7,21"
|
||||
locations_default(#20070,#10000,7,18,7,21)
|
||||
hasLocation(#20069,#20070)
|
||||
#20071=*
|
||||
tokeninfo(#20071,6,#20001,26,"of")
|
||||
#20072=@"loc,{#10000},7,23,7,24"
|
||||
locations_default(#20072,#10000,7,23,7,24)
|
||||
hasLocation(#20071,#20072)
|
||||
#20073=*
|
||||
tokeninfo(#20073,6,#20001,27,"calls")
|
||||
#20074=@"loc,{#10000},7,26,7,30"
|
||||
locations_default(#20074,#10000,7,26,7,30)
|
||||
hasLocation(#20073,#20074)
|
||||
#20075=*
|
||||
tokeninfo(#20075,8,#20001,28,")")
|
||||
#20076=@"loc,{#10000},7,31,7,31"
|
||||
locations_default(#20076,#10000,7,31,7,31)
|
||||
hasLocation(#20075,#20076)
|
||||
#20077=*
|
||||
tokeninfo(#20077,8,#20001,29,"{")
|
||||
#20078=@"loc,{#10000},7,33,7,33"
|
||||
locations_default(#20078,#10000,7,33,7,33)
|
||||
hasLocation(#20077,#20078)
|
||||
#20079=*
|
||||
tokeninfo(#20079,6,#20001,30,"call")
|
||||
#20080=@"loc,{#10000},8,5,8,8"
|
||||
locations_default(#20080,#10000,8,5,8,8)
|
||||
hasLocation(#20079,#20080)
|
||||
#20081=*
|
||||
tokeninfo(#20081,8,#20001,31,"(")
|
||||
#20082=@"loc,{#10000},8,9,8,9"
|
||||
locations_default(#20082,#10000,8,9,8,9)
|
||||
hasLocation(#20081,#20082)
|
||||
#20083=*
|
||||
tokeninfo(#20083,8,#20001,32,")")
|
||||
#20084=@"loc,{#10000},8,10,8,10"
|
||||
locations_default(#20084,#10000,8,10,8,10)
|
||||
hasLocation(#20083,#20084)
|
||||
#20085=*
|
||||
tokeninfo(#20085,8,#20001,33,";")
|
||||
#20086=@"loc,{#10000},8,11,8,11"
|
||||
locations_default(#20086,#10000,8,11,8,11)
|
||||
hasLocation(#20085,#20086)
|
||||
#20087=*
|
||||
tokeninfo(#20087,8,#20001,34,"}")
|
||||
hasLocation(#20087,#20019)
|
||||
#20088=*
|
||||
tokeninfo(#20088,0,#20001,35,"")
|
||||
#20089=@"loc,{#10000},9,2,9,1"
|
||||
locations_default(#20089,#10000,9,2,9,1)
|
||||
hasLocation(#20088,#20089)
|
||||
toplevels(#20001,0)
|
||||
#20090=@"loc,{#10000},1,1,9,1"
|
||||
locations_default(#20090,#10000,1,1,9,1)
|
||||
hasLocation(#20001,#20090)
|
||||
#20091=@"var;{foo};{#20000}"
|
||||
variables(#20091,"foo",#20000)
|
||||
#20092=*
|
||||
stmts(#20092,17,#20001,0,"async f ... }\n}")
|
||||
#20093=@"loc,{#10000},1,1,5,1"
|
||||
locations_default(#20093,#10000,1,1,5,1)
|
||||
hasLocation(#20092,#20093)
|
||||
stmt_containers(#20092,#20001)
|
||||
#20094=*
|
||||
exprs(#20094,78,#20092,-1,"foo")
|
||||
hasLocation(#20094,#20025)
|
||||
expr_containers(#20094,#20092)
|
||||
literals("foo","foo",#20094)
|
||||
decl(#20094,#20091)
|
||||
#20095=*
|
||||
scopes(#20095,1)
|
||||
scopenodes(#20092,#20095)
|
||||
scopenesting(#20095,#20000)
|
||||
#20096=@"var;{arguments};{#20095}"
|
||||
variables(#20096,"arguments",#20095)
|
||||
is_arguments_object(#20096)
|
||||
is_async(#20092)
|
||||
#20097=*
|
||||
stmts(#20097,1,#20092,-2,"{\n f ... }\n}")
|
||||
#20098=@"loc,{#10000},1,22,5,1"
|
||||
locations_default(#20098,#10000,1,22,5,1)
|
||||
hasLocation(#20097,#20098)
|
||||
stmt_containers(#20097,#20092)
|
||||
#20099=*
|
||||
stmts(#20099,21,#20097,0,"for awa ... \n }")
|
||||
#20100=@"loc,{#10000},2,5,4,5"
|
||||
locations_default(#20100,#10000,2,5,4,5)
|
||||
hasLocation(#20099,#20100)
|
||||
stmt_containers(#20099,#20092)
|
||||
#20101=*
|
||||
exprs(#20101,79,#20099,1,"calls")
|
||||
hasLocation(#20101,#20045)
|
||||
enclosing_stmt(#20101,#20099)
|
||||
expr_containers(#20101,#20092)
|
||||
literals("calls","calls",#20101)
|
||||
#20102=@"var;{calls};{#20000}"
|
||||
variables(#20102,"calls",#20000)
|
||||
bind(#20101,#20102)
|
||||
#20103=*
|
||||
scopes(#20103,6)
|
||||
scopenodes(#20099,#20103)
|
||||
scopenesting(#20103,#20095)
|
||||
#20104=@"var;{call};{#20103}"
|
||||
variables(#20104,"call",#20103)
|
||||
#20105=*
|
||||
stmts(#20105,22,#20099,0,"const call")
|
||||
#20106=@"loc,{#10000},2,16,2,25"
|
||||
locations_default(#20106,#10000,2,16,2,25)
|
||||
hasLocation(#20105,#20106)
|
||||
stmt_containers(#20105,#20092)
|
||||
#20107=*
|
||||
exprs(#20107,64,#20105,0,"call")
|
||||
hasLocation(#20107,#20041)
|
||||
enclosing_stmt(#20107,#20105)
|
||||
expr_containers(#20107,#20092)
|
||||
#20108=*
|
||||
exprs(#20108,78,#20107,0,"call")
|
||||
hasLocation(#20108,#20041)
|
||||
enclosing_stmt(#20108,#20105)
|
||||
expr_containers(#20108,#20092)
|
||||
literals("call","call",#20108)
|
||||
decl(#20108,#20104)
|
||||
#20109=*
|
||||
stmts(#20109,1,#20099,2,"{\n ... \n }")
|
||||
#20110=@"loc,{#10000},2,37,4,5"
|
||||
locations_default(#20110,#10000,2,37,4,5)
|
||||
hasLocation(#20109,#20110)
|
||||
stmt_containers(#20109,#20092)
|
||||
#20111=*
|
||||
stmts(#20111,2,#20109,0,"call();")
|
||||
#20112=@"loc,{#10000},3,9,3,15"
|
||||
locations_default(#20112,#10000,3,9,3,15)
|
||||
hasLocation(#20111,#20112)
|
||||
stmt_containers(#20111,#20092)
|
||||
#20113=*
|
||||
exprs(#20113,13,#20111,0,"call()")
|
||||
#20114=@"loc,{#10000},3,9,3,14"
|
||||
locations_default(#20114,#10000,3,9,3,14)
|
||||
hasLocation(#20113,#20114)
|
||||
enclosing_stmt(#20113,#20111)
|
||||
expr_containers(#20113,#20092)
|
||||
#20115=*
|
||||
exprs(#20115,79,#20113,-1,"call")
|
||||
hasLocation(#20115,#20051)
|
||||
enclosing_stmt(#20115,#20111)
|
||||
expr_containers(#20115,#20092)
|
||||
literals("call","call",#20115)
|
||||
bind(#20115,#20104)
|
||||
is_for_await_of(#20099)
|
||||
#20116=*
|
||||
stmts(#20116,21,#20001,1,"for awa ... ll();\n}")
|
||||
#20117=@"loc,{#10000},7,1,9,1"
|
||||
locations_default(#20117,#10000,7,1,9,1)
|
||||
hasLocation(#20116,#20117)
|
||||
stmt_containers(#20116,#20001)
|
||||
#20118=*
|
||||
exprs(#20118,79,#20116,1,"calls")
|
||||
hasLocation(#20118,#20074)
|
||||
enclosing_stmt(#20118,#20116)
|
||||
expr_containers(#20118,#20001)
|
||||
literals("calls","calls",#20118)
|
||||
bind(#20118,#20102)
|
||||
#20119=*
|
||||
scopes(#20119,6)
|
||||
scopenodes(#20116,#20119)
|
||||
scopenesting(#20119,#20000)
|
||||
#20120=@"var;{call};{#20119}"
|
||||
variables(#20120,"call",#20119)
|
||||
#20121=*
|
||||
stmts(#20121,22,#20116,0,"const call")
|
||||
#20122=@"loc,{#10000},7,12,7,21"
|
||||
locations_default(#20122,#10000,7,12,7,21)
|
||||
hasLocation(#20121,#20122)
|
||||
stmt_containers(#20121,#20001)
|
||||
#20123=*
|
||||
exprs(#20123,64,#20121,0,"call")
|
||||
hasLocation(#20123,#20070)
|
||||
enclosing_stmt(#20123,#20121)
|
||||
expr_containers(#20123,#20001)
|
||||
#20124=*
|
||||
exprs(#20124,78,#20123,0,"call")
|
||||
hasLocation(#20124,#20070)
|
||||
enclosing_stmt(#20124,#20121)
|
||||
expr_containers(#20124,#20001)
|
||||
literals("call","call",#20124)
|
||||
decl(#20124,#20120)
|
||||
#20125=*
|
||||
stmts(#20125,1,#20116,2,"{\n call();\n}")
|
||||
#20126=@"loc,{#10000},7,33,9,1"
|
||||
locations_default(#20126,#10000,7,33,9,1)
|
||||
hasLocation(#20125,#20126)
|
||||
stmt_containers(#20125,#20001)
|
||||
#20127=*
|
||||
stmts(#20127,2,#20125,0,"call();")
|
||||
#20128=@"loc,{#10000},8,5,8,11"
|
||||
locations_default(#20128,#10000,8,5,8,11)
|
||||
hasLocation(#20127,#20128)
|
||||
stmt_containers(#20127,#20001)
|
||||
#20129=*
|
||||
exprs(#20129,13,#20127,0,"call()")
|
||||
#20130=@"loc,{#10000},8,5,8,10"
|
||||
locations_default(#20130,#10000,8,5,8,10)
|
||||
hasLocation(#20129,#20130)
|
||||
enclosing_stmt(#20129,#20127)
|
||||
expr_containers(#20129,#20001)
|
||||
#20131=*
|
||||
exprs(#20131,79,#20129,-1,"call")
|
||||
hasLocation(#20131,#20080)
|
||||
enclosing_stmt(#20131,#20127)
|
||||
expr_containers(#20131,#20001)
|
||||
literals("call","call",#20131)
|
||||
bind(#20131,#20120)
|
||||
is_for_await_of(#20116)
|
||||
#20132=*
|
||||
entry_cfg_node(#20132,#20001)
|
||||
#20133=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20133,#10000,1,1,1,0)
|
||||
hasLocation(#20132,#20133)
|
||||
#20134=*
|
||||
exit_cfg_node(#20134,#20001)
|
||||
hasLocation(#20134,#20089)
|
||||
successor(#20118,#20116)
|
||||
successor(#20116,#20121)
|
||||
successor(#20116,#20134)
|
||||
successor(#20125,#20127)
|
||||
successor(#20127,#20131)
|
||||
successor(#20131,#20129)
|
||||
successor(#20129,#20116)
|
||||
successor(#20121,#20124)
|
||||
successor(#20124,#20123)
|
||||
successor(#20123,#20125)
|
||||
successor(#20092,#20118)
|
||||
#20135=*
|
||||
entry_cfg_node(#20135,#20092)
|
||||
hasLocation(#20135,#20133)
|
||||
#20136=*
|
||||
exit_cfg_node(#20136,#20092)
|
||||
#20137=@"loc,{#10000},5,2,5,1"
|
||||
locations_default(#20137,#10000,5,2,5,1)
|
||||
hasLocation(#20136,#20137)
|
||||
successor(#20097,#20101)
|
||||
successor(#20101,#20099)
|
||||
successor(#20099,#20105)
|
||||
successor(#20099,#20136)
|
||||
successor(#20109,#20111)
|
||||
successor(#20111,#20115)
|
||||
successor(#20115,#20113)
|
||||
successor(#20113,#20099)
|
||||
successor(#20105,#20108)
|
||||
successor(#20108,#20107)
|
||||
successor(#20107,#20109)
|
||||
successor(#20135,#20097)
|
||||
successor(#20094,#20092)
|
||||
successor(#20132,#20094)
|
||||
numlines(#10000,9,8,0)
|
||||
filetype(#10000,"javascript")
|
||||
@@ -133,76 +133,71 @@ locations_default(#20044,#10000,1,17,4,1)
|
||||
hasLocation(#20043,#20044)
|
||||
stmt_containers(#20043,#20039)
|
||||
#20045=*
|
||||
scopes(#20045,4)
|
||||
scopenodes(#20043,#20045)
|
||||
scopenesting(#20045,#20041)
|
||||
#20046=@"var;{arguments};{#20045}"
|
||||
variables(#20046,"arguments",#20045)
|
||||
stmts(#20045,2,#20043,0,"arguments;")
|
||||
#20046=@"loc,{#10000},2,5,2,14"
|
||||
locations_default(#20046,#10000,2,5,2,14)
|
||||
hasLocation(#20045,#20046)
|
||||
stmt_containers(#20045,#20039)
|
||||
#20047=*
|
||||
stmts(#20047,2,#20043,0,"arguments;")
|
||||
#20048=@"loc,{#10000},2,5,2,14"
|
||||
locations_default(#20048,#10000,2,5,2,14)
|
||||
hasLocation(#20047,#20048)
|
||||
stmt_containers(#20047,#20039)
|
||||
#20049=*
|
||||
exprs(#20049,79,#20047,0,"arguments")
|
||||
hasLocation(#20049,#20021)
|
||||
enclosing_stmt(#20049,#20047)
|
||||
expr_containers(#20049,#20039)
|
||||
literals("arguments","arguments",#20049)
|
||||
bind(#20049,#20046)
|
||||
exprs(#20047,79,#20045,0,"arguments")
|
||||
hasLocation(#20047,#20021)
|
||||
enclosing_stmt(#20047,#20045)
|
||||
expr_containers(#20047,#20039)
|
||||
literals("arguments","arguments",#20047)
|
||||
bind(#20047,#20042)
|
||||
#20048=*
|
||||
stmts(#20048,2,#20043,1,"let (arguments);")
|
||||
#20049=@"loc,{#10000},3,5,3,20"
|
||||
locations_default(#20049,#10000,3,5,3,20)
|
||||
hasLocation(#20048,#20049)
|
||||
stmt_containers(#20048,#20039)
|
||||
#20050=*
|
||||
stmts(#20050,24,#20043,1,"let (arguments);")
|
||||
#20051=@"loc,{#10000},3,5,3,20"
|
||||
locations_default(#20051,#10000,3,5,3,20)
|
||||
exprs(#20050,13,#20048,0,"let (arguments)")
|
||||
#20051=@"loc,{#10000},3,5,3,19"
|
||||
locations_default(#20051,#10000,3,5,3,19)
|
||||
hasLocation(#20050,#20051)
|
||||
stmt_containers(#20050,#20039)
|
||||
enclosing_stmt(#20050,#20048)
|
||||
expr_containers(#20050,#20039)
|
||||
#20052=*
|
||||
scopes(#20052,4)
|
||||
scopenodes(#20050,#20052)
|
||||
scopenesting(#20052,#20045)
|
||||
#20053=@"var;{arguments};{#20052}"
|
||||
variables(#20053,"arguments",#20052)
|
||||
exprs(#20052,79,#20050,-1,"let")
|
||||
hasLocation(#20052,#20025)
|
||||
enclosing_stmt(#20052,#20048)
|
||||
expr_containers(#20052,#20039)
|
||||
literals("let","let",#20052)
|
||||
#20053=@"var;{let};{#20000}"
|
||||
variables(#20053,"let",#20000)
|
||||
bind(#20052,#20053)
|
||||
#20054=*
|
||||
exprs(#20054,64,#20050,0,"arguments")
|
||||
exprs(#20054,79,#20050,0,"arguments")
|
||||
hasLocation(#20054,#20029)
|
||||
enclosing_stmt(#20054,#20050)
|
||||
enclosing_stmt(#20054,#20048)
|
||||
expr_containers(#20054,#20039)
|
||||
literals("arguments","arguments",#20054)
|
||||
bind(#20054,#20042)
|
||||
#20055=*
|
||||
exprs(#20055,78,#20054,0,"arguments")
|
||||
hasLocation(#20055,#20029)
|
||||
enclosing_stmt(#20055,#20050)
|
||||
expr_containers(#20055,#20039)
|
||||
literals("arguments","arguments",#20055)
|
||||
decl(#20055,#20053)
|
||||
#20056=*
|
||||
stmts(#20056,0,#20050,-1,";")
|
||||
hasLocation(#20056,#20033)
|
||||
stmt_containers(#20056,#20039)
|
||||
entry_cfg_node(#20055,#20001)
|
||||
#20056=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20056,#10000,1,1,1,0)
|
||||
hasLocation(#20055,#20056)
|
||||
#20057=*
|
||||
entry_cfg_node(#20057,#20001)
|
||||
#20058=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20058,#10000,1,1,1,0)
|
||||
hasLocation(#20057,#20058)
|
||||
exit_cfg_node(#20057,#20001)
|
||||
hasLocation(#20057,#20036)
|
||||
successor(#20039,#20057)
|
||||
#20058=*
|
||||
entry_cfg_node(#20058,#20039)
|
||||
hasLocation(#20058,#20056)
|
||||
#20059=*
|
||||
exit_cfg_node(#20059,#20001)
|
||||
exit_cfg_node(#20059,#20039)
|
||||
hasLocation(#20059,#20036)
|
||||
successor(#20039,#20059)
|
||||
#20060=*
|
||||
entry_cfg_node(#20060,#20039)
|
||||
hasLocation(#20060,#20058)
|
||||
#20061=*
|
||||
exit_cfg_node(#20061,#20039)
|
||||
hasLocation(#20061,#20036)
|
||||
successor(#20043,#20047)
|
||||
successor(#20050,#20055)
|
||||
successor(#20056,#20061)
|
||||
successor(#20055,#20054)
|
||||
successor(#20054,#20056)
|
||||
successor(#20047,#20049)
|
||||
successor(#20049,#20050)
|
||||
successor(#20060,#20043)
|
||||
successor(#20043,#20045)
|
||||
successor(#20048,#20052)
|
||||
successor(#20054,#20050)
|
||||
successor(#20052,#20054)
|
||||
successor(#20050,#20059)
|
||||
successor(#20045,#20047)
|
||||
successor(#20047,#20048)
|
||||
successor(#20058,#20043)
|
||||
successor(#20040,#20039)
|
||||
successor(#20057,#20040)
|
||||
successor(#20055,#20040)
|
||||
numlines(#10000,4,4,0)
|
||||
filetype(#10000,"javascript")
|
||||
|
||||
@@ -97,28 +97,28 @@ scopenodes(#20001,#20033)
|
||||
scopenesting(#20033,#20000)
|
||||
is_module(#20001)
|
||||
is_es2015_module(#20001)
|
||||
#20034=*
|
||||
stmts(#20034,30,#20001,0,"export ... foo();")
|
||||
hasLocation(#20034,#20003)
|
||||
stmt_containers(#20034,#20001)
|
||||
#20034=@"var;{foo};{#20033}"
|
||||
variables(#20034,"foo",#20033)
|
||||
#20035=*
|
||||
stmts(#20035,17,#20034,-1,"declare ... foo();")
|
||||
#20036=@"loc,{#10000},1,8,1,30"
|
||||
locations_default(#20036,#10000,1,8,1,30)
|
||||
hasLocation(#20035,#20036)
|
||||
stmts(#20035,30,#20001,0,"export ... foo();")
|
||||
hasLocation(#20035,#20003)
|
||||
stmt_containers(#20035,#20001)
|
||||
has_declare_keyword(#20035)
|
||||
#20037=*
|
||||
exprs(#20037,78,#20035,-1,"foo")
|
||||
hasLocation(#20037,#20013)
|
||||
expr_containers(#20037,#20035)
|
||||
literals("foo","foo",#20037)
|
||||
#20038=@"var;{foo};{#20000}"
|
||||
variables(#20038,"foo",#20000)
|
||||
decl(#20037,#20038)
|
||||
#20036=*
|
||||
stmts(#20036,17,#20035,-1,"declare ... foo();")
|
||||
#20037=@"loc,{#10000},1,8,1,30"
|
||||
locations_default(#20037,#10000,1,8,1,30)
|
||||
hasLocation(#20036,#20037)
|
||||
stmt_containers(#20036,#20001)
|
||||
has_declare_keyword(#20036)
|
||||
#20038=*
|
||||
exprs(#20038,78,#20036,-1,"foo")
|
||||
hasLocation(#20038,#20013)
|
||||
expr_containers(#20038,#20036)
|
||||
literals("foo","foo",#20038)
|
||||
decl(#20038,#20034)
|
||||
#20039=*
|
||||
scopes(#20039,1)
|
||||
scopenodes(#20035,#20039)
|
||||
scopenodes(#20036,#20039)
|
||||
scopenesting(#20039,#20033)
|
||||
#20040=@"var;{arguments};{#20039}"
|
||||
variables(#20040,"arguments",#20039)
|
||||
@@ -142,8 +142,8 @@ hasLocation(#20043,#20044)
|
||||
exit_cfg_node(#20045,#20001)
|
||||
hasLocation(#20045,#20031)
|
||||
successor(#20041,#20045)
|
||||
successor(#20034,#20035)
|
||||
successor(#20035,#20041)
|
||||
successor(#20043,#20034)
|
||||
successor(#20035,#20036)
|
||||
successor(#20036,#20041)
|
||||
successor(#20043,#20035)
|
||||
numlines(#10000,2,2,0)
|
||||
filetype(#10000,"typescript")
|
||||
|
||||
@@ -305,11 +305,12 @@ hasLocation(#20096,#20097)
|
||||
enclosing_stmt(#20096,#20092)
|
||||
expr_containers(#20096,#20001)
|
||||
#20098=*
|
||||
exprs(#20098,79,#20096,0,"M")
|
||||
exprs(#20098,103,#20096,0,"M")
|
||||
hasLocation(#20098,#20052)
|
||||
enclosing_stmt(#20098,#20092)
|
||||
expr_containers(#20098,#20001)
|
||||
literals("M","M",#20098)
|
||||
namespacebind(#20098,#20069)
|
||||
bind(#20098,#20066)
|
||||
#20099=*
|
||||
exprs(#20099,0,#20096,1,"N")
|
||||
|
||||
@@ -14,7 +14,7 @@ import DataFlow::PathGraph
|
||||
/**
|
||||
* Gets the name of an unescaped placeholder in a lodash template.
|
||||
*
|
||||
* For example, the string `<h1><%= title %></h1>` contains the placeholder `title`.
|
||||
* For example, the string `"<h1><%= title %></h1>"` contains the placeholder "title".
|
||||
*/
|
||||
bindingset[s]
|
||||
string getAPlaceholderInString(string s) {
|
||||
|
||||
@@ -175,7 +175,7 @@ predicate isOtherModeledArgument(DataFlow::Node n, FilteringReason reason) {
|
||||
or
|
||||
n instanceof CryptographicKey and reason instanceof CryptographicKeyReason
|
||||
or
|
||||
any(CryptographicOperation op).getInput().flow() = n and
|
||||
any(CryptographicOperation op).getInput() = n and
|
||||
reason instanceof CryptographicOperationFlowReason
|
||||
or
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
|
||||
@@ -16,220 +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()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This module provides functionality for getting a representation of the access path of nodes
|
||||
* within the program.
|
||||
*
|
||||
* For example, it gives the `User.find` callee here:
|
||||
*
|
||||
* ```js
|
||||
* const mongoose = require('mongoose'),
|
||||
* User = mongoose.model('User', null);
|
||||
* User.find({ 'isAdmin': true })
|
||||
* ```
|
||||
* the access path `mongoose member model instanceorreturn member find instanceorreturn`.
|
||||
*
|
||||
* This access path is based on the simplified access path that the untrusted data flowing to
|
||||
* external API query associates to each of its sinks, with modifications to optionally include
|
||||
* explicit structural information and to improve how well the path tokenizes.
|
||||
*/
|
||||
private module AccessPaths {
|
||||
bindingset[str]
|
||||
private predicate isNumericString(string str) { exists(str.toInt()) }
|
||||
|
||||
/**
|
||||
* Gets a parameter of `base` with name `name`, or a property named `name` of a destructuring parameter.
|
||||
*/
|
||||
private API::Node getNamedParameter(API::Node base, string name) {
|
||||
exists(API::Node param |
|
||||
param = base.getAParameter() and
|
||||
not param = base.getReceiver()
|
||||
|
|
||||
result = param and
|
||||
name = param.getAnImmediateUse().asExpr().(Parameter).getName()
|
||||
or
|
||||
param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and
|
||||
result = param.getMember(name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility class that is equivalent to `boolean` but does not require type joining.
|
||||
*/
|
||||
class Boolean extends boolean {
|
||||
Boolean() { this = true or this = false }
|
||||
}
|
||||
|
||||
/** Get the access path for the node. This includes structural information like `member`, `param`, and `functionalarg` if `includeStructuralInfo` is true. */
|
||||
predicate accessPaths(
|
||||
API::Node node, Boolean includeStructuralInfo, string accessPath, string apiName
|
||||
) {
|
||||
//node = API::moduleImport(result)
|
||||
node = API::moduleImport(apiName) and accessPath = apiName
|
||||
or
|
||||
exists(API::Node previousNode, string previousAccessPath |
|
||||
previousNode.getDepth() < node.getDepth() and
|
||||
accessPaths(previousNode, includeStructuralInfo, previousAccessPath, apiName)
|
||||
|
|
||||
// e.g. `new X`, `X()`
|
||||
node = [previousNode.getInstance(), previousNode.getReturn()] and
|
||||
if includeStructuralInfo = true
|
||||
then accessPath = previousAccessPath + " instanceorreturn"
|
||||
else accessPath = previousAccessPath
|
||||
or
|
||||
// e.g. `x.y`, `x[y]`, `const { y } = x`, where `y` is non-numeric and is known at analysis
|
||||
// time.
|
||||
exists(string member |
|
||||
node = previousNode.getMember(member) and
|
||||
not node = previousNode.getUnknownMember() and
|
||||
not isNumericString(member) and
|
||||
not (member = "default" and previousNode = API::moduleImport(_)) and
|
||||
not member = "then" // use the 'promised' edges for .then callbacks
|
||||
|
|
||||
if includeStructuralInfo = true
|
||||
then accessPath = previousAccessPath + " member " + member
|
||||
else accessPath = previousAccessPath + " " + member
|
||||
)
|
||||
or
|
||||
// e.g. `x.y`, `x[y]`, `const { y } = x`, where `y` is numeric or not known at analysis time.
|
||||
(
|
||||
node = previousNode.getUnknownMember() or
|
||||
node = previousNode.getMember(any(string s | isNumericString(s)))
|
||||
) and
|
||||
if includeStructuralInfo = true
|
||||
then accessPath = previousAccessPath + " member"
|
||||
else accessPath = previousAccessPath
|
||||
or
|
||||
// e.g. `x.then(y => ...)`
|
||||
node = previousNode.getPromised() and
|
||||
accessPath = previousAccessPath
|
||||
or
|
||||
// e.g. `x.y((a, b) => ...)`
|
||||
// Name callback parameters after their name in the source code.
|
||||
// For example, the `res` parameter in `express.get('/foo', (req, res) => {...})` will be
|
||||
// named `express member get functionalarg param res`.
|
||||
exists(string paramName |
|
||||
node = getNamedParameter(previousNode.getAParameter(), paramName) and
|
||||
(
|
||||
if includeStructuralInfo = true
|
||||
then accessPath = previousAccessPath + " functionalarg param " + paramName
|
||||
else accessPath = previousAccessPath + " " + paramName
|
||||
)
|
||||
or
|
||||
exists(string callbackName, int index |
|
||||
node =
|
||||
getNamedParameter(previousNode
|
||||
.getASuccessor(API::Label::parameter(index))
|
||||
.getMember(callbackName), paramName) and
|
||||
index != -1 and // ignore receiver
|
||||
if includeStructuralInfo = true
|
||||
then
|
||||
accessPath =
|
||||
previousAccessPath + " functionalarg " + index + " " + callbackName + " param " +
|
||||
paramName
|
||||
else accessPath = previousAccessPath + " " + index + " " + callbackName + " " + paramName
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
exists(EndpointFeature f | f.getName() = featureName and result = f.getValue(endpoint)) and
|
||||
featureName = getASupportedFeatureName()
|
||||
}
|
||||
|
||||
private module FunctionNames {
|
||||
@@ -284,13 +72,7 @@ private module FunctionNames {
|
||||
}
|
||||
|
||||
/** Get a name of a supported generic token-based feature. */
|
||||
string getASupportedFeatureName() {
|
||||
result =
|
||||
[
|
||||
"enclosingFunctionName", "calleeName", "receiverName", "argumentIndex", "calleeApiName",
|
||||
"calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody"
|
||||
]
|
||||
}
|
||||
string getASupportedFeatureName() { result = any(EndpointFeature f).getName() }
|
||||
|
||||
/**
|
||||
* Generic token-based features for ATM.
|
||||
@@ -303,3 +85,591 @@ 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: defines feature-name/value tuples for use in ML.
|
||||
*/
|
||||
abstract class EndpointFeature extends TEndpointFeature {
|
||||
/**
|
||||
* Gets the name of the feature. Used by the ML model.
|
||||
* Names are coupled to models: changing the name of a feature requires retraining the model.
|
||||
*/
|
||||
abstract string getName();
|
||||
|
||||
/**
|
||||
* Gets the value of the feature. Used by the ML model.
|
||||
* Models are trained based on feature values, so changing the value of a feature requires retraining the model.
|
||||
*/
|
||||
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)'.
|
||||
* The line breaks act as a separator between the parameters of different functions but
|
||||
* will be treated by tokenization as if they were spaces.
|
||||
*/
|
||||
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 of any properties an endpoint is assigned to (if any).
|
||||
*
|
||||
* ### Example
|
||||
* ```javascript
|
||||
* const div = document.createElement('div');
|
||||
* div.innerHTML = endpoint; // feature value is 'innerHTML'
|
||||
*
|
||||
* foo({x: endpoint}); // feature value is 'x'
|
||||
* ```
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* ### 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) { ... }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
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 {
|
||||
/**
|
||||
* Renders an operand in a string concatenation by surrounding a constant in quotes, and
|
||||
* by using `getSimpleAccessPath` for everything else.
|
||||
*/
|
||||
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 result 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 result 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 `?`.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* - The node `x.foo` will have the simple access path `x.foo`.
|
||||
* - In the following file, the simple access path will be `import("./foo").bar.baz`:
|
||||
*
|
||||
* ```javascript
|
||||
* import * as lib from "./foo"
|
||||
* console.log(lib.bar.baz());
|
||||
* // ^^^^^^^^^^^ node
|
||||
*/
|
||||
string getSimpleAccessPath(DataFlow::Node node) {
|
||||
exists(Expr e | e = node.asExpr().getUnderlyingValue() |
|
||||
if
|
||||
e instanceof SuperAccess or
|
||||
e instanceof ThisAccess or
|
||||
e instanceof VarAccess or
|
||||
e instanceof Import or
|
||||
e instanceof AwaitExpr or
|
||||
node instanceof DataFlow::PropRead or
|
||||
node instanceof DataFlow::InvokeNode
|
||||
then
|
||||
e instanceof SuperAccess and result = "super"
|
||||
or
|
||||
e instanceof ThisAccess and result = "this"
|
||||
or
|
||||
e instanceof VarAccess and result = e.(VarAccess).getName()
|
||||
or
|
||||
e instanceof Import and result = "import(" + getSimpleImportPath(e) + ")"
|
||||
or
|
||||
e instanceof AwaitExpr and
|
||||
result = "(await " + getSimpleAccessPath(e.(AwaitExpr).getOperand().flow()) + ")"
|
||||
or
|
||||
node instanceof DataFlow::PropRead and
|
||||
result =
|
||||
getSimpleAccessPath(node.(DataFlow::PropRead).getBase()) + "." +
|
||||
getPropertyNameOrUnknown(node)
|
||||
or
|
||||
(node instanceof DataFlow::InvokeNode and not e instanceof Import) and
|
||||
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() |
|
||||
// Hide absolute imports from ML training data.
|
||||
// ============================================
|
||||
// There is the hypothesis that exposing absolute imports to the model
|
||||
// might lead to bad generalization. For example, the model might learn
|
||||
// to strongly associate a specific database client with sinks and no
|
||||
// longer be able to flag sinks when data flow is broken.
|
||||
// Placing this logic so deeply within the feature extraction code is
|
||||
// perhaps a bit of a hack and it is a use case to consider when refactoring
|
||||
// endpoint filters/data extraction.
|
||||
if p.matches(".%") then result = "\"p\"" else result = "!"
|
||||
)
|
||||
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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,10 @@ module SinkEndpointFilter {
|
||||
result = "modeled database access"
|
||||
or
|
||||
// Remove calls to APIs that aren't relevant to NoSQL injection
|
||||
call.getReceiver().asExpr() instanceof HTTP::RequestExpr and
|
||||
call.getReceiver() instanceof Http::RequestNode and
|
||||
result = "receiver is a HTTP request expression"
|
||||
or
|
||||
call.getReceiver().asExpr() instanceof HTTP::ResponseExpr and
|
||||
call.getReceiver() instanceof Http::ResponseNode and
|
||||
result = "receiver is a HTTP response expression"
|
||||
)
|
||||
or
|
||||
@@ -115,7 +115,7 @@ predicate isBaseAdditionalFlowStep(
|
||||
inlbl = TaintedObject::label() and
|
||||
outlbl = TaintedObject::label() and
|
||||
exists(NoSql::Query query, DataFlow::SourceNode queryObj |
|
||||
queryObj.flowsToExpr(query) and
|
||||
queryObj.flowsTo(query) and
|
||||
queryObj.flowsTo(trg) and
|
||||
src = queryObj.getAPropertyWrite().getRhs()
|
||||
)
|
||||
|
||||
@@ -15,16 +15,16 @@ import extraction.ExtractEndpointData
|
||||
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate, Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result = NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof SqlInjectionQuery and
|
||||
result = SqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof TaintedPathQuery and
|
||||
result = TaintedPathATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof XssQuery and
|
||||
result = XssATM::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for a particular dataflow config.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import evaluation.EndToEndEvaluation
|
||||
|
||||
query predicate countAlertsAndSinks(int numAlerts, int numSinks) {
|
||||
numAlerts =
|
||||
count(DataFlow::Configuration cfg, DataFlow::Node source, DataFlow::Node sink |
|
||||
cfg.hasFlow(source, sink) and not isFlowExcluded(source, sink)
|
||||
) and
|
||||
numSinks =
|
||||
count(DataFlow::Node sink |
|
||||
exists(DataFlow::Configuration cfg | cfg.isSink(sink) or cfg.isSink(sink, _))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for the `CodeInjection` security query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.CodeInjectionQuery
|
||||
import CountAlertsAndSinks
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for the `NosqlInection` security query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.NosqlInjectionQuery
|
||||
import CountAlertsAndSinks
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for the `SqlInection` security query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.SqlInjectionQuery
|
||||
import CountAlertsAndSinks
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for the `TaintedPath` security query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.TaintedPathQuery
|
||||
import CountAlertsAndSinks
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for the `DomBasedXss` security query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.DomBasedXssQuery
|
||||
import CountAlertsAndSinks
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
* Count the number of sinks and alerts for the `XssThroughDom` security query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.XssThroughDomQuery
|
||||
import CountAlertsAndSinks
|
||||
@@ -5,7 +5,8 @@
|
||||
* evaluation pipeline.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.NosqlInjection
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.NosqlInjectionQuery as NosqlInjection
|
||||
import EndToEndEvaluation as EndToEndEvaluation
|
||||
|
||||
from
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* evaluation pipeline.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.SqlInjection
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.SqlInjectionQuery as SqlInjection
|
||||
import EndToEndEvaluation as EndToEndEvaluation
|
||||
|
||||
from
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* evaluation pipeline.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.TaintedPath
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.TaintedPathQuery as TaintedPath
|
||||
import EndToEndEvaluation as EndToEndEvaluation
|
||||
|
||||
from
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* pipeline.
|
||||
*/
|
||||
|
||||
import semmle.javascript.security.dataflow.DomBasedXss
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.DomBasedXssQuery as DomBasedXss
|
||||
import EndToEndEvaluation as EndToEndEvaluation
|
||||
|
||||
from
|
||||
|
||||
@@ -14,10 +14,26 @@ import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.EndpointScoring as EndpointScoring
|
||||
import experimental.adaptivethreatmodeling.EndpointTypes
|
||||
import experimental.adaptivethreatmodeling.FilteringReasons
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
|
||||
/** DEPRECATED: Alias for NosqlInjectionAtm */
|
||||
deprecated module NosqlInjectionATM = NosqlInjectionAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
|
||||
/** DEPRECATED: Alias for SqlInjectionAtm */
|
||||
deprecated module SqlInjectionATM = SqlInjectionAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
|
||||
/** DEPRECATED: Alias for TaintedPathAtm */
|
||||
deprecated module TaintedPathATM = TaintedPathAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
/** DEPRECATED: Alias for XssAtm */
|
||||
deprecated module XssATM = XssAtm;
|
||||
|
||||
import Labels
|
||||
import NoFeaturizationRestrictionsConfig
|
||||
import Queries
|
||||
@@ -25,13 +41,13 @@ import Queries
|
||||
/** Gets the ATM configuration object for the specified query. */
|
||||
AtmConfig getAtmCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result instanceof NosqlInjectionATM::NosqlInjectionAtmConfig
|
||||
result instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionATM::SqlInjectionAtmConfig
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathATM::TaintedPathAtmConfig
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssATM::DomBasedXssAtmConfig
|
||||
query instanceof XssQuery and result instanceof XssAtm::DomBasedXssAtmConfig
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getAtmCfg */
|
||||
@@ -39,13 +55,13 @@ deprecated ATMConfig getATMCfg(Query query) { result = getAtmCfg(query) }
|
||||
|
||||
/** Gets the ATM data flow configuration for the specified query. */
|
||||
DataFlow::Configuration getDataFlowCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionATM::Configuration
|
||||
query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionAtm::Configuration
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionATM::Configuration
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::Configuration
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathATM::Configuration
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::Configuration
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssATM::Configuration
|
||||
query instanceof XssQuery and result instanceof XssAtm::Configuration
|
||||
}
|
||||
|
||||
/** Gets a known sink for the specified query. */
|
||||
@@ -188,10 +204,10 @@ module FlowFromSource {
|
||||
|
||||
Query getQuery() { result = q }
|
||||
|
||||
/** The sinks are the endpoints we're extracting. */
|
||||
/** Holds if `sink` is an endpoint we're extracting. */
|
||||
override predicate isSink(DataFlow::Node sink) { sink = getAnEndpoint(q) }
|
||||
|
||||
/** The sinks are the endpoints we're extracting. */
|
||||
/** Holds if `sink` is an endpoint we're extracting. */
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) {
|
||||
sink = getAnEndpoint(q) and exists(lbl)
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
* Maps ML-powered queries to their `EndpointType` for clearer labelling while evaluating ML model during training.
|
||||
*/
|
||||
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.AdaptiveThreatModeling
|
||||
|
||||
from string queryName, AtmConfig c, EndpointType e
|
||||
where
|
||||
(
|
||||
queryName = "SqlInjection" and
|
||||
c instanceof SqlInjectionATM::SqlInjectionAtmConfig
|
||||
c instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
queryName = "NosqlInjection" and
|
||||
c instanceof NosqlInjectionATM::NosqlInjectionAtmConfig
|
||||
c instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
queryName = "TaintedPath" and
|
||||
c instanceof TaintedPathATM::TaintedPathAtmConfig
|
||||
c instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
queryName = "Xss" and c instanceof XssATM::DomBasedXssAtmConfig
|
||||
queryName = "Xss" and c instanceof XssAtm::DomBasedXssAtmConfig
|
||||
) and
|
||||
e = c.getASinkEndpointType()
|
||||
select queryName, e.getEncoding() as label
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,20 +7,20 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters as StandardEndpointFilters
|
||||
import extraction.NoFeaturizationRestrictionsConfig
|
||||
|
||||
query predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) {
|
||||
(
|
||||
not exists(NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(SqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(TaintedPathATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(XssATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
StandardEndpointFilters::isArgumentToModeledFunction(endpoint)
|
||||
) and
|
||||
EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue)
|
||||
|
||||
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
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* This test checks several components of the endpoint filters for each query to see whether they
|
||||
* filter out any known sinks. It explicitly does not check the endpoint filtering step that's based
|
||||
* on whether the endpoint is an argument to a modelled function, since this necessarily filters out
|
||||
* on whether the endpoint is an argument to a modeled function, since this necessarily filters out
|
||||
* all known sinks. However, we can test all the other filtering steps against the set of known
|
||||
* sinks.
|
||||
*
|
||||
@@ -17,31 +17,31 @@ import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
import semmle.javascript.security.dataflow.DomBasedXssCustomizations
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters as StandardEndpointFilters
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathATM
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
query predicate nosqlFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof NosqlInjection::Sink and
|
||||
reason = NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
not reason = ["argument to modeled function", "modeled sink", "modeled database access"]
|
||||
}
|
||||
|
||||
query predicate sqlFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof SqlInjection::Sink and
|
||||
reason = SqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate taintedPathFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof TaintedPath::Sink and
|
||||
reason = TaintedPathATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate xssFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof DomBasedXss::Sink and
|
||||
reason = XssATM::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -1,6 +1,6 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionATM
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
|
||||
query predicate effectiveSinks(DataFlow::Node node) {
|
||||
not exists(NosqlInjectionATM::SinkEndpointFilter::getAReasonSinkExcluded(node))
|
||||
not exists(NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(node))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,131 @@
|
||||
## 0.3.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
- Several of the SQL and NoSQL library models have improved, leading to more results for the `js/sql-injection` query,
|
||||
and in some cases the `js/missing-rate-limiting` query.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Many library models have been rewritten to use dataflow nodes instead of the AST.
|
||||
The types of some classes have been changed, and these changes may break existing code.
|
||||
Other classes and predicates have been renamed, in these cases the old name is still available as a deprecated feature.
|
||||
|
||||
* The basetype of the following list of classes has changed from an expression to a dataflow node, and thus code using these classes might break.
|
||||
The fix to these breakages is usually to use `asExpr()` to get an expression from a dataflow node, or to use `.flow()` to get a dataflow node from an expression.
|
||||
- DOM.qll#WebStorageWrite
|
||||
- CryptoLibraries.qll#CryptographicOperation
|
||||
- Express.qll#Express::RequestBodyAccess
|
||||
- HTTP.qll#HTTP::ResponseBody
|
||||
- HTTP.qll#HTTP::CookieDefinition
|
||||
- HTTP.qll#HTTP::ServerDefinition
|
||||
- HTTP.qll#HTTP::RouteSetup
|
||||
- NoSQL.qll#NoSql::Query
|
||||
- SQL.qll#SQL::SqlString
|
||||
- SQL.qll#SQL::SqlSanitizer
|
||||
- HTTP.qll#ResponseBody
|
||||
- HTTP.qll#CookieDefinition
|
||||
- HTTP.qll#ServerDefinition
|
||||
- HTTP.qll#RouteSetup
|
||||
- HTTP.qll#HTTP::RedirectInvocation
|
||||
- HTTP.qll#RedirectInvocation
|
||||
- Express.qll#Express::RouterDefinition
|
||||
- AngularJSCore.qll#LinkFunction
|
||||
- Connect.qll#Connect::StandardRouteHandler
|
||||
- CryptoLibraries.qll#CryptographicKeyCredentialsExpr
|
||||
- AWS.qll#AWS::Credentials
|
||||
- Azure.qll#Azure::Credentials
|
||||
- Connect.qll#Connect::Credentials
|
||||
- DigitalOcean.qll#DigitalOcean::Credentials
|
||||
- Express.qll#Express::Credentials
|
||||
- NodeJSLib.qll#NodeJSLib::Credentials
|
||||
- PkgCloud.qll#PkgCloud::Credentials
|
||||
- Request.qll#Request::Credentials
|
||||
- ServiceDefinitions.qll#InjectableFunctionServiceRequest
|
||||
- SensitiveActions.qll#SensitiveVariableAccess
|
||||
- SensitiveActions.qll#CleartextPasswordExpr
|
||||
- Connect.qll#Connect::ServerDefinition
|
||||
- Restify.qll#Restify::ServerDefinition
|
||||
- Connect.qll#Connect::RouteSetup
|
||||
- Express.qll#Express::RouteSetup
|
||||
- Fastify.qll#Fastify::RouteSetup
|
||||
- Hapi.qll#Hapi::RouteSetup
|
||||
- Koa.qll#Koa::RouteSetup
|
||||
- Restify.qll#Restify::RouteSetup
|
||||
- NodeJSLib.qll#NodeJSLib::RouteSetup
|
||||
- Express.qll#Express::StandardRouteHandler
|
||||
- Express.qll#Express::SetCookie
|
||||
- Hapi.qll#Hapi::RouteHandler
|
||||
- HTTP.qll#HTTP::Servers::StandardHeaderDefinition
|
||||
- HTTP.qll#Servers::StandardHeaderDefinition
|
||||
- Hapi.qll#Hapi::ServerDefinition
|
||||
- Koa.qll#Koa::AppDefinition
|
||||
- SensitiveActions.qll#SensitiveCall
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added support for TypeScript 4.8.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* A model for the `mermaid` library has been added. XSS queries can now detect flow through the `render` method of the `mermaid` library.
|
||||
|
||||
## 0.2.5
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
* The utility files previously in the `semmle.javascript.security.performance` package have been moved to the `semmle.javascript.security.regexp` package.
|
||||
The previous files still exist as deprecated aliases.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Most deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed that top-level `for await` statements would produce a syntax error. These statements are now parsed correctly.
|
||||
|
||||
## 0.2.3
|
||||
|
||||
## 0.2.2
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
|
||||
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
|
||||
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added support for TypeScript 4.7.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All new ECMAScript 2022 features are now supported.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `isLibaryFile` predicate from `ClassifyFiles.qll` has been renamed to `isLibraryFile` to fix a typo.
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
@@ -37,6 +37,8 @@ predicate inVoidContext(Expr e) {
|
||||
)
|
||||
or
|
||||
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
|
||||
or
|
||||
exists(ConditionalExpr cond | e = cond.getABranch() | inVoidContext(cond))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.1.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `isLibaryFile` predicate from `ClassifyFiles.qll` has been renamed to `isLibraryFile` to fix a typo.
|
||||
1
javascript/ql/lib/change-notes/released/0.1.4.md
Normal file
1
javascript/ql/lib/change-notes/released/0.1.4.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.1.4
|
||||
9
javascript/ql/lib/change-notes/released/0.2.0.md
Normal file
9
javascript/ql/lib/change-notes/released/0.2.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.2.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added support for TypeScript 4.7.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All new ECMAScript 2022 features are now supported.
|
||||
7
javascript/ql/lib/change-notes/released/0.2.1.md
Normal file
7
javascript/ql/lib/change-notes/released/0.2.1.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## 0.2.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
|
||||
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
|
||||
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.
|
||||
1
javascript/ql/lib/change-notes/released/0.2.2.md
Normal file
1
javascript/ql/lib/change-notes/released/0.2.2.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.2.2
|
||||
1
javascript/ql/lib/change-notes/released/0.2.3.md
Normal file
1
javascript/ql/lib/change-notes/released/0.2.3.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.2.3
|
||||
16
javascript/ql/lib/change-notes/released/0.2.4.md
Normal file
16
javascript/ql/lib/change-notes/released/0.2.4.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 0.2.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
* The utility files previously in the `semmle.javascript.security.performance` package have been moved to the `semmle.javascript.security.regexp` package.
|
||||
The previous files still exist as deprecated aliases.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Most deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed that top-level `for await` statements would produce a syntax error. These statements are now parsed correctly.
|
||||
1
javascript/ql/lib/change-notes/released/0.2.5.md
Normal file
1
javascript/ql/lib/change-notes/released/0.2.5.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.2.5
|
||||
71
javascript/ql/lib/change-notes/released/0.3.0.md
Normal file
71
javascript/ql/lib/change-notes/released/0.3.0.md
Normal file
@@ -0,0 +1,71 @@
|
||||
## 0.3.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Many library models have been rewritten to use dataflow nodes instead of the AST.
|
||||
The types of some classes have been changed, and these changes may break existing code.
|
||||
Other classes and predicates have been renamed, in these cases the old name is still available as a deprecated feature.
|
||||
|
||||
* The basetype of the following list of classes has changed from an expression to a dataflow node, and thus code using these classes might break.
|
||||
The fix to these breakages is usually to use `asExpr()` to get an expression from a dataflow node, or to use `.flow()` to get a dataflow node from an expression.
|
||||
- DOM.qll#WebStorageWrite
|
||||
- CryptoLibraries.qll#CryptographicOperation
|
||||
- Express.qll#Express::RequestBodyAccess
|
||||
- HTTP.qll#HTTP::ResponseBody
|
||||
- HTTP.qll#HTTP::CookieDefinition
|
||||
- HTTP.qll#HTTP::ServerDefinition
|
||||
- HTTP.qll#HTTP::RouteSetup
|
||||
- NoSQL.qll#NoSql::Query
|
||||
- SQL.qll#SQL::SqlString
|
||||
- SQL.qll#SQL::SqlSanitizer
|
||||
- HTTP.qll#ResponseBody
|
||||
- HTTP.qll#CookieDefinition
|
||||
- HTTP.qll#ServerDefinition
|
||||
- HTTP.qll#RouteSetup
|
||||
- HTTP.qll#HTTP::RedirectInvocation
|
||||
- HTTP.qll#RedirectInvocation
|
||||
- Express.qll#Express::RouterDefinition
|
||||
- AngularJSCore.qll#LinkFunction
|
||||
- Connect.qll#Connect::StandardRouteHandler
|
||||
- CryptoLibraries.qll#CryptographicKeyCredentialsExpr
|
||||
- AWS.qll#AWS::Credentials
|
||||
- Azure.qll#Azure::Credentials
|
||||
- Connect.qll#Connect::Credentials
|
||||
- DigitalOcean.qll#DigitalOcean::Credentials
|
||||
- Express.qll#Express::Credentials
|
||||
- NodeJSLib.qll#NodeJSLib::Credentials
|
||||
- PkgCloud.qll#PkgCloud::Credentials
|
||||
- Request.qll#Request::Credentials
|
||||
- ServiceDefinitions.qll#InjectableFunctionServiceRequest
|
||||
- SensitiveActions.qll#SensitiveVariableAccess
|
||||
- SensitiveActions.qll#CleartextPasswordExpr
|
||||
- Connect.qll#Connect::ServerDefinition
|
||||
- Restify.qll#Restify::ServerDefinition
|
||||
- Connect.qll#Connect::RouteSetup
|
||||
- Express.qll#Express::RouteSetup
|
||||
- Fastify.qll#Fastify::RouteSetup
|
||||
- Hapi.qll#Hapi::RouteSetup
|
||||
- Koa.qll#Koa::RouteSetup
|
||||
- Restify.qll#Restify::RouteSetup
|
||||
- NodeJSLib.qll#NodeJSLib::RouteSetup
|
||||
- Express.qll#Express::StandardRouteHandler
|
||||
- Express.qll#Express::SetCookie
|
||||
- Hapi.qll#Hapi::RouteHandler
|
||||
- HTTP.qll#HTTP::Servers::StandardHeaderDefinition
|
||||
- HTTP.qll#Servers::StandardHeaderDefinition
|
||||
- Hapi.qll#Hapi::ServerDefinition
|
||||
- Koa.qll#Koa::AppDefinition
|
||||
- SensitiveActions.qll#SensitiveCall
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added support for TypeScript 4.8.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* A model for the `mermaid` library has been added. XSS queries can now detect flow through the `render` method of the `mermaid` library.
|
||||
6
javascript/ql/lib/change-notes/released/0.3.1.md
Normal file
6
javascript/ql/lib/change-notes/released/0.3.1.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## 0.3.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
- Several of the SQL and NoSQL library models have improved, leading to more results for the `js/sql-injection` query,
|
||||
and in some cases the `js/missing-rate-limiting` query.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.2
|
||||
lastReleaseVersion: 0.3.1
|
||||
|
||||
@@ -45,7 +45,7 @@ private predicate variableDefLookup(VarAccess va, AstNode def, string kind) {
|
||||
|
||||
/**
|
||||
* Holds if variable access `va` is of kind `kind` and refers to the
|
||||
* variable declaration.
|
||||
* variable declaration `decl`.
|
||||
*
|
||||
* For example, in the statement `var x = 42, y = x;`, the initializing
|
||||
* expression of `y` is a variable access `x` of kind `"V"` that refers to
|
||||
@@ -99,6 +99,7 @@ import semmle.javascript.frameworks.JWT
|
||||
import semmle.javascript.frameworks.Handlebars
|
||||
import semmle.javascript.frameworks.History
|
||||
import semmle.javascript.frameworks.Immutable
|
||||
import semmle.javascript.frameworks.ImportGeneratedModels
|
||||
import semmle.javascript.frameworks.Knex
|
||||
import semmle.javascript.frameworks.LazyCache
|
||||
import semmle.javascript.frameworks.LdapJS
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-all
|
||||
version: 0.1.3-dev
|
||||
version: 0.3.2-dev
|
||||
groups: javascript
|
||||
dbscheme: semmlecode.javascript.dbscheme
|
||||
extractor: javascript
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
private import Expressions.ExprHasNoEffect
|
||||
|
||||
/**
|
||||
* An AMD `define` call.
|
||||
@@ -26,7 +27,7 @@ private import semmle.javascript.internal.CachedStages
|
||||
*/
|
||||
class AmdModuleDefinition extends CallExpr {
|
||||
AmdModuleDefinition() {
|
||||
getParent() instanceof ExprStmt and
|
||||
inVoidContext(this) and
|
||||
getCallee().(GlobalVarAccess).getName() = "define" and
|
||||
exists(int n | n = getNumArgument() |
|
||||
n = 1
|
||||
@@ -202,13 +203,22 @@ private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString
|
||||
override string getValue() { result = getStringValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nd` is nested inside an AMD module definition.
|
||||
*/
|
||||
private predicate inAmdModuleDefinition(AstNode nd) {
|
||||
nd.getParent() instanceof AmdModuleDefinition
|
||||
or
|
||||
inAmdModuleDefinition(nd.getParent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is an AMD module definition in `tl` which is not
|
||||
* nested inside another module definition.
|
||||
*/
|
||||
private predicate amdModuleTopLevel(AmdModuleDefinition def, TopLevel tl) {
|
||||
def.getTopLevel() = tl and
|
||||
not def.getParent+() instanceof AmdModuleDefinition
|
||||
not inAmdModuleDefinition(def)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import javascript
|
||||
*/
|
||||
module Actions {
|
||||
/** A YAML node in a GitHub Actions workflow file. */
|
||||
private class Node extends YAMLNode {
|
||||
private class Node extends YamlNode {
|
||||
Node() {
|
||||
this.getLocation()
|
||||
.getFile()
|
||||
@@ -24,9 +24,12 @@ module Actions {
|
||||
* An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
|
||||
*/
|
||||
class Workflow extends Node, YAMLDocument, YAMLMapping {
|
||||
class Workflow extends Node, YamlDocument, YamlMapping {
|
||||
/** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */
|
||||
YAMLMapping getJobs() { result = this.lookup("jobs") }
|
||||
YamlMapping getJobs() { result = this.lookup("jobs") }
|
||||
|
||||
/** Gets the name of the workflow. */
|
||||
string getName() { result = this.lookup("name").(YamlString).getValue() }
|
||||
|
||||
/** Gets the name of the workflow file. */
|
||||
string getFileName() { result = this.getFile().getBaseName() }
|
||||
@@ -42,7 +45,7 @@ module Actions {
|
||||
* An Actions On trigger within a workflow.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#on.
|
||||
*/
|
||||
class On extends YAMLNode, YAMLMappingLikeNode {
|
||||
class On extends YamlNode, YamlMappingLikeNode {
|
||||
Workflow workflow;
|
||||
|
||||
On() { workflow.lookup("on") = this }
|
||||
@@ -55,7 +58,7 @@ module Actions {
|
||||
* An Actions job within a workflow.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs.
|
||||
*/
|
||||
class Job extends YAMLNode, YAMLMapping {
|
||||
class Job extends YamlNode, YamlMapping {
|
||||
string jobId;
|
||||
Workflow workflow;
|
||||
|
||||
@@ -71,32 +74,35 @@ module Actions {
|
||||
* Gets the ID of this job, as a YAML scalar node.
|
||||
* This is the job's key within the `jobs` mapping.
|
||||
*/
|
||||
YAMLString getIdNode() { workflow.getJobs().maps(result, this) }
|
||||
YamlString getIdNode() { workflow.getJobs().maps(result, this) }
|
||||
|
||||
/** Gets the human-readable name of this job, if any, as a string. */
|
||||
string getName() { result = this.getNameNode().getValue() }
|
||||
|
||||
/** Gets the human-readable name of this job, if any, as a YAML scalar node. */
|
||||
YAMLString getNameNode() { result = this.lookup("name") }
|
||||
YamlString getNameNode() { result = this.lookup("name") }
|
||||
|
||||
/** Gets the step at the given index within this job. */
|
||||
Step getStep(int index) { result.getJob() = this and result.getIndex() = index }
|
||||
|
||||
/** Gets the sequence of `steps` within this job. */
|
||||
YAMLSequence getSteps() { result = this.lookup("steps") }
|
||||
YamlSequence getSteps() { result = this.lookup("steps") }
|
||||
|
||||
/** Gets the workflow this job belongs to. */
|
||||
Workflow getWorkflow() { result = workflow }
|
||||
|
||||
/** Gets the value of the `if` field in this job, if any. */
|
||||
JobIf getIf() { result.getJob() = this }
|
||||
|
||||
/** Gets the value of the `runs-on` field in this job. */
|
||||
JobRunson getRunsOn() { result.getJob() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `if` within a job.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif.
|
||||
*/
|
||||
class JobIf extends YAMLNode, YAMLScalar {
|
||||
class JobIf extends YamlNode, YamlScalar {
|
||||
Job job;
|
||||
|
||||
JobIf() { job.lookup("if") = this }
|
||||
@@ -105,11 +111,24 @@ module Actions {
|
||||
Job getJob() { result = job }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `runs-on` within a job.
|
||||
* See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on.
|
||||
*/
|
||||
class JobRunson extends YamlNode, YamlScalar {
|
||||
Job job;
|
||||
|
||||
JobRunson() { job.lookup("runs-on") = this }
|
||||
|
||||
/** Gets the step this field belongs to. */
|
||||
Job getJob() { result = job }
|
||||
}
|
||||
|
||||
/**
|
||||
* A step within an Actions job.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps.
|
||||
*/
|
||||
class Step extends YAMLNode, YAMLMapping {
|
||||
class Step extends YamlNode, YamlMapping {
|
||||
int index;
|
||||
Job job;
|
||||
|
||||
@@ -129,13 +148,16 @@ module Actions {
|
||||
|
||||
/** Gets the value of the `if` field in this step, if any. */
|
||||
StepIf getIf() { result.getStep() = this }
|
||||
|
||||
/** Gets the ID of this step, if any. */
|
||||
string getId() { result = this.lookup("id").(YamlString).getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `if` within a step.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsif.
|
||||
*/
|
||||
class StepIf extends YAMLNode, YAMLScalar {
|
||||
class StepIf extends YamlNode, YamlScalar {
|
||||
Step step;
|
||||
|
||||
StepIf() { step.lookup("if") = this }
|
||||
@@ -164,7 +186,7 @@ module Actions {
|
||||
*
|
||||
* Does not handle local repository references, e.g. `.github/actions/action-name`.
|
||||
*/
|
||||
class Uses extends YAMLNode, YAMLScalar {
|
||||
class Uses extends YamlNode, YamlScalar {
|
||||
Step step;
|
||||
|
||||
Uses() { step.lookup("uses") = this }
|
||||
@@ -194,7 +216,7 @@ module Actions {
|
||||
* arg2: abc
|
||||
* ```
|
||||
*/
|
||||
class With extends YAMLNode, YAMLMapping {
|
||||
class With extends YamlNode, YamlMapping {
|
||||
Step step;
|
||||
|
||||
With() { step.lookup("with") = this }
|
||||
@@ -213,7 +235,7 @@ module Actions {
|
||||
* ref: ${{ github.event.pull_request.head.sha }}
|
||||
* ```
|
||||
*/
|
||||
class Ref extends YAMLNode, YAMLString {
|
||||
class Ref extends YamlNode, YamlString {
|
||||
With with;
|
||||
|
||||
Ref() { with.lookup("ref") = this }
|
||||
@@ -226,7 +248,7 @@ module Actions {
|
||||
* A `run` field within an Actions job step, which runs command-line programs using an operating system shell.
|
||||
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
|
||||
*/
|
||||
class Run extends YAMLNode, YAMLString {
|
||||
class Run extends YamlNode, YamlString {
|
||||
Step step;
|
||||
|
||||
Run() { step.lookup("run") = this }
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
* Provides an implementation of _API graphs_, which are an abstract representation of the API
|
||||
* surface used and/or defined by a code base.
|
||||
*
|
||||
* The nodes of the API graph represent definitions and uses of API components. The edges are
|
||||
* directed and labeled; they specify how the components represented by nodes relate to each other.
|
||||
* For example, if one of the nodes represents a definition of an API function, then there
|
||||
* will be nodes corresponding to the function's parameters, which are connected to the function
|
||||
* node by edges labeled `parameter <i>`.
|
||||
* See `API::Node` for more in-depth documentation.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
@@ -14,50 +10,159 @@ private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
||||
private import internal.CachedStages
|
||||
|
||||
/**
|
||||
* Provides classes and predicates for working with APIs defined or used in a database.
|
||||
* Provides classes and predicates for working with the API boundary between the current
|
||||
* codebase and external libraries.
|
||||
*
|
||||
* See `API::Node` for more in-depth documentation.
|
||||
*/
|
||||
module API {
|
||||
/**
|
||||
* An abstract representation of a definition or use of an API component such as a function
|
||||
* exported by an npm package, a parameter of such a function, or its result.
|
||||
* A node in the API graph, representing a value that has crossed the boundary between this
|
||||
* codebase and an external library (or in general, any external codebase).
|
||||
*
|
||||
* ### Basic usage
|
||||
*
|
||||
* API graphs are typically used to identify "API calls", that is, calls to an external function
|
||||
* whose implementation is not necessarily part of the current codebase.
|
||||
*
|
||||
* The most basic use of API graphs is typically as follows:
|
||||
* 1. Start with `API::moduleImport` for the relevant library.
|
||||
* 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function.
|
||||
* 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`.
|
||||
*
|
||||
* For example, a simplified way to get arguments to `underscore.extend` would be
|
||||
* ```ql
|
||||
* API::moduleImport("underscore").getMember("extend").getParameter(0).asSink()
|
||||
* ```
|
||||
*
|
||||
* The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`.
|
||||
*
|
||||
* ### API graph nodes
|
||||
*
|
||||
* There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value:
|
||||
* - **Use-nodes** represent values held by the current codebase, which came from an external library.
|
||||
* (The current codebase is "using" a value that came from the library).
|
||||
* - **Def-nodes** represent values held by the external library, which came from this codebase.
|
||||
* (The current codebase "defines" the value seen by the library).
|
||||
*
|
||||
* API graph nodes are associated with data-flow nodes in the current codebase.
|
||||
* (Since external libraries are not part of the database, there is no way to associate with concrete
|
||||
* data-flow nodes from the external library).
|
||||
* - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase,
|
||||
* such as the return value of a call to an external function.
|
||||
* - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase,
|
||||
* such as an argument passed in a call to an external function.
|
||||
*
|
||||
*
|
||||
* ### Access paths and edge labels
|
||||
*
|
||||
* Nodes in the API graph are associated with a set of access paths, describing a series of operations
|
||||
* that may be performed to obtain that value.
|
||||
*
|
||||
* For example, the access path `API::moduleImport("lodash").getMember("extend")` represents the action of
|
||||
* importing `lodash` and then accessing the member `extend` on the resulting object.
|
||||
* It would be associated with an expression such as `require("lodash").extend`.
|
||||
*
|
||||
* Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node
|
||||
* determines who is performing the operation, and the type of the `B` node determines who ends up holding
|
||||
* the result:
|
||||
* - An edge starting from a use-node describes what the current codebase is doing to a value that
|
||||
* came from a library.
|
||||
* - An edge starting from a def-node describes what the external library might do to a value that
|
||||
* came from the current codebase.
|
||||
* - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node).
|
||||
* - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is
|
||||
* the place where it was "last seen" in the current codebase before flowing out)
|
||||
*
|
||||
* Because the implementation of the external library is not visible, it is not known exactly what operations
|
||||
* it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would
|
||||
* lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform
|
||||
* those operations. (When constructing these edges, we assume the library is somewhat well-behaved).
|
||||
*
|
||||
* For example, given this snippet:
|
||||
* ```js
|
||||
* require('foo')(x => { doSomething(x) })
|
||||
* ```
|
||||
* A callback is passed to the external function `foo`. We can't know if `foo` will actually invoke this callback.
|
||||
* But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter.
|
||||
* For that reason, an edge is generated representing the argument-passing operation that might be performed by `foo`.
|
||||
* This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x`.
|
||||
*
|
||||
* ### Thinking in operations versus code patterns
|
||||
*
|
||||
* Treating edges as "operations" helps avoid a pitfall in which library models become overly specific to certain code patterns.
|
||||
* Consider the following two equivalent calls to `foo`:
|
||||
* ```js
|
||||
* const foo = require('foo');
|
||||
*
|
||||
* foo({
|
||||
* myMethod(x) {...}
|
||||
* });
|
||||
*
|
||||
* foo({
|
||||
* get myMethod() {
|
||||
* return function(x) {...}
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
* If `foo` calls `myMethod` on its first parameter, either of the `myMethod` implementations will be invoked.
|
||||
* And indeed, the access path `API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0)` correctly
|
||||
* identifies both `x` parameters.
|
||||
*
|
||||
* Observe how `getMember("myMethod")` behaves when the member is defined via a getter. When thinking in code patterns,
|
||||
* it might seem obvious that `getMember` should have obtained a reference to the getter method itself.
|
||||
* But when seeing it as an access to `myMethod` performed by the library, we can deduce that the relevant expression
|
||||
* on the client side is actually the return-value of the getter.
|
||||
*
|
||||
* Although one may think of API graphs as a tool to find certain program elements in the codebase,
|
||||
* it can lead to some situations where intuition does not match what works best in practice.
|
||||
*/
|
||||
class Node extends Impl::TApiNode {
|
||||
/**
|
||||
* Gets a data-flow node corresponding to a use of the API component represented by this node.
|
||||
* Get a data-flow node where this value may flow after entering the current codebase.
|
||||
*
|
||||
* For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the
|
||||
* `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function.
|
||||
*
|
||||
* This includes indirect uses found via data flow, meaning that in
|
||||
* `f(obj.foo); function f(x) {};` both `obj.foo` and `x` are uses of the `foo` member from `obj`.
|
||||
*
|
||||
* As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to
|
||||
* `x` are uses of the first parameter of `plusOne`.
|
||||
* This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow.
|
||||
* See `asSource()` for examples.
|
||||
*/
|
||||
pragma[inline]
|
||||
DataFlow::Node getAUse() {
|
||||
exists(DataFlow::SourceNode src | Impl::use(this, src) |
|
||||
Impl::trackUseNode(src).flowsTo(result)
|
||||
)
|
||||
DataFlow::Node getAValueReachableFromSource() {
|
||||
Impl::trackUseNode(this.asSource()).flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an immediate use of the API component represented by this node.
|
||||
* Get a data-flow node where this value enters the current codebase.
|
||||
*
|
||||
* For example, `require('fs').readFileSync` is a an immediate use of the `readFileSync` member
|
||||
* from the `fs` module.
|
||||
* For example:
|
||||
* ```js
|
||||
* // API::moduleImport("fs").asSource()
|
||||
* require('fs');
|
||||
*
|
||||
* Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses
|
||||
* found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference
|
||||
* to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to
|
||||
* this API component.
|
||||
* // API::moduleImport("fs").getMember("readFile").asSource()
|
||||
* require('fs').readFile;
|
||||
*
|
||||
* // API::moduleImport("fs").getMember("readFile").getReturn().asSource()
|
||||
* require('fs').readFile();
|
||||
*
|
||||
* require('fs').readFile(
|
||||
* filename,
|
||||
* // 'y' matched by API::moduleImport("fs").getMember("readFile").getParameter(1).getParameter(0).asSource()
|
||||
* y => {
|
||||
* ...
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
DataFlow::SourceNode getAnImmediateUse() { Impl::use(this, result) }
|
||||
DataFlow::SourceNode asSource() { Impl::use(this, result) }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `asSource`. */
|
||||
deprecated DataFlow::SourceNode getAnImmediateUse() { result = this.asSource() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource`. */
|
||||
deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() }
|
||||
|
||||
/**
|
||||
* Gets a call to the function represented by this API component.
|
||||
*/
|
||||
CallNode getACall() { result = this.getReturn().getAnImmediateUse() }
|
||||
CallNode getACall() { result = this.getReturn().asSource() }
|
||||
|
||||
/**
|
||||
* Gets a call to the function represented by this API component,
|
||||
@@ -72,7 +177,7 @@ module API {
|
||||
/**
|
||||
* Gets a `new` call to the function represented by this API component.
|
||||
*/
|
||||
NewNode getAnInstantiation() { result = this.getInstance().getAnImmediateUse() }
|
||||
NewNode getAnInstantiation() { result = this.getInstance().asSource() }
|
||||
|
||||
/**
|
||||
* Gets an invocation (with our without `new`) to the function represented by this API component.
|
||||
@@ -80,26 +185,38 @@ module API {
|
||||
InvokeNode getAnInvocation() { result = this.getACall() or result = this.getAnInstantiation() }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node corresponding to the right-hand side of a definition of the API
|
||||
* component represented by this node.
|
||||
* Get a data-flow node where this value leaves the current codebase and flows into an
|
||||
* external library (or in general, any external codebase).
|
||||
*
|
||||
* For example, in the assignment `exports.plusOne = (x) => x+1`, the function expression
|
||||
* `(x) => x+1` is the right-hand side of the definition of the member `plusOne` of
|
||||
* the enclosing module, and the expression `x+1` is the right-had side of the definition of
|
||||
* its result.
|
||||
* Concretely, this is either an argument passed to a call to external code,
|
||||
* or the right-hand side of a property write on an object flowing into such a call.
|
||||
*
|
||||
* Note that for parameters, it is the arguments flowing into that parameter that count as
|
||||
* right-hand sides of the definition, not the declaration of the parameter itself.
|
||||
* Consequently, in `require('fs').readFileSync(file)`, `file` is the right-hand
|
||||
* side of a definition of the first parameter of `readFileSync` from the `fs` module.
|
||||
* For example:
|
||||
* ```js
|
||||
* // 'x' is matched by API::moduleImport("foo").getParameter(0).asSink()
|
||||
* require('foo')(x);
|
||||
*
|
||||
* // 'x' is matched by API::moduleImport("foo").getParameter(0).getMember("prop").asSink()
|
||||
* require('foo')({
|
||||
* prop: x
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
DataFlow::Node getARhs() { Impl::rhs(this, result) }
|
||||
DataFlow::Node asSink() { Impl::rhs(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition
|
||||
* of the API component represented by this node.
|
||||
* Get a data-flow node that transitively flows to an external library (or in general, any external codebase).
|
||||
*
|
||||
* This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow.
|
||||
* See `asSink()` for examples.
|
||||
*/
|
||||
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) }
|
||||
DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `asSink`. */
|
||||
deprecated DataFlow::Node getARhs() { result = this.asSink() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `getAValueReachingSink`. */
|
||||
deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() }
|
||||
|
||||
/**
|
||||
* Gets a node representing member `m` of this API component.
|
||||
@@ -334,7 +451,7 @@ module API {
|
||||
* In other words, the value of a use of `that` may flow into the right-hand side of a
|
||||
* definition of this node.
|
||||
*/
|
||||
predicate refersTo(Node that) { this.getARhs() = that.getAUse() }
|
||||
predicate refersTo(Node that) { this.asSink() = that.getAValueReachableFromSource() }
|
||||
|
||||
/**
|
||||
* Gets the data-flow node that gives rise to this node, if any.
|
||||
@@ -416,8 +533,9 @@ module API {
|
||||
|
||||
/** Gets a node corresponding to an import of module `m`. */
|
||||
Node moduleImport(string m) {
|
||||
result = Impl::MkModuleImport(m) or
|
||||
result = Impl::MkModuleImport(m).(Node).getMember("default")
|
||||
result = Internal::getAModuleImportRaw(m)
|
||||
or
|
||||
result = ModelOutput::getATypeNode(m, "")
|
||||
}
|
||||
|
||||
/** Gets a node corresponding to an export of module `m`. */
|
||||
@@ -427,6 +545,22 @@ module API {
|
||||
module Node {
|
||||
/** Gets a node whose type has the given qualified name. */
|
||||
Node ofType(string moduleName, string exportedName) {
|
||||
result = Internal::getANodeOfTypeRaw(moduleName, exportedName)
|
||||
or
|
||||
result = ModelOutput::getATypeNode(moduleName, exportedName)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides access to API graph nodes without taking into account types from models. */
|
||||
module Internal {
|
||||
/** Gets a node corresponding to an import of module `m` without taking into account types from models. */
|
||||
Node getAModuleImportRaw(string m) {
|
||||
result = Impl::MkModuleImport(m) or
|
||||
result = Impl::MkModuleImport(m).(Node).getMember("default")
|
||||
}
|
||||
|
||||
/** Gets a node whose type has the given qualified name, not including types from models. */
|
||||
Node getANodeOfTypeRaw(string moduleName, string exportedName) {
|
||||
result = Impl::MkTypeUse(moduleName, exportedName).(Node).getInstance()
|
||||
}
|
||||
}
|
||||
@@ -445,11 +579,17 @@ module API {
|
||||
bindingset[this]
|
||||
EntryPoint() { any() }
|
||||
|
||||
/** Gets a data-flow node that uses this entry point. */
|
||||
abstract DataFlow::SourceNode getAUse();
|
||||
/** DEPRECATED. This predicate has been renamed to `getASource`. */
|
||||
deprecated DataFlow::SourceNode getAUse() { none() }
|
||||
|
||||
/** Gets a data-flow node that defines this entry point. */
|
||||
abstract DataFlow::Node getARhs();
|
||||
/** DEPRECATED. This predicate has been renamed to `getASink`. */
|
||||
deprecated DataFlow::SourceNode getARhs() { none() }
|
||||
|
||||
/** Gets a data-flow node where a value enters the current codebase through this entry-point. */
|
||||
DataFlow::SourceNode getASource() { none() }
|
||||
|
||||
/** Gets a data-flow node where a value leaves the current codebase through this entry-point. */
|
||||
DataFlow::Node getASink() { none() }
|
||||
|
||||
/** Gets an API-node for this entry point. */
|
||||
API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) }
|
||||
@@ -512,7 +652,7 @@ module API {
|
||||
exports(m, _, _)
|
||||
or
|
||||
exists(NodeModule nm | nm = mod |
|
||||
exists(SSA::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()]))
|
||||
exists(Ssa::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()]))
|
||||
)
|
||||
)
|
||||
} or
|
||||
@@ -523,7 +663,14 @@ module API {
|
||||
or
|
||||
any(Type t).hasUnderlyingType(m, _)
|
||||
} or
|
||||
MkClassInstance(DataFlow::ClassNode cls) { cls = trackDefNode(_) and hasSemantics(cls) } or
|
||||
MkClassInstance(DataFlow::ClassNode cls) {
|
||||
hasSemantics(cls) and
|
||||
(
|
||||
cls = trackDefNode(_)
|
||||
or
|
||||
cls.getAnInstanceReference() = trackDefNode(_)
|
||||
)
|
||||
} or
|
||||
MkAsyncFuncResult(DataFlow::FunctionNode f) {
|
||||
f = trackDefNode(_) and f.getFunction().isAsync() and hasSemantics(f)
|
||||
} or
|
||||
@@ -567,7 +714,7 @@ module API {
|
||||
base = MkRoot() and
|
||||
exists(EntryPoint e |
|
||||
lbl = Label::entryPoint(e) and
|
||||
rhs = e.getARhs()
|
||||
rhs = e.getASink()
|
||||
)
|
||||
or
|
||||
exists(string m, string prop |
|
||||
@@ -615,16 +762,6 @@ module API {
|
||||
.getStaticMember(name, DataFlow::MemberKind::getter())
|
||||
.getAReturn()
|
||||
)
|
||||
or
|
||||
// If `new C()` escapes, generate edges to its instance members
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
pred = cls.getAClassReference().getAnInstantiation() and
|
||||
lbl = Label::member(name)
|
||||
|
|
||||
rhs = cls.getInstanceMethod(name)
|
||||
or
|
||||
rhs = cls.getInstanceMember(name, DataFlow::MemberKind::getter()).getAReturn()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
@@ -744,7 +881,7 @@ module API {
|
||||
base = MkRoot() and
|
||||
exists(EntryPoint e |
|
||||
lbl = Label::entryPoint(e) and
|
||||
ref = e.getAUse()
|
||||
ref = e.getASource()
|
||||
)
|
||||
or
|
||||
// property reads
|
||||
@@ -1113,9 +1250,13 @@ module API {
|
||||
succ = MkUse(ref)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node rhs |
|
||||
rhs(pred, lbl, rhs) and
|
||||
exists(DataFlow::Node rhs | rhs(pred, lbl, rhs) |
|
||||
succ = MkDef(rhs)
|
||||
or
|
||||
exists(DataFlow::ClassNode cls |
|
||||
cls.getAnInstanceReference() = rhs and
|
||||
succ = MkClassInstance(cls)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node def |
|
||||
@@ -1178,8 +1319,8 @@ module API {
|
||||
API::Node callee;
|
||||
|
||||
InvokeNode() {
|
||||
this = callee.getReturn().getAnImmediateUse() or
|
||||
this = callee.getInstance().getAnImmediateUse() or
|
||||
this = callee.getReturn().asSource() or
|
||||
this = callee.getInstance().asSource() or
|
||||
this = Impl::getAPromisifiedInvocation(callee, _, _)
|
||||
}
|
||||
|
||||
@@ -1194,7 +1335,7 @@ module API {
|
||||
* Gets an API node where a RHS of the node is the `i`th argument to this call.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Node getAParameterCandidate(int i) { result.getARhs() = this.getArgument(i) }
|
||||
private Node getAParameterCandidate(int i) { result.asSink() = this.getArgument(i) }
|
||||
|
||||
/** Gets the API node for a parameter of this invocation. */
|
||||
Node getAParameter() { result = this.getParameter(_) }
|
||||
@@ -1205,13 +1346,13 @@ module API {
|
||||
/** Gets the API node for the return value of this call. */
|
||||
Node getReturn() {
|
||||
result = callee.getReturn() and
|
||||
result.getAnImmediateUse() = this
|
||||
result.asSource() = this
|
||||
}
|
||||
|
||||
/** Gets the API node for the object constructed by this invocation. */
|
||||
Node getInstance() {
|
||||
result = callee.getInstance() and
|
||||
result.getAnImmediateUse() = this
|
||||
result.asSource() = this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1354,7 +1495,7 @@ module API {
|
||||
/** Gets the EntryPoint associated with this label. */
|
||||
API::EntryPoint getEntryPoint() { result = e }
|
||||
|
||||
override string toString() { result = "getASuccessor(Label::entryPoint(\"" + e + "\"))" }
|
||||
override string toString() { result = "entryPoint(\"" + e + "\")" }
|
||||
}
|
||||
|
||||
/** A label that gets a promised value. */
|
||||
|
||||
@@ -36,12 +36,18 @@ module ArrayTaintTracking {
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `array.filter(x => x)` keeps the taint
|
||||
// `array.filter(x => x)` and `array.filter(x => !!x)` keeps the taint
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "filter" and
|
||||
pred = call.getReceiver() and
|
||||
succ = call and
|
||||
exists(DataFlow::FunctionNode callback | callback = call.getArgument(0).getAFunctionValue() |
|
||||
callback.getParameter(0).getALocalUse() = callback.getAReturn()
|
||||
exists(DataFlow::FunctionNode callback, DataFlow::Node param, DataFlow::Node ret |
|
||||
callback = call.getArgument(0).getAFunctionValue() and
|
||||
param = callback.getParameter(0).getALocalUse() and
|
||||
ret = callback.getAReturn()
|
||||
|
|
||||
param = ret
|
||||
or
|
||||
param = DataFlow::exprNode(ret.asExpr().(LogNotExpr).getOperand().(LogNotExpr).getOperand())
|
||||
)
|
||||
or
|
||||
// `array.reduce` with tainted value in callback
|
||||
@@ -75,7 +81,7 @@ module ArrayTaintTracking {
|
||||
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice"]) and
|
||||
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice", "at"]) and
|
||||
succ = call
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
@@ -199,13 +205,13 @@ private module ArrayDataFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for retrieving an element from an array using `.pop()` or `.shift()`.
|
||||
* A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`.
|
||||
* E.g. `array.pop()`.
|
||||
*/
|
||||
private class ArrayPopStep extends DataFlow::SharedFlowStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["pop", "shift"] and
|
||||
call.getMethodName() = ["pop", "shift", "at"] and
|
||||
prop = arrayElement() and
|
||||
obj = call.getReceiver() and
|
||||
element = call
|
||||
@@ -255,14 +261,12 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step for creating an array and storing the elements in the array.
|
||||
*/
|
||||
private class ArrayCreationStep extends DataFlow::SharedFlowStep {
|
||||
private class ArrayCreationStep extends PreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::ArrayCreationNode array, int i |
|
||||
element = array.getElement(i) and
|
||||
obj = array and
|
||||
if array = any(PromiseAllCreation c).getArrayNode()
|
||||
then prop = arrayElement(i)
|
||||
else prop = arrayElement()
|
||||
prop = arrayElement(i)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -340,6 +344,14 @@ private module ArrayLibraries {
|
||||
result = DataFlow::globalVarRef("Array").getAMemberCall("from")
|
||||
or
|
||||
result = DataFlow::moduleImport("array-from").getACall()
|
||||
or
|
||||
// Array.prototype.slice.call acts the same as Array.from, and is sometimes used with e.g. the arguments object.
|
||||
result =
|
||||
DataFlow::globalVarRef("Array")
|
||||
.getAPropertyRead("prototype")
|
||||
.getAPropertyRead("slice")
|
||||
.getAMethodCall("call") and
|
||||
result.getNumArgument() = 1
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user