mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #502 from xiemaisi/js/summaries
Approved by asger-semmle
This commit is contained in:
2
javascript/config/suites/javascript/flow-summaries
Normal file
2
javascript/config/suites/javascript/flow-summaries
Normal file
@@ -0,0 +1,2 @@
|
||||
+ semmlecode-javascript-queries/Security/Summaries/ExtractSourceSummaries.ql
|
||||
+ semmlecode-javascript-queries/Security/Summaries/ExtractSinkSummaries.ql
|
||||
223
javascript/documentation/flow-summaries.rst
Normal file
223
javascript/documentation/flow-summaries.rst
Normal 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``.
|
||||
35
javascript/ql/src/Security/Summaries/AllConfigurations.qll
Normal file
35
javascript/ql/src/Security/Summaries/AllConfigurations.qll
Normal 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
|
||||
@@ -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()
|
||||
20
javascript/ql/src/Security/Summaries/ExtractSinkSummaries.ql
Normal file
20
javascript/ql/src/Security/Summaries/ExtractSinkSummaries.ql
Normal 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()
|
||||
@@ -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()
|
||||
158
javascript/ql/src/Security/Summaries/ImportFromCsv.qll
Normal file
158
javascript/ql/src/Security/Summaries/ImportFromCsv.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
20
javascript/ql/src/Security/Summaries/PortalEntrySink.qll
Normal file
20
javascript/ql/src/Security/Summaries/PortalEntrySink.qll
Normal 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 }
|
||||
}
|
||||
20
javascript/ql/src/Security/Summaries/PortalExitSource.qll
Normal file
20
javascript/ql/src/Security/Summaries/PortalExitSource.qll
Normal 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 }
|
||||
}
|
||||
91
javascript/ql/src/Security/Summaries/Shared.qll
Normal file
91
javascript/ql/src/Security/Summaries/Shared.qll
Normal 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())
|
||||
)
|
||||
}
|
||||
}
|
||||
18
javascript/ql/src/Security/Summaries/SinkFromAnnotation.qll
Normal file
18
javascript/ql/src/Security/Summaries/SinkFromAnnotation.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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` → `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` → `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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
459
javascript/ql/src/semmle/javascript/dataflow/Portals.qll
Normal file
459
javascript/ql/src/semmle/javascript/dataflow/Portals.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
|
||||
1715
javascript/ql/test/library-tests/Portals/PortalEntry.expected
Normal file
1715
javascript/ql/test/library-tests/Portals/PortalEntry.expected
Normal file
File diff suppressed because it is too large
Load Diff
5
javascript/ql/test/library-tests/Portals/PortalEntry.ql
Normal file
5
javascript/ql/test/library-tests/Portals/PortalEntry.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.Portals
|
||||
|
||||
from Portal p, boolean escapes
|
||||
select p, p.getAnEntryNode(escapes), escapes
|
||||
1041
javascript/ql/test/library-tests/Portals/PortalExit.expected
Normal file
1041
javascript/ql/test/library-tests/Portals/PortalExit.expected
Normal file
File diff suppressed because it is too large
Load Diff
5
javascript/ql/test/library-tests/Portals/PortalExit.ql
Normal file
5
javascript/ql/test/library-tests/Portals/PortalExit.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.Portals
|
||||
|
||||
from Portal p, boolean isRemote
|
||||
select p, p.getAnExitNode(isRemote), isRemote
|
||||
@@ -0,0 +1,9 @@
|
||||
function Promise(exec) {
|
||||
this.exec = exec;
|
||||
}
|
||||
|
||||
Promise.prototype.then = function(fulfilled, rejected) {
|
||||
rejected(null);
|
||||
};
|
||||
|
||||
exports.Promise = Promise;
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "bluebird"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
var Promise= require('./index').Promise;
|
||||
var p = new Promise();
|
||||
10
javascript/ql/test/library-tests/Portals/src/cyclic/index.js
Normal file
10
javascript/ql/test/library-tests/Portals/src/cyclic/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function foo(cb) {
|
||||
cb(foo);
|
||||
return foo;
|
||||
}
|
||||
foo.f00 = foo;
|
||||
|
||||
foo(foo);
|
||||
foo(foo());
|
||||
|
||||
exports.foo = foo;
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "cyclic"
|
||||
}
|
||||
1
javascript/ql/test/library-tests/Portals/src/m1/index.js
Normal file
1
javascript/ql/test/library-tests/Portals/src/m1/index.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = (x) => x;
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "m1"
|
||||
}
|
||||
16
javascript/ql/test/library-tests/Portals/src/m2/main.js
Normal file
16
javascript/ql/test/library-tests/Portals/src/m2/main.js
Normal 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; }
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "m2",
|
||||
"main": "main.js"
|
||||
}
|
||||
3
javascript/ql/test/library-tests/Portals/src/m3/index.js
Normal file
3
javascript/ql/test/library-tests/Portals/src/m3/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
var m1 = require("m1");
|
||||
|
||||
module.exports = function() { console.log(m1("Hello, world!")); };
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "client",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"m1": "*",
|
||||
"m2": "*"
|
||||
}
|
||||
}
|
||||
1
javascript/ql/test/library-tests/Portals/src/m3/tst.js
Normal file
1
javascript/ql/test/library-tests/Portals/src/m3/tst.js
Normal file
@@ -0,0 +1 @@
|
||||
require(".")();
|
||||
5
javascript/ql/test/library-tests/Portals/src/m3/tst2.js
Normal file
5
javascript/ql/test/library-tests/Portals/src/m3/tst2.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { foo } from "m2";
|
||||
var o = {
|
||||
y: "?"
|
||||
};
|
||||
foo({ x: o });
|
||||
5
javascript/ql/test/library-tests/Portals/src/m3/tst3.js
Normal file
5
javascript/ql/test/library-tests/Portals/src/m3/tst3.js
Normal 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");
|
||||
1
javascript/ql/test/library-tests/Portals/src/m4/index.js
Normal file
1
javascript/ql/test/library-tests/Portals/src/m4/index.js
Normal file
@@ -0,0 +1 @@
|
||||
exports.foo = function(x) {};
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "m4"
|
||||
}
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/Summaries/ExtractFlowStepSummaries.ql
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/Summaries/ExtractSinkSummaries.ql
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/Summaries/ExtractSourceSummaries.ql
|
||||
192
javascript/ql/test/query-tests/Security/Summaries/index.js
Normal file
192
javascript/ql/test/query-tests/Security/Summaries/index.js
Normal 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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user