mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
JS: Remove Portal-based flow summary implementation
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* Imports the standard library and any taint-tracking configuration classes for which
|
|
||||||
* flow summarization should be enabled.
|
|
||||||
*
|
|
||||||
* To enable flow summarization for other queries, import their configuration classes here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import javascript
|
|
||||||
import semmle.javascript.security.dataflow.CodeInjection
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 table
|
|
||||||
* @id js/step-summary-extraction
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Configurations
|
|
||||||
import PortalExitSource
|
|
||||||
import PortalEntrySink
|
|
||||||
|
|
||||||
from
|
|
||||||
TaintTracking::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
|
|
||||||
Portal p1, Portal p2, DataFlow::FlowLabel lbl1, DataFlow::FlowLabel lbl2,
|
|
||||||
DataFlow::MidPathNode last
|
|
||||||
where
|
|
||||||
cfg = source.getConfiguration() and
|
|
||||||
last = source.getASuccessor*() and
|
|
||||||
sink = last.getASuccessor() and
|
|
||||||
p1 = source.getNode().(PortalExitSource).getPortal() and
|
|
||||||
p2 = sink.getNode().(PortalEntrySink).getPortal() and
|
|
||||||
lbl1 = last.getPathSummary().getStartLabel() and
|
|
||||||
lbl2 = last.getPathSummary().getEndLabel() and
|
|
||||||
// avoid constructing infeasible paths
|
|
||||||
last.getPathSummary().hasCall() = false and
|
|
||||||
last.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()
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 table
|
|
||||||
* @id js/sink-summary-extraction
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Configurations
|
|
||||||
import PortalExitSource
|
|
||||||
import SinkFromAnnotation
|
|
||||||
|
|
||||||
from
|
|
||||||
DataFlow::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
|
|
||||||
Portal p, DataFlow::MidPathNode last
|
|
||||||
where
|
|
||||||
cfg = source.getConfiguration() and
|
|
||||||
last = source.getASuccessor*() and
|
|
||||||
sink = last.getASuccessor() and
|
|
||||||
p = source.getNode().(PortalExitSource).getPortal() and
|
|
||||||
// avoid constructing infeasible paths
|
|
||||||
last.getPathSummary().hasReturn() = false
|
|
||||||
select p.toString(), last.getPathSummary().getStartLabel().toString(), cfg.toString()
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 table
|
|
||||||
* @id js/source-summary-extraction
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Configurations
|
|
||||||
import PortalEntrySink
|
|
||||||
import SourceFromAnnotation
|
|
||||||
|
|
||||||
from
|
|
||||||
DataFlow::Configuration cfg, DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink,
|
|
||||||
Portal p, DataFlow::MidPathNode last
|
|
||||||
where
|
|
||||||
cfg = source.getConfiguration() and
|
|
||||||
last = source.getASuccessor*() and
|
|
||||||
sink = last.getASuccessor() and
|
|
||||||
p = sink.getNode().(PortalEntrySink).getPortal() and
|
|
||||||
// avoid constructing infeasible paths
|
|
||||||
last.getPathSummary().hasCall() = false
|
|
||||||
select p.toString(), last.getPathSummary().getEndLabel().toString(), cfg.toString()
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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() = this.getField(0) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the flow label of this source.
|
|
||||||
*/
|
|
||||||
DataFlow::FlowLabel getFlowLabel() { sourceFlowLabelSpec(result, this.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, this.getField(2)) }
|
|
||||||
|
|
||||||
override string toString() {
|
|
||||||
exists(string config |
|
|
||||||
if this.getField(2) = ""
|
|
||||||
then config = "any configuration"
|
|
||||||
else config = this.getConfiguration()
|
|
||||||
|
|
|
||||||
result = this.getPortal() + " as " + this.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() = this.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, this.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, this.getField(2)) }
|
|
||||||
|
|
||||||
override string toString() {
|
|
||||||
exists(string labels, string config |
|
|
||||||
labels = strictconcat(this.getFlowLabel(), " or ") and
|
|
||||||
if this.getField(2) = ""
|
|
||||||
then config = "any configuration"
|
|
||||||
else config = this.getConfiguration()
|
|
||||||
|
|
|
||||||
result = this.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() = this.getField(0) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the start flow label of this additional step.
|
|
||||||
*/
|
|
||||||
DataFlow::FlowLabel getStartFlowLabel() { result.toString() = this.getField(1) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the end portal of this additional step.
|
|
||||||
*/
|
|
||||||
Portal getEndPortal() { result.toString() = this.getField(2) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the end flow label of this additional step.
|
|
||||||
*/
|
|
||||||
DataFlow::FlowLabel getEndFlowLabel() { result.toString() = this.getField(3) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the configuration to which this step should be added.
|
|
||||||
*/
|
|
||||||
DataFlow::Configuration getConfiguration() { configSpec(result, this.getField(4)) }
|
|
||||||
|
|
||||||
override string toString() {
|
|
||||||
exists(string config |
|
|
||||||
if this.getField(4) = ""
|
|
||||||
then config = "any configuration"
|
|
||||||
else config = this.getConfiguration()
|
|
||||||
|
|
|
||||||
result =
|
|
||||||
"edge from " + this.getStartPortal() + " to " + this.getEndPortal() + ", transforming " +
|
|
||||||
this.getStartFlowLabel() + " into " + this.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 {
|
|
||||||
string flowLabel;
|
|
||||||
string config;
|
|
||||||
|
|
||||||
AdditionalSourceFromSpec() {
|
|
||||||
exists(Portal portal |
|
|
||||||
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 {
|
|
||||||
string flowLabel;
|
|
||||||
string config;
|
|
||||||
|
|
||||||
AdditionalSinkFromSpec() {
|
|
||||||
exists(Portal portal |
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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) { any() }
|
|
||||||
|
|
||||||
/** Gets the portal of which this is an entry node. */
|
|
||||||
Portal getPortal() { result = p }
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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) { any() }
|
|
||||||
|
|
||||||
/** Gets the portal of which this is an exit node. */
|
|
||||||
Portal getPortal() { result = p }
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.isData()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.isDataOrTaint()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
| (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))) | taint | (return (member h (root https://www.npmjs.com/package/infer-sources))) | taint | 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 | 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 | 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 | 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 | CodeInjection |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Summaries/ExtractFlowStepSummaries.ql
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
| (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 hashPass (root https://www.npmjs.com/package/infer-sources))) | data | CodeInjection |
|
|
||||||
| (parameter 0 (member hashPass (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
|
|
||||||
| (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))) | taint | CodeInjection |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Summaries/ExtractSinkSummaries.ql
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
| (parameter 0 (parameter 0 (member listen (root https://www.npmjs.com/package/infer-sources)))) | taint | CodeInjection |
|
|
||||||
| (return (member taintedSource (root https://www.npmjs.com/package/infer-sources))) | taint | CodeInjection |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Summaries/ExtractSourceSummaries.ql
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"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