Merge pull request #502 from xiemaisi/js/summaries

Approved by asger-semmle
This commit is contained in:
semmle-qlci
2019-01-11 10:27:03 +00:00
committed by GitHub
47 changed files with 4571 additions and 6 deletions

View File

@@ -0,0 +1,2 @@
+ semmlecode-javascript-queries/Security/Summaries/ExtractSourceSummaries.ql
+ semmlecode-javascript-queries/Security/Summaries/ExtractSinkSummaries.ql

View File

@@ -0,0 +1,223 @@
Summary-based information flow analysis
=======================================
Overview
--------
This document presents an approach for running information flow analyses (such as the standard
Semmle 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.
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,
Semmle's 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``,
``Security/Summaries/ExtractSourceSummaries.ql``)
- Extract sink summaries (``js/sink-summary-extraction``,
``Security/Summaries/ExtractSinkSummaries.ql``)
- Extract source summaries (``js/source-summary-extraction``,
``Security/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/Security/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 (member (root https://www.npmjs.com/package/mkdirp) default) 0)
As a more complicated example,
.. code-block:: lisp
(parameter (parameter (member (instance (member (root https://www.npmjs.com/package/bluebird) Promise)) then) 1) 0)
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,35 @@
/**
* Imports the standard library and all taint-tracking configuration classes from the security queries.
*/
import javascript
import semmle.javascript.security.dataflow.BrokenCryptoAlgorithm
import semmle.javascript.security.dataflow.CleartextLogging
import semmle.javascript.security.dataflow.CleartextStorage
import semmle.javascript.security.dataflow.ClientSideUrlRedirect
import semmle.javascript.security.dataflow.CodeInjection
import semmle.javascript.security.dataflow.CommandInjection
import semmle.javascript.security.dataflow.ConditionalBypass
import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentials
import semmle.javascript.security.dataflow.DifferentKindsComparisonBypass
import semmle.javascript.security.dataflow.DomBasedXss as DomBasedXss
import semmle.javascript.security.dataflow.FileAccessToHttp
import semmle.javascript.security.dataflow.HardcodedCredentials
import semmle.javascript.security.dataflow.InsecureRandomness
import semmle.javascript.security.dataflow.InsufficientPasswordHash
import semmle.javascript.security.dataflow.NosqlInjection
import semmle.javascript.security.dataflow.ReflectedXss as ReflectedXss
import semmle.javascript.security.dataflow.RegExpInjection
import semmle.javascript.security.dataflow.RemotePropertyInjection
import semmle.javascript.security.dataflow.RequestForgery
import semmle.javascript.security.dataflow.ServerSideUrlRedirect
import semmle.javascript.security.dataflow.SqlInjection
import semmle.javascript.security.dataflow.StackTraceExposure
import semmle.javascript.security.dataflow.StoredXss as StoredXss
import semmle.javascript.security.dataflow.TaintedFormatString
import semmle.javascript.security.dataflow.TaintedPath
import semmle.javascript.security.dataflow.TypeConfusionThroughParameterTampering
import semmle.javascript.security.dataflow.UnsafeDeserialization
import semmle.javascript.security.dataflow.XmlBomb
import semmle.javascript.security.dataflow.XpathInjection
import semmle.javascript.security.dataflow.Xxe

View File

@@ -0,0 +1,32 @@
/**
* @name Extract flow step summaries
* @description Extracts flow step summaries, that is, tuples `(p1, lbl1, p2, lbl2, cfg)`
* representing the fact that data with flow label `lbl1` may flow from a
* user-controlled exit node of portal `p1` to an escaping entry node of portal `p2`,
* and have label `lbl2` at that point. Moreover, the path from `p1` to `p2` contains
* no sanitizers specified by configuration `cfg`.
* @kind flow-step-summary
* @id js/step-summary-extraction
*/
import AllConfigurations
import PortalExitSource
import PortalEntrySink
from
TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Portal p1,
Portal p2, DataFlow::FlowLabel lbl1, DataFlow::FlowLabel lbl2
where
cfg.hasFlowPath(source, sink) and
p1 = source.getNode().(PortalExitSource).getPortal() and
p2 = sink.getNode().(PortalEntrySink).getPortal() and
lbl1 = sink.getPathSummary().getStartLabel() and
lbl2 = sink.getPathSummary().getEndLabel() and
// avoid constructing infeasible paths
sink.getPathSummary().hasCall() = false and
sink.getPathSummary().hasReturn() = false and
// restrict to steps flow function parameters to returns
p1.(ParameterPortal).getBasePortal() = p2.(ReturnPortal).getBasePortal() and
// restrict to data/taint flow
lbl1 instanceof DataFlow::StandardFlowLabel
select p1.toString(), lbl1.toString(), p2.toString(), lbl2.toString(), cfg.toString()

View File

@@ -0,0 +1,20 @@
/**
* @name Extract sink summaries
* @description Extracts sink summaries, that is, tuples `(p, lbl, cfg)` representing the fact
* that data with flow label `lbl` may flow from a user-controlled exit node of portal
* `p` to a known sink for configuration `cfg`.
* @kind sink-summary
* @id js/sink-summary-extraction
*/
import AllConfigurations
import PortalExitSource
import SinkFromAnnotation
from TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Portal p
where
cfg.hasFlowPath(source, sink) and
p = source.getNode().(PortalExitSource).getPortal() and
// avoid constructing infeasible paths
sink.getPathSummary().hasReturn() = false
select p.toString(), source.getPathSummary().getStartLabel().toString(), cfg.toString()

View File

@@ -0,0 +1,20 @@
/**
* @name Extract source summaries
* @description Extracts source summaries, that is, tuples `(p, lbl, cfg)` representing the fact
* that data may flow from a known source for configuration `cfg` to an escaping entry
* node of portal `p`, and have flow label `lbl` at that point.
* @kind source-summary
* @id js/source-summary-extraction
*/
import AllConfigurations
import PortalEntrySink
import SourceFromAnnotation
from TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Portal p
where
cfg.hasFlowPath(source, sink) and
p = sink.getNode().(PortalEntrySink).getPortal() and
// avoid constructing infeasible paths
sink.getPathSummary().hasCall() = false
select p.toString(), sink.getPathSummary().getEndLabel().toString(), cfg.toString()

View File

@@ -0,0 +1,158 @@
/**
* Provides classes for importing source, sink and flow step summaries
* from external CSV data added at snapshot build time.
*/
import javascript
import semmle.javascript.dataflow.Portals
import external.ExternalArtifact
private import Shared
/**
* An additional source specified in an `additional-sources.csv` file.
*/
class AdditionalSourceSpec extends ExternalData {
AdditionalSourceSpec() { this.getDataPath() = "additional-sources.csv" }
/**
* Gets the portal of this additional source.
*/
Portal getPortal() { result.toString() = getField(0) }
/**
* Gets the flow label of this source.
*/
DataFlow::FlowLabel getFlowLabel() { sourceFlowLabelSpec(result, getField(1)) }
/**
* Gets the configuration for which this is a source, or any
* configuration if this source does not specify a configuration.
*/
DataFlow::Configuration getConfiguration() { configSpec(result, getField(2)) }
override string toString() {
exists(string config |
if getField(2) = "" then config = "any configuration" else config = getConfiguration()
|
result = getPortal() + " as " + getFlowLabel() + " source for " + config
)
}
}
private class AdditionalSourceFromSpec extends DataFlow::AdditionalSource {
AdditionalSourceSpec spec;
AdditionalSourceFromSpec() { this = spec.getPortal().getAnExitNode(_) }
override predicate isSourceFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
cfg = spec.getConfiguration() and lbl = spec.getFlowLabel()
}
}
/**
* An additional sink specified in an `additional-sinks.csv` file.
*/
class AdditionalSinkSpec extends ExternalData {
AdditionalSinkSpec() { this.getDataPath() = "additional-sinks.csv" }
/**
* Gets the portal specification of this additional sink.
*/
Portal getPortal() { result.toString() = getField(0) }
/**
* Gets the flow label of this sink, or any standard flow label if this sink
* does not specify a flow label.
*/
DataFlow::FlowLabel getFlowLabel() { sinkFlowLabelSpec(result, getField(1)) }
/**
* Gets the configuration for which this is a sink, or any configuration if
* this sink does not specify a configuration.
*/
DataFlow::Configuration getConfiguration() { configSpec(result, getField(2)) }
override string toString() {
exists(string labels, string config |
labels = strictconcat(getFlowLabel(), " or ") and
if getField(2) = "" then config = "any configuration" else config = getConfiguration()
|
result = getPortal() + " as " + labels + " sink for " + config
)
}
}
private class AdditionalSinkFromSpec extends DataFlow::AdditionalSink {
AdditionalSinkSpec spec;
AdditionalSinkFromSpec() { this = spec.getPortal().getAnEntryNode(_) }
override predicate isSinkFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
cfg = spec.getConfiguration() and lbl = spec.getFlowLabel()
}
}
/**
* An additional flow step specified in an `additional-steps.csv` file.
*/
class AdditionalStepSpec extends ExternalData {
AdditionalStepSpec() { this.getDataPath() = "additional-steps.csv" }
/**
* Gets the start portal of this additional step.
*/
Portal getStartPortal() { result.toString() = getField(0) }
/**
* Gets the start flow label of this additional step.
*/
DataFlow::FlowLabel getStartFlowLabel() { result.toString() = getField(1) }
/**
* Gets the end portal of this additional step.
*/
Portal getEndPortal() { result.toString() = getField(2) }
/**
* Gets the end flow label of this additional step.
*/
DataFlow::FlowLabel getEndFlowLabel() { result.toString() = getField(3) }
/**
* Gets the configuration to which this step should be added.
*/
DataFlow::Configuration getConfiguration() { configSpec(result, getField(4)) }
override string toString() {
exists(string config |
if getField(4) = "" then config = "any configuration" else config = getConfiguration()
|
result = "edge from " + getStartPortal() + " to " + getEndPortal() + ", transforming " +
getStartFlowLabel() + " into " + getEndFlowLabel() + " for " + config
)
}
}
private class AdditionalFlowStepFromSpec extends DataFlow::Configuration {
AdditionalStepSpec spec;
DataFlow::Node entry;
DataFlow::Node exit;
AdditionalFlowStepFromSpec() {
this = spec.getConfiguration() and
entry = spec.getStartPortal().getAnEntryNode(_) and
exit = spec.getEndPortal().getAnExitNode(_)
}
override predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
pred = entry and
succ = exit and
predlbl = spec.getStartFlowLabel() and
succlbl = spec.getEndFlowLabel()
}
}

View File

@@ -0,0 +1,105 @@
/**
* Provides classes for importing source, sink and flow step summaries
* through external predicates.
*/
import javascript
import semmle.javascript.dataflow.Portals
import external.ExternalArtifact
import Shared
/**
* An external predicate providing information about additional sources.
*
* This predicate can be populated from the output of the `ExtractSourceSummaries` query.
*/
external predicate additionalSources(string portal, string flowLabel, string config);
/**
* An external predicate providing information about additional sinks.
*
* This predicate can be populated from the output of the `ExtractSinkSummaries` query.
*/
external predicate additionalSinks(string portal, string flowLabel, string config);
/**
* An external predicate providing information about additional flow steps.
*
* This predicate can be populated from the output of the `ExtractFlowStepSummaries` query.
*/
external predicate additionalSteps(
string startPortal, string startFlowLabel, string endPortal, string endFlowLabel, string config
);
/**
* An additional source specified through the `additionalSources` predicate.
*/
private class AdditionalSourceFromSpec extends DataFlow::AdditionalSource {
Portal portal;
string flowLabel;
string config;
AdditionalSourceFromSpec() {
additionalSources(portal.toString(), flowLabel, config) and
this = portal.getAnExitNode(_)
}
override predicate isSourceFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
configSpec(cfg, config) and sourceFlowLabelSpec(lbl, flowLabel)
}
}
/**
* An additional sink specified through the `additionalSinks` predicate.
*/
private class AdditionalSinkFromSpec extends DataFlow::AdditionalSink {
Portal portal;
string flowLabel;
string config;
AdditionalSinkFromSpec() {
additionalSinks(portal.toString(), flowLabel, config) and
this = portal.getAnEntryNode(_)
}
override predicate isSinkFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
configSpec(cfg, config) and sinkFlowLabelSpec(lbl, flowLabel)
}
}
/**
* An additional flow step specified through the `additionalSteps` predicate.
*/
private class AdditionalFlowStepFromSpec extends DataFlow::Configuration {
DataFlow::Node entry;
string startFlowLabel;
DataFlow::Node exit;
string endFlowLabel;
AdditionalFlowStepFromSpec() {
exists(Portal startPortal, Portal endPortal, string config |
additionalSteps(startPortal.toString(), startFlowLabel, endPortal.toString(), endFlowLabel,
config) and
configSpec(this, config) and
entry = startPortal.getAnEntryNode(_) and
exit = endPortal.getAnExitNode(_)
)
}
override predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
pred = entry and
succ = exit and
predlbl = startFlowLabel and
succlbl = endFlowLabel
}
}

View File

@@ -0,0 +1,20 @@
import javascript
import semmle.javascript.dataflow.Portals
/**
* An escaping entry node of a portal, viewed as an additional sink node for any flow
* configuration currently in scope.
*/
class PortalEntrySink extends DataFlow::AdditionalSink {
Portal p;
PortalEntrySink() { this = p.getAnEntryNode(true) }
override predicate isSinkFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
cfg instanceof TaintTracking::Configuration and
lbl = any(DataFlow::FlowLabel l)
}
/** Gets the portal of which this is an entry node. */
Portal getPortal() { result = p }
}

View File

@@ -0,0 +1,20 @@
import javascript
import semmle.javascript.dataflow.Portals
/**
* A remote exit node of a portal, viewed as an additional source node for any flow
* configuration currently in scope.
*/
class PortalExitSource extends DataFlow::AdditionalSource {
Portal p;
PortalExitSource() { this = p.getAnExitNode(true) }
override predicate isSourceFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
cfg instanceof TaintTracking::Configuration and
lbl = any(DataFlow::FlowLabel l)
}
/** Gets the portal of which this is an exit node. */
Portal getPortal() { result = p }
}

View File

@@ -0,0 +1,91 @@
/**
* Provides utility predicates for working with flow summary specifications.
*/
import javascript
/**
* Holds if `config` matches `spec`, that is, either `spec` is the ID of `config`
* or `spec` is the empty string and `config` is an arbitrary configuration.
*/
predicate configSpec(DataFlow::Configuration config, string spec) {
config.getId() = spec
or
spec = ""
}
/**
* Holds if `lbl` matches `spec`, that is, either `spec` is the name of `config`
* or `spec` is the empty string and `lbl` is the built-in flow label `data`.
*/
predicate sourceFlowLabelSpec(DataFlow::FlowLabel lbl, string spec) {
lbl.toString() = spec
or
spec = "" and
lbl = DataFlow::FlowLabel::data()
}
/**
* Holds if `lbl` matches `spec`, that is, either `spec` is the name of `config`
* or `spec` is the empty string and `lbl` is an arbitrary standard flow label.
*/
predicate sinkFlowLabelSpec(DataFlow::FlowLabel lbl, string spec) {
lbl.toString() = spec
or
spec = "" and
lbl instanceof DataFlow::StandardFlowLabel
}
/**
* A comment that annotates data flow nodes as sources/sinks.
*
* Such a comment starts with "Semmle:", possibly preceded by whitespace, and
* may contain specifications of the form "source: label,config" and "sink: label, config"
* where again whitespace is not significant and the ",config" part may be missing.
*
* It applies to any data flow node that ends on the line where the comment starts,
* and annotates the node as being a source/sink with the given flow label(s) for
* the given configuration (or any configuration if omitted).
*/
class AnnotationComment extends Comment {
string ann;
AnnotationComment() { ann = getText().regexpCapture("(?s)\\s*Semmle:(.*)", 1) }
/**
* Holds if this comment applies to `nd`, that is, it starts on the same line on
* which `nd` ends.
*/
predicate appliesTo(DataFlow::Node nd) {
exists(string file, int line |
getLocation().hasLocationInfo(file, line, _, _, _) and
nd.hasLocationInfo(file, _, _, line, _)
)
}
/**
* Holds if this comment contains an annotation of the form `source: label, config`
* or `source: label` (modulo whitespace). In the latter case, `config` may be
* any configuration.
*/
predicate specifiesSource(DataFlow::FlowLabel label, DataFlow::Configuration config) {
exists(string spec |
spec = ann.regexpFind("(?<=\\bsource:)\\s*[^\\s,]+(\\s*,\\s*[^\\s,])?", _, _) and
sourceFlowLabelSpec(label, spec.splitAt(",", 0).trim()) and
configSpec(config, spec.splitAt(",", 1).trim())
)
}
/**
* Holds if this comment contains an annotation of the form `sink: label, config`
* or `sink: label` (modulo whitespace). In the latter case, `config` may be
* any configuration.
*/
predicate specifiesSink(string label, string config) {
exists(string spec |
spec = ann.regexpFind("(?<=\\bsink:)\\s*[^\\s,]+(\\s*,\\s*[^\\s,]+)?", _, _) and
sinkFlowLabelSpec(label, spec.splitAt(",", 0).trim()) and
configSpec(config, spec.splitAt(",", 1).trim())
)
}
}

View File

@@ -0,0 +1,18 @@
import javascript
import Shared
/**
* A data flow node that is annotated as a sink.
*/
class SinkFromAnnotation extends DataFlow::AdditionalSink {
AnnotationComment c;
SinkFromAnnotation() {
c.appliesTo(this) and
c.specifiesSink(_, _)
}
override predicate isSinkFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
c.specifiesSink(lbl, cfg)
}
}

