JavaScript: Add support for annotation comments specifying additional sources and sinks.

This commit is contained in:
Max Schaefer
2018-11-08 10:27:28 +00:00
parent bdf29d010a
commit 132570940a
7 changed files with 101 additions and 1 deletions

View File

@@ -9,6 +9,7 @@
import AllConfigurations
import PortalExitSource
import SinkFromAnnotation
from TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
Portal p

View File

@@ -9,6 +9,7 @@
import AllConfigurations
import PortalEntrySink
import SourceFromAnnotation
from TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
Portal p

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@
| (parameter (member (root https://www.npmjs.com/package/infer-sources) hashPass) 0) | data | InsufficientPasswordHash |
| (parameter (member (root https://www.npmjs.com/package/infer-sources) hashPass) 0) | taint | CodeInjection |
| (parameter (member (root https://www.npmjs.com/package/infer-sources) hashPass) 0) | taint | InsufficientPasswordHash |
| (parameter (member (root https://www.npmjs.com/package/infer-sources) mkdirp) 0) | taint | TaintedPath |
| (parameter (member (root https://www.npmjs.com/package/infer-sources) multiple) 0) | data | CodeInjection |
| (parameter (member (root https://www.npmjs.com/package/infer-sources) multiple) 0) | data | CommandInjection |
| (parameter (member (root https://www.npmjs.com/package/infer-sources) multiple) 0) | taint | CodeInjection |

View File

@@ -161,6 +161,10 @@ function h(y) {
return y;
}
function mkdirp(path) {
path /* Semmle: sink: taint, TaintedPath */
}
module.exports = {
codeInjection: codeInjection,
commandInjection: commandInjection,
@@ -183,5 +187,6 @@ module.exports = {
notACookieSource: notACookieSource,
invoke: invoke,
g: g,
h: h
h: h,
mkdirp: mkdirp
}