JavaScript: Allow summary details to be omitted.

If a summary does not specify a configuration, it is taken to apply to all configurations without custom sanitisers/barriers.

If a source summary does not specify a flow label, `data` is assumed.

If a sink summary does not specify a flow label, both `data` and `taint` are assumed.

Flow step summaries cannot omit flow labels.

Note that the standard extraction queries always provide explicit configurations and flow labels, and hence do not exercise this functionality.
This commit is contained in:
Max Schaefer 2018-11-08 09:39:56 +00:00
Родитель 7c87c43511
Коммит bdf29d010a
3 изменённых файлов: 79 добавлений и 19 удалений

Просмотреть файл

@ -6,6 +6,7 @@
import javascript
import semmle.javascript.dataflow.Portals
import external.ExternalArtifact
private import Shared
/**
* An additional source specified in an `additional-sources.csv` file.
@ -26,18 +27,25 @@ class AdditionalSourceSpec extends ExternalData {
* Gets the flow label of this source.
*/
DataFlow::FlowLabel getFlowLabel() {
result.toString() = getField(1)
sourceFlowLabelSpec(result, getField(1))
}
/**
* Gets the configuration for which this is a source.
* Gets the configuration for which this is a source, or any
* configuration if this source does not specify a configuration.
*/
DataFlow::Configuration getConfiguration() {
result.toString() = getField(2)
configSpec(result, getField(2))
}
override string toString() {
result = getPortal() + " as " + getFlowLabel() + " source for " + getConfiguration()
exists (string config |
if getField(2) = "" then
config = "any configuration"
else
config = getConfiguration() |
result = getPortal() + " as " + getFlowLabel() + " source for " + config
)
}
}
@ -69,21 +77,30 @@ class AdditionalSinkSpec extends ExternalData {
}
/**
* Gets the flow label of this sink.
* Gets the flow label of this sink, or any standard flow label if this sink
* does not specify a flow label.
*/
DataFlow::FlowLabel getFlowLabel() {
result.toString() = getField(1)
sinkFlowLabelSpec(result, getField(1))
}
/**
* Gets the configuration for which this is a sink.
* Gets the configuration for which this is a sink, or any configuration if
* this sink does not specify a configuration.
*/
DataFlow::Configuration getConfiguration() {
result.toString() = getField(2)
configSpec(result, getField(2))
}
override string toString() {
result = getPortal() + " as " + getFlowLabel() + " sink for " + getConfiguration()
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
)
}
}
@ -138,13 +155,19 @@ class AdditionalStepSpec extends ExternalData {
* Gets the configuration to which this step should be added.
*/
DataFlow::Configuration getConfiguration() {
result.toString() = getField(4)
configSpec(result, getField(4))
}
override string toString() {
result = "edge from " + getStartPortal() + " to " + getEndPortal() +
", transforming " + getStartFlowLabel() + " into " + getEndFlowLabel() +
" for " + getConfiguration()
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
)
}
}

Просмотреть файл

@ -6,6 +6,7 @@
import javascript
import semmle.javascript.dataflow.Portals
import external.ExternalArtifact
import Shared
/**
* An external predicate providing information about additional sources.
@ -42,8 +43,7 @@ private class AdditionalSourceFromSpec extends DataFlow::AdditionalSource {
}
override predicate isSourceFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
cfg.toString() = config and
lbl = flowLabel
configSpec(cfg, config) and sourceFlowLabelSpec(lbl, flowLabel)
}
}
@ -61,8 +61,7 @@ private class AdditionalSinkFromSpec extends DataFlow::AdditionalSink {
}
override predicate isSinkFor(DataFlow::Configuration cfg, DataFlow::FlowLabel lbl) {
cfg.toString() = config and
lbl = flowLabel
configSpec(cfg, config) and sinkFlowLabelSpec(lbl, flowLabel)
}
}
/**
@ -75,8 +74,9 @@ private class AdditionalFlowStepFromSpec extends DataFlow::Configuration {
string endFlowLabel;
AdditionalFlowStepFromSpec() {
exists (Portal startPortal, Portal endPortal |
additionalSteps(startPortal.toString(), startFlowLabel, endPortal.toString(), endFlowLabel, this) and
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(_)
)

Просмотреть файл

@ -0,0 +1,37 @@
/**
* Provides utility predicates for working with flow summary specifications.
*/
import javascript
/**
* Holds if `config` matches `spec`, that is, either `spec` is the name of `config`
* or `spec` is the empty string and `config` is an arbitrary configuration.
*/
predicate configSpec(DataFlow::Configuration config, string spec) {
config.toString() = 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
}