View File

@@ -0,0 +1,18 @@
import javascript
import Shared
/**
* A data flow node that is annotated as a source.
*/
class SourceFromAnnotation extends DataFlow::AdditionalSource {
AnnotationComment c;
SourceFromAnnotation() {
c.appliesTo(this) and
c.specifiesSource(_, _)
}
override predicate isSourceFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
c.specifiesSource(lbl, cfg)
}
}

View File

@@ -318,6 +318,11 @@ class NPMPackage extends @folder {
* Gets the main module of this package.
*/
Module getMainModule() { result = pkg.getMainModule() }
/**
* Holds if this package declares a dependency on version `v` of package `p`.
*/
predicate declaresDependency(string p, string v) { pkg.declaresDependency(p, v) }
}
/**

View File

@@ -260,8 +260,18 @@ class AbstractRegExp extends DefiniteAbstractValue, TAbstractRegExp {
abstract class AbstractCallable extends DefiniteAbstractValue {
/**
* Gets the function represented by this abstract value.
*
* For abstract class values, this is the constructor method of the class.
*/
abstract Function getFunction();
/**
* Gets the definition of the function or class represented by this abstract value.
*
* For abstract class values, this is the definition of the class itself (and not
* its constructor).
*/
abstract AST::ValueNode getDefinition();
}
/**
@@ -270,6 +280,8 @@ abstract class AbstractCallable extends DefiniteAbstractValue {
class AbstractFunction extends AbstractCallable, TAbstractFunction {
override Function getFunction() { this = TAbstractFunction(result) }
override AST::ValueNode getDefinition() { result = getFunction() }
override boolean getBooleanValue() { result = true }
override InferredType getType() { result = TTFunction() }
@@ -298,6 +310,8 @@ class AbstractClass extends AbstractCallable, TAbstractClass {
override Function getFunction() { result = getClass().getConstructor().getBody() }
override AST::ValueNode getDefinition() { result = getClass() }
override boolean getBooleanValue() { result = true }
override InferredType getType() { result = TTClass() }

View File

@@ -86,6 +86,12 @@ abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
/**
* Gets the unique identifier of this configuration among all data flow tracking
* configurations.
*/
string getId() { result = this }
/**
* Holds if `source` is a relevant data flow source for this configuration.
*/
@@ -221,7 +227,7 @@ class TaintKind = FlowLabel;
/**
* A standard flow label, that is, either `FlowLabel::data()` or `FlowLabel::taint()`.
*/
private class StandardFlowLabel extends FlowLabel {
class StandardFlowLabel extends FlowLabel {
StandardFlowLabel() { this = "data" or this = "taint" }
}
@@ -299,7 +305,19 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
* Holds if `pred` &rarr; `succ` should be considered a data flow edge.
*/
cached
abstract predicate step(DataFlow::Node pred, DataFlow::Node succ);
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge
* transforming values with label `predlbl` to have label `succlbl`.
*/
cached
predicate step(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
none()
}
}
/**
@@ -311,7 +329,13 @@ abstract class AdditionalSource extends DataFlow::Node {
* Holds if this data flow node should be considered a source node for
* configuration `cfg`.
*/
abstract predicate isSourceFor(Configuration cfg);
predicate isSourceFor(Configuration cfg) { none() }
/**
* Holds if this data flow node should be considered a source node for
* values labeled with `lbl` under configuration `cfg`.
*/
predicate isSourceFor(Configuration cfg, FlowLabel lbl) { none() }
}
/**
@@ -323,7 +347,13 @@ abstract class AdditionalSink extends DataFlow::Node {
* Holds if this data flow node should be considered a sink node for
* configuration `cfg`.
*/
abstract predicate isSinkFor(Configuration cfg);
predicate isSinkFor(Configuration cfg) { none() }
/**
* Holds if this data flow node should be considered a sink node for
* values labeled with `lbl` under configuration `cfg`.
*/
predicate isSinkFor(Configuration cfg, FlowLabel lbl) { none() }
}
/**
@@ -451,6 +481,8 @@ private predicate isSource(DataFlow::Node nd, DataFlow::Configuration cfg, FlowL
(cfg.isSource(nd) or nd.(AdditionalSource).isSourceFor(cfg)) and
lbl = FlowLabel::data()
or
nd.(AdditionalSource).isSourceFor(cfg, lbl)
or
cfg.isSource(nd, lbl)
}
@@ -461,6 +493,8 @@ private predicate isSink(DataFlow::Node nd, DataFlow::Configuration cfg, FlowLab
(cfg.isSink(nd) or nd.(AdditionalSink).isSinkFor(cfg)) and
lbl = any(StandardFlowLabel f)
or
nd.(AdditionalSink).isSinkFor(cfg, lbl)
or
cfg.isSink(nd, lbl)
}
@@ -696,13 +730,18 @@ private predicate onPath(DataFlow::Node nd, DataFlow::Configuration cfg, PathSum
)
}
/**
* Holds if `cfg` has at least one source and at least one sink.
*/
pragma[noinline]
private predicate isLive(DataFlow::Configuration cfg) { isSource(_, cfg, _) and isSink(_, cfg, _) }
/**
* A data flow node on an inter-procedural path from a source.
*/
private newtype TPathNode =
MkPathNode(DataFlow::Node nd, DataFlow::Configuration cfg, PathSummary summary) {
isSource(_, cfg, _) and
isSink(_, cfg, _) and
isLive(cfg) and
onPath(nd, cfg, summary)
}
@@ -737,6 +776,9 @@ class PathNode extends TPathNode {
/** Gets the underlying data flow tracking configuration of this path node. */
DataFlow::Configuration getConfiguration() { result = cfg }
/** Gets the summary of the path underlying this path node. */
PathSummary getPathSummary() { result = summary }
/** Gets a successor node of this path node. */
PathNode getASuccessor() {
exists(DataFlow::Node succ, PathSummary newSummary |

View File

@@ -88,6 +88,9 @@ module DataFlow {
/** Gets the container in which this node occurs. */
StmtContainer getContainer() { result = getBasicBlock().getContainer() }
/** Gets the toplevel in which this node occurs. */
TopLevel getTopLevel() { result = getContainer().getTopLevel() }
/**
* Holds if this data flow node accesses the global variable `g`, either directly
* or through the `window` object.

View File

@@ -0,0 +1,459 @@
/**
* Provides models of "portals", that is, interface points between different
* components of a code base. A typical example of a portal is a parameter
* of a function exported by an npm package (which is the only kind of component
* we support at the moment).
*
* Portals have entry and exit nodes. For example, the (unique) exit node of
* the parameter of an exported function is the `ParameterNode` corresponding
* to that parameter, while its entries are all nodes corresponding to arguments
* passed into the parameter via a call.
*
* The API of this library is not stable yet and may change.
*/
import javascript
private newtype TPortal =
MkNpmPackagePortal(string pkgName) {
NpmPackagePortal::imports(_, pkgName) or
NpmPackagePortal::imports(_, pkgName, _) or
NpmPackagePortal::exports(pkgName, _) or
MemberPortal::exports(pkgName, _, _)
} or
MkMemberPortal(Portal base, string prop) {
(
MemberPortal::reads(base, prop, _, _) or
MemberPortal::writes(base, prop, _, _)
) and
// only consider alpha-numeric properties, excluding special properties
// and properties whose names look like they are meant to be internal
prop.regexpMatch("(?!prototype$|__)[a-zA-Z_]\\w*")
} or
MkInstancePortal(Portal base) {
InstancePortal::instanceUse(base, _, _) or
InstancePortal::instanceDef(base, _, _) or
InstancePortal::instanceMemberDef(base, _, _, _)
} or
MkParameterPortal(Portal base, int i) {
ParameterPortal::parameter(base, i, _, _) or
ParameterPortal::argument(base, i, _, _)
} or
MkReturnPortal(Portal base) {
ReturnPortal::calls(_, base, _) or
ReturnPortal::returns(base, _, _)
}
/**
* A portal, that is, an interface point between different npm packages.
*/
cached
class Portal extends TPortal {
/**
* Gets an exit node for this portal, that is, a node from which data
* that comes through the portal emerges. The flag `isRemote`
* indicates whether data read from this node may come from a different
* package.
*/
cached
abstract DataFlow::SourceNode getAnExitNode(boolean isRemote);
/**
* Gets an entry node for this portal, that is, a node through which data
* enters the portal. The flag `escapes` indicates whether data written to
* the node may escape to a different package.
*/
cached
abstract DataFlow::Node getAnEntryNode(boolean escapes);
/**
* Gets the member portal with the given `name` of this portal, if any.
*/
cached
MemberPortal getMember(string name) {
result.getName() = name and
result.getBasePortal() = this
}
/**
* Gets the instance portal of this portal, if any.
*/
cached
InstancePortal getInstance() { result.getBasePortal() = this }
/**
* Gets the portal of parameter `idx` of this portal, if any.
*/
cached
ParameterPortal getParameter(int idx) {
result.getIndex() = idx and
result.getBasePortal() = this
}
/**
* Gets the return value portal of this portal, if any.
*/
cached
ReturnPortal getReturn() { result.getBasePortal() = this }
/**
* Gets a textual representation of this portal.
*
* Different portals must have different `toString`s, so the result of
* this predicate can be used to uniquely identify a portal.
*/
cached
abstract string toString();
/**
* INTERNAL: Do not use outside this library.
*
* The constructor depth of this portal, used to limit the number of
* portals.
*/
cached
abstract int depth();
}
/**
* A portal representing the exports value of the main module of an npm
* package (that is, a value of `module.exports` for CommonJS modules, or
* the module namespace object for ES2015 modules).
*
* Assignments to `module.exports` are entries to this portal, while
* imports are exits.
*/
private class NpmPackagePortal extends Portal, MkNpmPackagePortal {
string pkgName;
NpmPackagePortal() { this = MkNpmPackagePortal(pkgName) }
/** Gets the name of the npm package. */
string getName() { result = pkgName }
override DataFlow::SourceNode getAnExitNode(boolean isRemote) {
NpmPackagePortal::imports(result, pkgName) and
isRemote = false
}
override DataFlow::Node getAnEntryNode(boolean escapes) {
NpmPackagePortal::exports(pkgName, result) and
escapes = true
}
override string toString() { result = "(root https://www.npmjs.com/package/" + pkgName + ")" }
override int depth() { result = 1 }
}
private module NpmPackagePortal {
/** Gets an import of `imported` inside package `importer`. */
pragma[noinline]
private DataFlow::SourceNode getAModuleImport(NPMPackage importer, string imported) {
result = DataFlow::moduleImport(imported) and
result.getTopLevel() = importer.getAModule()
}
/** Gets an import of `member` from `imported` inside package `importer`. */
pragma[noinline]
private DataFlow::SourceNode getAModuleMemberImport(
NPMPackage importer, string imported, string member
) {
result = DataFlow::moduleMember(imported, member) and
result.getTopLevel() = importer.getAModule()
}
/** Holds if `imp` is an import of package `pkgName`. */
predicate imports(DataFlow::SourceNode imp, string pkgName) {
exists(NPMPackage pkg |
imp = getAModuleImport(pkg, pkgName) and
pkg.declaresDependency(pkgName, _)
)
}
/** Holds if `imp` imports `member` from package `pkgName`. */
predicate imports(DataFlow::SourceNode imp, string pkgName, string member) {
exists(NPMPackage pkg |
imp = getAModuleMemberImport(pkg, pkgName, member) and
pkg.declaresDependency(pkgName, _)
)
}
/** Gets the main module of package `pkgName`. */
Module packageMain(string pkgName) {
exists(PackageJSON pkg |
// don't construct portals for private packages
not pkg.isPrivate() and
// don't construct portals for vendored-in packages
exists(Folder pkgDir | pkgDir = pkg.getFile().getParentContainer() |
pkgDir.getRelativePath() = ""
or
not pkgDir.getParentContainer().getBaseName() = "node_modules"
) and
pkg.getPackageName() = pkgName and
result = pkg.getMainModule()
)
}
/** Holds if the main module of package `pkgName` exports `exp`. */
predicate exports(string pkgName, DataFlow::Node exp) {
exists(Module m | m = packageMain(pkgName) |
exists(AnalyzedPropertyWrite apw |
apw.writes(m.(AnalyzedModule).getModuleObject(), "exports", exp)
)
or
m.(ES2015Module).exports("default", exp.(DataFlow::ValueNode).getAstNode())
)
}
}
/**
* Gets the maximum depth a portal may have.
*
* This is a somewhat crude way of preventing us from constructing infinitely many portals.
*/
private int maxdepth() { result = 10 }
/**
* A portal that is constructed over some base portal.
*/
abstract private class CompoundPortal extends Portal {
Portal base;
bindingset[this]
CompoundPortal() {
// bound size of portal to prevent infinite recursion
base.depth() < maxdepth()
}
/** Gets the base portal over which this portal is constructed. */
Portal getBasePortal() { result = base }
override int depth() { result = base.depth() + 1 }
}
/**
* A portal corresponding to a named property of objects flowing through another portal.
*
* Entries to this portal are the right-hand sides of writes to the property, while
* property reads are exits.
*/
private class MemberPortal extends CompoundPortal, MkMemberPortal {
string prop;
MemberPortal() { this = MkMemberPortal(base, prop) }
/** Gets the name of this member. */
string getName() { result = prop }
override DataFlow::SourceNode getAnExitNode(boolean isRemote) {
MemberPortal::reads(base, prop, result, isRemote)
}
override DataFlow::Node getAnEntryNode(boolean escapes) {
MemberPortal::writes(base, prop, result, escapes)
}
override string toString() { result = "(member " + prop + " " + base + ")" }
}
private module MemberPortal {
/** Gets a node representing a value flowing through `base`, that is, either an entry node or an exit node. */
private DataFlow::SourceNode portalBaseRef(Portal base, boolean escapes) {
result = base.getAnExitNode(escapes)
or
result = base.getAnEntryNode(escapes).getALocalSource()
}
/** Holds if `read` is a read of property `prop` of a value flowing through `base`. */
predicate reads(Portal base, string prop, DataFlow::SourceNode read, boolean isRemote) {
read = portalBaseRef(base, isRemote).getAPropertyRead(prop)
or
// imports are a kind of property read
exists(string pkg |
NpmPackagePortal::imports(read, pkg, prop) and
base = MkNpmPackagePortal(pkg) and
isRemote = false
)
}
/** Holds if the main module of `pkgName` exports `rhs` under the name `prop`. */
predicate exports(string pkgName, string prop, DataFlow::Node rhs) {
exists(AnalyzedModule m, AnalyzedPropertyWrite apw |
m = NpmPackagePortal::packageMain(pkgName) and
apw.writes(m.getAnExportsValue(), prop, rhs)
)
}
/**
* Holds if there is a write to property `prop` of a value flowing through `base`, and `rhs` is the
* right-hand side of that write.
*/
predicate writes(Portal base, string prop, DataFlow::Node rhs, boolean escapes) {
portalBaseRef(base, escapes).hasPropertyWrite(prop, rhs)
or
InstancePortal::instanceMemberDef(base.(InstancePortal).getBasePortal(), prop, rhs, escapes)
or
// exports are a kind of property write.
exists(string pkgName |
exports(pkgName, prop, rhs) and
base = MkNpmPackagePortal(pkgName) and
escapes = true
)
}
}
/**
* A portal corresponding to an instantiation of functions or classes flowing through
* another portal.
*
* Entries to this portal are the return values of functions that flow through the base
* portal (to model the fact that `new f()` evaluates to the return value of `f` it is
* non-primitive), while exits are `new` expressions and other expressions referring to
* instances of functions/classes flowing through the base portal.
*/
private class InstancePortal extends CompoundPortal, MkInstancePortal {
InstancePortal() { this = MkInstancePortal(base) }
override DataFlow::SourceNode getAnExitNode(boolean isRemote) {
InstancePortal::instanceUse(base, result, isRemote)
}
override DataFlow::Node getAnEntryNode(boolean escapes) {
InstancePortal::instanceDef(base, result, escapes)
}
override string toString() { result = "(instance " + base + ")" }
}
private module InstancePortal {
/**
* Holds if `ctor` is a class, or a function that looks like a constructor function,
* that is, it contains a `this` expression.
*/
private predicate instantiable(DataFlow::Node ctor) {
ctor.getAstNode() instanceof ClassDefinition
or
exists(ThisExpr thiz | ctor = thiz.getBinder().flow())
}
/** Holds if `i` represents instances of `ctor`, which flows into `base`. */
private predicate isInstance(
Portal base, DataFlow::SourceNode ctor, AbstractInstance i, boolean escapes
) {
ctor = DataFlow::valueNode(i.getConstructor().getDefinition()) and
ctor.flowsTo(base.getAnEntryNode(escapes)) and
instantiable(ctor)
}
/** Holds if `nd` is an expression evaluating to an instance of `base`. */
predicate instanceUse(Portal base, DataFlow::SourceNode nd, boolean isRemote) {
nd = base.getAnExitNode(isRemote).getAnInstantiation()
or
isInstance(base, _, nd.analyze().getAValue(), isRemote)
}
/**
* Holds if there is a definition of a property `name` on an instance of `base`, and `rhs` is the
* right-hand side of that definition.
*/
predicate instanceMemberDef(Portal base, string name, DataFlow::Node rhs, boolean escapes) {
exists(AbstractInstance i, DataFlow::SourceNode ctor | isInstance(base, ctor, i, escapes) |
// ES2015 instance method
exists(MemberDefinition mem |
mem = ctor.getAstNode().(ClassDefinition).getAMember() and
not mem.isStatic() and
not mem instanceof ConstructorDefinition
|
name = mem.getName() and
rhs = DataFlow::valueNode(mem.getInit())
)
or
// ES5 instance method
exists(DataFlow::PropWrite pw |
pw = ctor.getAPropertyRead("prototype").getAPropertyWrite(name) and
rhs = pw.getRhs()
)
)
}
/** Holds if `nd` is a return node of a function flowing into `base`. */
predicate instanceDef(Portal base, DataFlow::Node nd, boolean escapes) {
exists(DataFlow::FunctionNode fn |
isInstance(base, fn, _, escapes) and
nd = fn.getAReturn() and
instantiable(fn)
)
}
}
/**
* A portal corresponding to a positional parameter of another portal.
*
* Arguments to functions flowing through the base portal are entries, while the corresponding
* parameter nodes are exits.
*/
class ParameterPortal extends CompoundPortal, MkParameterPortal {
int i;
ParameterPortal() { this = MkParameterPortal(base, i) }
/** Gets the index of the parameterb represented by this portal. */
int getIndex() { result = i }
override DataFlow::SourceNode getAnExitNode(boolean isRemote) {
ParameterPortal::parameter(base, i, result, isRemote)
}
override DataFlow::Node getAnEntryNode(boolean escapes) {
ParameterPortal::argument(base, i, result, escapes)
}
override string toString() { result = "(parameter " + i + " " + base + ")" }
}
private module ParameterPortal {
/** Holds if `param` is the `i`th parameter of a function flowing through `base`. */
predicate parameter(Portal base, int i, DataFlow::SourceNode param, boolean isRemote) {
param = base.getAnEntryNode(isRemote).getALocalSource().(DataFlow::FunctionNode).getParameter(i)
}
/** Holds if `arg` is the `i`th argument passed to an invocation of a function flowing through `base`. */
predicate argument(Portal base, int i, DataFlow::Node arg, boolean escapes) {
exists(DataFlow::InvokeNode invk |
invk = base.getAnExitNode(escapes).getAnInvocation() and
arg = invk.getArgument(i)
)
}
}
/**
* A portal corresponding to the return value of another portal.
*
* Returned expressions are entries, calls are exits.
*/
class ReturnPortal extends CompoundPortal, MkReturnPortal {
ReturnPortal() { this = MkReturnPortal(base) }
override DataFlow::SourceNode getAnExitNode(boolean isRemote) {
ReturnPortal::calls(result, base, isRemote)
}
override DataFlow::Node getAnEntryNode(boolean escapes) {
ReturnPortal::returns(base, result, escapes)
}
override string toString() { result = "(return " + base + ")" }
}
private module ReturnPortal {
/** Holds if `invk` is a call to a function flowing through `callee`. */
predicate calls(DataFlow::InvokeNode invk, Portal callee, boolean isRemote) {
invk = callee.getAnExitNode(isRemote).getAnInvocation()
}
/** Holds if `ret` is a return node of a function flowing through `callee`. */
predicate returns(Portal base, DataFlow::Node ret, boolean escapes) {
ret = base.getAnEntryNode(escapes).getALocalSource().(DataFlow::FunctionNode).getAReturn()
}
}

View File

@@ -70,6 +70,8 @@ predicate localFlowStep(
or
any(DataFlow::AdditionalFlowStep afs).step(pred, succ) and predlbl = succlbl
or
any(DataFlow::AdditionalFlowStep afs).step(pred, succ, predlbl, succlbl)
or
exists(boolean vp | configuration.isAdditionalFlowStep(pred, succ, vp) |
vp = true and
predlbl = succlbl
@@ -278,6 +280,9 @@ class PathSummary extends TPathSummary {
/** Indicates whether the path represented by this summary contains any call steps. */
boolean hasCall() { result = hasCall }
/** Gets the flow label describing the value at the start of this flow path. */
FlowLabel getStartLabel() { result = start }
/** Gets the flow label describing the value at the end of this flow path. */
FlowLabel getEndLabel() { result = end }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
import javascript
import semmle.javascript.dataflow.Portals
from Portal p, boolean escapes
select p, p.getAnEntryNode(escapes), escapes

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
import javascript
import semmle.javascript.dataflow.Portals
from Portal p, boolean isRemote
select p, p.getAnExitNode(isRemote), isRemote

View File

@@ -0,0 +1,9 @@
function Promise(exec) {
this.exec = exec;
}
Promise.prototype.then = function(fulfilled, rejected) {
rejected(null);
};
exports.Promise = Promise;

View File

@@ -0,0 +1,3 @@
{
"name": "bluebird"
}

View File

@@ -0,0 +1,2 @@
var Promise= require('./index').Promise;
var p = new Promise();

View File

@@ -0,0 +1,10 @@
function foo(cb) {
cb(foo);
return foo;
}
foo.f00 = foo;
foo(foo);
foo(foo());
exports.foo = foo;

View File

@@ -0,0 +1,3 @@
{
"name": "cyclic"
}

View File

@@ -0,0 +1 @@
module.exports = (x) => x;

View File

@@ -0,0 +1,3 @@
{
"name": "m1"
}

View File

@@ -0,0 +1,16 @@
export function foo(p) {
console.log(p.x.y);
p.z = "hi";
}
export default class {
constructor(name) {
this.name = name;
}
m(x) {
console.log(x + " " + this.name);
}
static s(y) { return y; }
};

View File

@@ -0,0 +1,4 @@
{
"name": "m2",
"main": "main.js"
}

View File

@@ -0,0 +1,3 @@
var m1 = require("m1");
module.exports = function() { console.log(m1("Hello, world!")); };

View File

@@ -0,0 +1,8 @@
{
"name": "client",
"private": true,
"dependencies": {
"m1": "*",
"m2": "*"
}
}

View File

@@ -0,0 +1 @@
require(".")();

View File

@@ -0,0 +1,5 @@
import { foo } from "m2";
var o = {
y: "?"
};
foo({ x: o });

View File

@@ -0,0 +1,5 @@
import A from "m2";
A.m("hi");
A.s("there");
new A("me").m("hi");
new A("me").s("there");

View File

@@ -0,0 +1 @@
exports.foo = function(x) {};

View File

@@ -0,0 +1,3 @@
{
"name": "m4"
}

View File

@@ -0,0 +1,162 @@
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | BrokenCryptoAlgorithm |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | ClearTextStorage |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | ClientSideUrlRedirect |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | CommandInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | ConditionalBypass |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | CorsMisconfigurationForCredentials |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | DifferentKindsComparisonBypass |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | DomBasedXss |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | FileAccessToHttp |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | InsecureRandomness |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | InsufficientPasswordHash |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | NosqlInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | ReflectedXss |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | RegExpInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | RemotePropertyInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | RequestForgery |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | ServerSideUrlRedirect |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | SqlInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | StackTraceExposure |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | StoredXss |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | TaintedFormatString |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | TaintedPath |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | UnsafeDeserialization |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | XmlBomb |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | XpathInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | data | (return (member h (root https://www.npmjs.com/package/infer-sources))) | data | Xxe |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | BrokenCryptoAlgorithm |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | ClearTextStorage |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | ClientSideUrlRedirect |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | CommandInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | ConditionalBypass |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | CorsMisconfigurationForCredentials |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | DifferentKindsComparisonBypass |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | DomBasedXss |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | FileAccessToHttp |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | InsecureRandomness |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | InsufficientPasswordHash |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | NosqlInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | ReflectedXss |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | RegExpInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | RemotePropertyInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | RequestForgery |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | ServerSideUrlRedirect |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | SqlInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | StackTraceExposure |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | StoredXss |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedFormatString |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedPath |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | UnsafeDeserialization |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | XmlBomb |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | XpathInjection |
| (parameter 0 (member h (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | Xxe |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | BrokenCryptoAlgorithm |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | ClearTextStorage |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | ClientSideUrlRedirect |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | CommandInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | ConditionalBypass |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | CorsMisconfigurationForCredentials |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | DifferentKindsComparisonBypass |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | DomBasedXss |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | FileAccessToHttp |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | InsecureRandomness |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | InsufficientPasswordHash |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | NosqlInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | ReflectedXss |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | RegExpInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | RemotePropertyInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | RequestForgery |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | ServerSideUrlRedirect |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | SqlInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | StackTraceExposure |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | StoredXss |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | TaintedFormatString |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | TaintedPath |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | UnsafeDeserialization |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | XmlBomb |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | XpathInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | data | Xxe |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | BrokenCryptoAlgorithm |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | ClearTextStorage |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | ClientSideUrlRedirect |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | CommandInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | ConditionalBypass |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | CorsMisconfigurationForCredentials |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | DifferentKindsComparisonBypass |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | DomBasedXss |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | FileAccessToHttp |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | InsecureRandomness |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | InsufficientPasswordHash |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | NosqlInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | ReflectedXss |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | RegExpInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | RemotePropertyInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | RequestForgery |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | ServerSideUrlRedirect |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | SqlInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | StackTraceExposure |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | StoredXss |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedFormatString |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedPath |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | UnsafeDeserialization |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | XmlBomb |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | XpathInjection |
| (parameter 0 (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notASink (root https://www.npmjs.com/package/infer-sources))) | taint | Xxe |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | BrokenCryptoAlgorithm |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | ClearTextStorage |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | ClientSideUrlRedirect |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | CommandInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | ConditionalBypass |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | CorsMisconfigurationForCredentials |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | DifferentKindsComparisonBypass |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | DomBasedXss |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | FileAccessToHttp |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | InsecureRandomness |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | InsufficientPasswordHash |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | NosqlInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | ReflectedXss |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | RegExpInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | RemotePropertyInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | RequestForgery |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | ServerSideUrlRedirect |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | SqlInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | StackTraceExposure |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | StoredXss |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | TaintedFormatString |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | TaintedPath |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | UnsafeDeserialization |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | XmlBomb |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | XpathInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | data | Xxe |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | BrokenCryptoAlgorithm |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | ClearTextStorage |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | ClientSideUrlRedirect |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | CommandInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | ConditionalBypass |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | CorsMisconfigurationForCredentials |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | DifferentKindsComparisonBypass |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | DomBasedXss |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | FileAccessToHttp |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | InsecureRandomness |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | InsufficientPasswordHash |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | NosqlInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | ReflectedXss |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | RegExpInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | RemotePropertyInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | RequestForgery |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | ServerSideUrlRedirect |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | SqlInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | StackTraceExposure |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | StoredXss |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedFormatString |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedPath |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | UnsafeDeserialization |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | XmlBomb |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | XpathInjection |
| (parameter 0 (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | (return (member notATaintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | Xxe |

View File

@@ -0,0 +1 @@
Security/Summaries/ExtractFlowStepSummaries.ql

View File

@@ -0,0 +1,39 @@
| (member name (parameter 0 (member regexpInj (root https://www.npmjs.com/package/infer-sources)))) | data | RegExpInjection |
| (member name (parameter 0 (member regexpInj (root https://www.npmjs.com/package/infer-sources)))) | taint | RegExpInjection |
| (parameter 0 (member codeInjection (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
| (parameter 0 (member codeInjection (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (parameter 0 (member commandInjection (root https://www.npmjs.com/package/infer-sources))) | data | CommandInjection |
| (parameter 0 (member commandInjection (root https://www.npmjs.com/package/infer-sources))) | taint | CommandInjection |
| (parameter 0 (member hashPass (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
| (parameter 0 (member hashPass (root https://www.npmjs.com/package/infer-sources))) | data | InsufficientPasswordHash |
| (parameter 0 (member hashPass (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (parameter 0 (member hashPass (root https://www.npmjs.com/package/infer-sources))) | taint | InsufficientPasswordHash |
| (parameter 0 (member mkdirp (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedPath |
| (parameter 0 (member multiple (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
| (parameter 0 (member multiple (root https://www.npmjs.com/package/infer-sources))) | data | CommandInjection |
| (parameter 0 (member multiple (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (parameter 0 (member multiple (root https://www.npmjs.com/package/infer-sources))) | taint | CommandInjection |
| (parameter 0 (member redirect (root https://www.npmjs.com/package/infer-sources))) | data | ServerSideUrlRedirect |
| (parameter 0 (member redirect (root https://www.npmjs.com/package/infer-sources))) | taint | ServerSideUrlRedirect |
| (parameter 0 (member reflected (root https://www.npmjs.com/package/infer-sources))) | data | ReflectedXss |
| (parameter 0 (member reflected (root https://www.npmjs.com/package/infer-sources))) | data | StoredXss |
| (parameter 0 (member reflected (root https://www.npmjs.com/package/infer-sources))) | taint | ReflectedXss |
| (parameter 0 (member reflected (root https://www.npmjs.com/package/infer-sources))) | taint | StoredXss |
| (parameter 0 (member regexpInj (root https://www.npmjs.com/package/infer-sources))) | data | RegExpInjection |
| (parameter 0 (member regexpInj (root https://www.npmjs.com/package/infer-sources))) | taint | RegExpInjection |
| (parameter 0 (member sqlInj (root https://www.npmjs.com/package/infer-sources))) | data | SqlInjection |
| (parameter 0 (member sqlInj (root https://www.npmjs.com/package/infer-sources))) | taint | SqlInjection |
| (parameter 0 (member taintedPath (root https://www.npmjs.com/package/infer-sources))) | data | TaintedPath |
| (parameter 0 (member taintedPath (root https://www.npmjs.com/package/infer-sources))) | taint | TaintedPath |
| (parameter 0 (member unsafeDes (root https://www.npmjs.com/package/infer-sources))) | data | UnsafeDeserialization |
| (parameter 0 (member unsafeDes (root https://www.npmjs.com/package/infer-sources))) | taint | UnsafeDeserialization |
| (parameter 0 (member xmlBomb (root https://www.npmjs.com/package/infer-sources))) | data | XmlBomb |
| (parameter 0 (member xmlBomb (root https://www.npmjs.com/package/infer-sources))) | data | Xxe |
| (parameter 0 (member xmlBomb (root https://www.npmjs.com/package/infer-sources))) | taint | XmlBomb |
| (parameter 0 (member xmlBomb (root https://www.npmjs.com/package/infer-sources))) | taint | Xxe |
| (parameter 0 (member xpathInj (root https://www.npmjs.com/package/infer-sources))) | data | XpathInjection |
| (parameter 0 (member xpathInj (root https://www.npmjs.com/package/infer-sources))) | taint | XpathInjection |
| (parameter 0 (member xxe (root https://www.npmjs.com/package/infer-sources))) | data | XmlBomb |
| (parameter 0 (member xxe (root https://www.npmjs.com/package/infer-sources))) | taint | XmlBomb |
| (parameter 1 (member remotePropeInjection (root https://www.npmjs.com/package/infer-sources))) | data | RemotePropertyInjection |
| (parameter 1 (member remotePropeInjection (root https://www.npmjs.com/package/infer-sources))) | taint | RemotePropertyInjection |

View File

@@ -0,0 +1 @@
Security/Summaries/ExtractSinkSummaries.ql

View File

@@ -0,0 +1,26 @@
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | ClientSideUrlRedirect |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | CodeInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | CommandInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | ConditionalBypass |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | CorsMisconfigurationForCredentials |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | DifferentKindsComparisonBypass |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | DomBasedXss |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | NosqlInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | ReflectedXss |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | RegExpInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | RemotePropertyInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | RequestForgery |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | ServerSideUrlRedirect |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | SqlInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | TaintedFormatString |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | TaintedPath |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | UnsafeDeserialization |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | XmlBomb |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | XpathInjection |
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | Xxe |
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | ClientSideUrlRedirect |
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | DomBasedXss |
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | XmlBomb |
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | XpathInjection |
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | Xxe |

View File

@@ -0,0 +1 @@
Security/Summaries/ExtractSourceSummaries.ql

View File

@@ -0,0 +1,192 @@
var http = require('http'),
url = require('url');
function listenForHeaders(cb) {
http.createServer(function (req, res) {
let cmd = url.parse(req.url, true).query.path;
cb(cmd); // sink
res.write('Hello World!');
res.end();
}).listen(8080);
};
function codeInjection(input) {
eval("url[" + input + "]");
}
function commandInjection(input) {
require("child_process").exec("ls " + input);
}
function multiple(input) {
codeInjection(input);
commandInjection(input);
}
function taintedPath(input) {
require("/tmp/" + input);
}
function regexpInj(data) {
new RegExp("^"+ data.name + "$", "i");
}
function xpathInj(userName) {
const xpath = require('xpath');
let badXPathExpr = xpath.parse("//users/user[login/text()='" + userName + "']/home_dir/text()");
badXPathExpr.select({
node: root
});
}
function xxe(input) {
const expat = require('node-expat');
var parser = new expat.Parser();
parser.write(input);
}
function xmlBomb(input) {
const libxmljs = require('libxmljs');
libxmljs.parseXml(input, { noent: true });
}
function hashPass(input) {
require('crypto').createCipher('aes192').write(input);
codeInjection(input)
}
function unsafeDes(input) {
const jsyaml = require("js-yaml");
let data;
return jsyaml.load(input);
}
function remoteProp(input1, input2, input3) {
var obj = url[input1];
obj[input2] = input3;
}
function reflected(userID) {
var express = require('express');
var app = express();
app.get('/user/:id', function(req, res) {
res.send("Unknown user: " + userID);
});
}
function redirect(input) {
var https = require('https');
var url = require('url');
var server = https.createServer(function(req, res) {
res.writeHead(302, { Location: '/' + input});
}).listen(8080)
}
function sqlInj(input) {
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
connection.connect();
connection.query('SELECT ' + input + ' AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
}
function createError () {
var err, status;
for (i = 0; i < 1000; i++) {
err = {};
status = err.a || err.b || err.c || err.d || err;
}
err.a = err.b = status
return err;
}
function forLoop(input) {
var intObj = {};
var res = 0;
for (var i = 0; i < input.x.length; i++) {
res += intObj.x + input.x[i];
}
if (res < 1000) {
intObj.res = res;
return intObj;
} else
return res;
}
function notASink(foo) {
return foo;
}
// this call should not make parameter `foo` a command injection sink
eval(notASink(42));
function taintedSource() {
return location.search;
}
function notATaintedSource(x) {
return x;
}
// this call should not make the return value of `notATaintedSource` a remote flow source
notATaintedSource(location.search);
function invoke(cb, x) {
cb(x);
}
// this call should not make the first argument to `cb` above a remote flow source
invoke((x)=>x, location.search);
function g(x) {
h(x);
}
function h(y) {
return y;
}
function mkdirp(path) {
path /* Semmle: sink: taint, TaintedPath */
}
module.exports = {
codeInjection: codeInjection,
commandInjection: commandInjection,
remotePropeInjection: remoteProp,
multiple: multiple,
taintedPath: taintedPath,
sqlInj: sqlInj,
listen: listenForHeaders,
createError: createError,
regexpInj: regexpInj,
xpathInj: xpathInj,
xmlBomb: xmlBomb,
hashPass: hashPass,
xxe: xxe,
unsafeDes: unsafeDes,
redirect: redirect,
reflected: reflected,
notASink: notASink,
taintedSource: taintedSource,
notATaintedSource: notATaintedSource,
invoke: invoke,
g: g,
h: h,
mkdirp: mkdirp
}

View File

@@ -0,0 +1,10 @@
{
"name": "infer-sources",
"version": "0.0.1",
"dependencies": {
"mysql": "2.15.0",
"xpath": "0.0.27",
"libxmljs": "0.19.1"
},
"main": "index.js"
}