Merge pull request #2705 from erik-krogh/CVE75

Approved by asgerf
This commit is contained in:
semmle-qlci
2020-01-30 13:07:05 +00:00
committed by GitHub
3 changed files with 133 additions and 9 deletions

View File

@@ -906,9 +906,7 @@ module NodeJSLib {
* An instance of an EventEmitter that is imported through the 'events' module.
*/
private class ImportedNodeJSEventEmitter extends NodeJSEventEmitter {
ImportedNodeJSEventEmitter() {
this = getAnEventEmitterImport().getAnInstantiation()
}
ImportedNodeJSEventEmitter() { this = getAnEventEmitterImport().getAnInstantiation() }
}
/**
@@ -922,20 +920,33 @@ module NodeJSLib {
}
/**
* An instantiation of a class that extends EventEmitter.
*
* An instantiation of a class that extends EventEmitter.
*
* By extending `NodeJSEventEmitter' we get data-flow on the events passing through this EventEmitter.
*/
private class CustomEventEmitter extends NodeJSEventEmitter {
class CustomEventEmitter extends NodeJSEventEmitter {
EventEmitterSubClass clazz;
CustomEventEmitter() {
this = any(EventEmitterSubClass clazz).getAClassReference().getAnInstantiation()
if exists(clazz.getAClassReference().getAnInstantiation()) then
this = clazz.getAClassReference().getAnInstantiation()
else
// In case there are no explicit instantiations of the clazz, then we still want to track data flow between `this` nodes.
// This cannot produce false flow as the `.ref()` method below is always used when creating event-registrations/event-dispatches.
this = clazz
}
override DataFlow::SourceNode ref() {
result = NodeJSEventEmitter.super.ref() and not this = clazz
or
result = clazz.getAReceiverNode()
}
}
/**
* A registration of an event handler on a NodeJS EventEmitter instance.
*/
private class NodeJSEventRegistration extends EventRegistration::DefaultEventRegistration, DataFlow::MethodCallNode {
private class NodeJSEventRegistration extends EventRegistration::DefaultEventRegistration,
DataFlow::MethodCallNode {
override NodeJSEventEmitter emitter;
NodeJSEventRegistration() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
@@ -944,9 +955,94 @@ module NodeJSLib {
/**
* A dispatch of an event on a NodeJS EventEmitter instance.
*/
private class NodeJSEventDispatch extends EventDispatch::DefaultEventDispatch, DataFlow::MethodCallNode {
private class NodeJSEventDispatch extends EventDispatch::DefaultEventDispatch,
DataFlow::MethodCallNode {
override NodeJSEventEmitter emitter;
NodeJSEventDispatch() { this = emitter.ref().getAMethodCall("emit") }
}
/**
* An instance of net.createServer(), which creates a new TCP/IPC server.
*/
private class NodeJSNetServer extends DataFlow::SourceNode {
NodeJSNetServer() { this = DataFlow::moduleMember("net", "createServer").getAnInvocation() }
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
t.start() and result = this
or
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
}
/**
* Gets a reference to this server.
*/
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
}
/**
* A connection opened on a NodeJS net server.
*/
private class NodeJSNetServerConnection extends EventEmitter::Range {
NodeJSNetServer server;
NodeJSNetServerConnection() {
exists(DataFlow::MethodCallNode call |
call = server.ref().getAMethodCall("on") and
call.getArgument(0).mayHaveStringValue("connection")
|
this = call.getCallback(1).getParameter(0)
)
}
DataFlow::SourceNode ref() { result = EventEmitter::trackEventEmitter(this) }
}
/**
* A registration of an event handler on a NodeJS net server instance.
*/
private class NodeJSNetServerRegistration extends EventRegistration::DefaultEventRegistration,
DataFlow::MethodCallNode {
override NodeJSNetServerConnection emitter;
NodeJSNetServerRegistration() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
}
/**
* A data flow node representing data received from a client to a NodeJS net server, viewed as remote user input.
*/
private class NodeJSNetServerItemAsRemoteFlow extends RemoteFlowSource {
NodeJSNetServerRegistration reg;
NodeJSNetServerItemAsRemoteFlow() { this = reg.getReceivedItem(_) }
override string getSourceType() { result = "NodeJS server" }
}
/**
* An instantiation of the `respjs` library, which is an EventEmitter.
*/
private class RespJS extends NodeJSEventEmitter {
RespJS() {
this = DataFlow::moduleImport("respjs").getAnInstantiation()
}
}
/**
* A event dispatch that serializes the input data and emits the result on the "data" channel.
*/
private class RespWrite extends EventDispatch::DefaultEventDispatch,
DataFlow::MethodCallNode {
override RespJS emitter;
RespWrite() { this = emitter.ref().getAMethodCall("write") }
override string getChannel() {
result = "data"
}
override DataFlow::Node getSentItem(int i) {
i = 0 and result = this.getArgument(i)
}
}
}

View File

@@ -0,0 +1,22 @@
const EventEmitter = require("events");
class MyEmitter extends EventEmitter {
foo() {
this.emit("foo", "bar");
this.on("foo", (data) => {});
}
}
class MySecondEmitter extends EventEmitter {
foo() {
this.emit("bar", "baz");
this.on("bar", (data) => {});
}
}
var x = new MySecondEmitter();
x.emit("bar", "baz2");
var y = new MySecondEmitter();
y.emit("bar", "baz3");
y.on("bar", (yData) => {})

View File

@@ -1,3 +1,9 @@
| customEmitter.js:5:20:5:24 | "bar" | customEmitter.js:6:19:6:22 | data |
| customEmitter.js:12:21:12:25 | "baz" | customEmitter.js:13:23:13:26 | data |
| customEmitter.js:12:21:12:25 | "baz" | customEmitter.js:22:14:22:18 | yData |
| customEmitter.js:18:15:18:20 | "baz2" | customEmitter.js:13:23:13:26 | data |
| customEmitter.js:21:15:21:20 | "baz3" | customEmitter.js:13:23:13:26 | data |
| customEmitter.js:21:15:21:20 | "baz3" | customEmitter.js:22:14:22:18 | yData |
| tst.js:9:23:9:33 | 'FirstData' | tst.js:6:40:6:44 | first |
| tst.js:10:24:10:35 | 'SecondData' | tst.js:7:32:7:37 | second |
| tst.js:15:24:15:39 | 'OtherFirstData' | tst.js:14:41:14:50 | otherFirst |