mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
JavaScript: Add support for annotation comments specifying additional sources and sinks.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
import AllConfigurations
|
||||
import PortalExitSource
|
||||
import SinkFromAnnotation
|
||||
|
||||
from TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
Portal p
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
import AllConfigurations
|
||||
import PortalEntrySink
|
||||
import SourceFromAnnotation
|
||||
|
||||
from TaintTracking::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
Portal p
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user