Merge branch 'main' into codeql-ci/js/ml-powered-pack-release-0.3.5

This commit is contained in:
Henry Mercer
2022-10-11 19:06:01 +01:00
committed by GitHub
7758 changed files with 611118 additions and 247177 deletions

View File

@@ -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``.

View File

@@ -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

View File

@@ -0,0 +1,4 @@
description: Associate symbols with external module declarations
compatibility: backwards
ast_node_symbol.rel: run ast_node_symbol.qlo

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
name: codeql/javascript-downgrades
groups: javascript
downgrades: .
library: true

View File

@@ -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;

View File

@@ -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",

View File

@@ -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)

View File

@@ -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);

View File

@@ -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.

View File

@@ -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==

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -830,7 +830,7 @@ public class FlowParser extends ESNextParser {
/** Should Flow syntax be allowed? */
private boolean flow() {
return options.esnext();
return options.allowFlowTypes();
}
@Override

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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.");

View File

@@ -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());
}
}

View File

@@ -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);

View File

@@ -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),

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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));
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -0,0 +1,9 @@
async function foo() {
for await (const call of calls) {
call();
}
}
for await (const call of calls) {
call();
}

View 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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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) {

View File

@@ -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() |

View File

@@ -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
)
)
}
}

View File

@@ -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()
)

View File

@@ -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]

View File

@@ -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, _))
)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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"
}

View File

@@ -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 | |

View File

@@ -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 | |

View File

@@ -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 | |

View File

@@ -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()

View File

@@ -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' |

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
import javascript
import extraction.NoFeaturizationRestrictionsConfig
class Endpoint extends DataFlow::Node {
Endpoint() { this.asExpr().(VarAccess).getName() = "endpoint" }
}

View File

@@ -0,0 +1,3 @@
<div class="form-group">
<input (change)="restoreBackup($event.target.files.item(endpoint))" />
</div>

View File

@@ -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";

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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))
}
/**

View File

@@ -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.

View File

@@ -0,0 +1 @@
## 0.1.4

View 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.

View 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.

View File

@@ -0,0 +1 @@
## 0.2.2

View File

@@ -0,0 +1 @@
## 0.2.3

View 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.

View File

@@ -0,0 +1 @@
## 0.2.5

View 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.

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.2
lastReleaseVersion: 0.3.1

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
/**

View File

@@ -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 }

View File

@@ -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. */

View File

@@ -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