mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
implement SocketIO on top of the EventEmitter model
This commit is contained in:
@@ -117,9 +117,9 @@ module EventRegistration {
|
||||
|
||||
abstract DataFlow::Node getReceivedItem(int i);
|
||||
|
||||
abstract DataFlow::Node getAReturnedValue();
|
||||
DataFlow::Node getAReturnedValue() { none() }
|
||||
|
||||
abstract EventDispatch::Range getAReturnDispatch();
|
||||
EventDispatch::Range getAReturnDispatch() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,10 +136,6 @@ module EventRegistration {
|
||||
override DataFlow::Node getReceivedItem(int i) {
|
||||
result = this.getABoundCallbackParameter(1, i)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAReturnedValue() { none() }
|
||||
|
||||
override EventDispatch::Range getAReturnDispatch() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,3 +223,4 @@ private class EventEmitterTaintStep extends DataFlow::AdditionalFlowStep {
|
||||
succ = dispatch
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
/**
|
||||
* Provides classes for working with server-side socket.io code
|
||||
@@ -24,46 +23,64 @@ module SocketIO {
|
||||
result = DataFlow::moduleImport("socket.io").getAMemberCall("listen")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that may refer to the socket.io server created at `srv`.
|
||||
*/
|
||||
private DataFlow::SourceNode server(ServerObject srv, DataFlow::TypeTracker t) {
|
||||
result = newServer() and
|
||||
srv = MkServer(result) and
|
||||
t.start()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = server(srv, t2) |
|
||||
result = pred.track(t2, t)
|
||||
abstract private class SocketIOObject extends DataFlow::SourceNode, EventEmitter::Range {
|
||||
abstract DataFlow::SourceNode ref();
|
||||
}
|
||||
|
||||
/** A socket.io server. */
|
||||
class ServerObject extends SocketIOObject {
|
||||
ServerObject() {
|
||||
this = newServer()
|
||||
}
|
||||
|
||||
/** Gets the default namespace of this server. */
|
||||
NamespaceObject getDefaultNamespace() { result = MkNamespace(this, "/") }
|
||||
|
||||
/** Gets the namespace with the given path of this server. */
|
||||
NamespaceObject getNamespace(string path) { result = MkNamespace(this, path) }
|
||||
|
||||
/**
|
||||
* Gets a data flow node that may refer to the socket.io server created at `srv`.
|
||||
*/
|
||||
private DataFlow::SourceNode server(DataFlow::TypeTracker t) {
|
||||
result = this and t.start()
|
||||
or
|
||||
// invocation of a chainable method
|
||||
exists(DataFlow::MethodCallNode mcn, string m |
|
||||
m = "adapter" or
|
||||
m = "attach" or
|
||||
m = "bind" or
|
||||
m = "listen" or
|
||||
m = "onconnection" or
|
||||
m = "origins" or
|
||||
m = "path" or
|
||||
m = "serveClient" or
|
||||
m = "set"
|
||||
|
|
||||
mcn = pred.getAMethodCall(m) and
|
||||
// exclude getter versions
|
||||
exists(mcn.getAnArgument()) and
|
||||
result = mcn and
|
||||
t = t2.continue()
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = server(t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
// invocation of a chainable method
|
||||
exists(DataFlow::MethodCallNode mcn, string m |
|
||||
m = "adapter" or
|
||||
m = "attach" or
|
||||
m = "bind" or
|
||||
m = "listen" or
|
||||
m = "onconnection" or
|
||||
m = "origins" or
|
||||
m = "path" or
|
||||
m = "serveClient" or
|
||||
m = "set" or
|
||||
m = EventEmitter::chainableMethod()
|
||||
|
|
||||
mcn = pred.getAMethodCall(m) and
|
||||
// exclude getter versions
|
||||
exists(mcn.getAnArgument()) and
|
||||
result = mcn and
|
||||
t = t2.continue()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode ref() { result = server(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/** A data flow node that may produce (that is, create or return) a socket.io server. */
|
||||
class ServerNode extends DataFlow::SourceNode {
|
||||
ServerObject srv;
|
||||
ServerObject obj;
|
||||
|
||||
ServerNode() { this = server(srv, DataFlow::TypeTracker::end()) }
|
||||
ServerNode() { this = obj.ref() }
|
||||
|
||||
/** Gets the server to which this node refers. */
|
||||
ServerObject getServer() { result = srv }
|
||||
ServerObject getServer() { result = obj }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,157 +101,184 @@ module SocketIO {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that may refer to the socket.io namespace created at `ns`.
|
||||
* A reference to a namespace object.
|
||||
*/
|
||||
private DataFlow::SourceNode namespace(NamespaceObject ns, DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
exists(ServerNode srv |
|
||||
// namespace lookup on `srv`
|
||||
result = srv.getAPropertyRead("sockets") and
|
||||
ns = srv.getServer().getDefaultNamespace()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode mcn, string path |
|
||||
mcn = srv.getAMethodCall("of") and
|
||||
mcn.getArgument(0).mayHaveStringValue(path) and
|
||||
result = mcn and
|
||||
ns = MkNamespace(srv.getServer(), path)
|
||||
class NamespaceBase extends SocketIOObject {
|
||||
NamespaceObject ns;
|
||||
|
||||
NamespaceBase() {
|
||||
exists(ServerObject srv |
|
||||
// namespace lookup on `srv`
|
||||
this = srv.ref().getAPropertyRead("sockets") and
|
||||
ns = srv.getDefaultNamespace()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode mcn, string path |
|
||||
mcn = srv.ref().getAMethodCall("of") and
|
||||
mcn.getArgument(0).mayHaveStringValue(path) and
|
||||
this = mcn and
|
||||
ns = MkNamespace(srv, path)
|
||||
)
|
||||
or
|
||||
// invocation of a method that `srv` forwards to its default namespace
|
||||
this = srv.ref().getAMethodCall(namespaceChainableMethod()) and
|
||||
ns = srv.getDefaultNamespace()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the namespace to which this object refers. */
|
||||
NamespaceObject getNamespace() { result = ns }
|
||||
|
||||
/**
|
||||
* Gets a data flow node that may refer to the socket.io namespace created at `ns`.
|
||||
*/
|
||||
private DataFlow::SourceNode namespace(DataFlow::TypeTracker t) {
|
||||
t.start() and result = this
|
||||
or
|
||||
// invocation of a method that `srv` forwards to its default namespace
|
||||
result = srv.getAMethodCall(namespaceChainableMethod()) and
|
||||
ns = srv.getServer().getDefaultNamespace()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::SourceNode pred, DataFlow::TypeTracker t2 | pred = namespace(ns, t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
// invocation of a chainable method
|
||||
result = pred.getAMethodCall(namespaceChainableMethod()) and
|
||||
t = t2.continue()
|
||||
or
|
||||
// invocation of chainable getter method
|
||||
exists(string m |
|
||||
m = "json" or
|
||||
m = "local" or
|
||||
m = "volatile"
|
||||
|
|
||||
result = pred.getAPropertyRead(m) and
|
||||
exists(DataFlow::SourceNode pred, DataFlow::TypeTracker t2 | pred = namespace(t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
// invocation of a chainable method
|
||||
result = pred.getAMethodCall(namespaceChainableMethod()) and
|
||||
t = t2.continue()
|
||||
or
|
||||
// invocation of chainable getter method
|
||||
exists(string m |
|
||||
m = "json" or
|
||||
m = "local" or
|
||||
m = "volatile"
|
||||
|
|
||||
result = pred.getAPropertyRead(m) and
|
||||
t = t2.continue()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode ref() { result = namespace(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/** A data flow node that may produce a namespace object. */
|
||||
class NamespaceNode extends DataFlow::SourceNode {
|
||||
NamespaceObject ns;
|
||||
NamespaceBase namespace;
|
||||
|
||||
NamespaceNode() { this = namespace(ns, DataFlow::TypeTracker::end()) }
|
||||
NamespaceNode() { this = namespace.ref() }
|
||||
|
||||
/** Gets the namespace to which this node refers. */
|
||||
NamespaceObject getNamespace() { result = ns }
|
||||
NamespaceObject getNamespace() { result = namespace.getNamespace() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that may refer to a socket.io socket belonging to namespace `ns`.
|
||||
*/
|
||||
private DataFlow::SourceNode socket(NamespaceObject ns, DataFlow::TypeTracker t) {
|
||||
// callback accepting a socket
|
||||
t.start() and
|
||||
exists(DataFlow::SourceNode base, string connect, DataFlow::MethodCallNode on |
|
||||
(
|
||||
ns = base.(ServerNode).getServer().getDefaultNamespace() or
|
||||
ns = base.(NamespaceNode).getNamespace()
|
||||
) and
|
||||
(connect = "connect" or connect = "connection")
|
||||
|
|
||||
on = base.getAMethodCall(EventEmitter::on()) and
|
||||
on.getArgument(0).mayHaveStringValue(connect) and
|
||||
result = on.getCallback(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::SourceNode pred, DataFlow::TypeTracker t2 | pred = socket(ns, t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
// invocation of a chainable method
|
||||
exists(string m |
|
||||
m = "binary" or
|
||||
m = "compress" or
|
||||
m = "disconnect" or
|
||||
m = "emit" or
|
||||
m = "in" or
|
||||
m = "join" or
|
||||
m = "leave" or
|
||||
m = "send" or
|
||||
m = "to" or
|
||||
m = "use" or
|
||||
m = "write" or
|
||||
m = EventEmitter::chainableMethod()
|
||||
/** An socket object from SocketIO */
|
||||
class SocketObject extends SocketIOObject {
|
||||
NamespaceObject ns;
|
||||
|
||||
SocketObject() {
|
||||
exists(DataFlow::SourceNode base, string connect, DataFlow::MethodCallNode on |
|
||||
(
|
||||
ns = any(ServerObject o | o.ref() = base).getDefaultNamespace() or
|
||||
ns = any(NamespaceBase o | o.ref() = base).getNamespace()
|
||||
) and
|
||||
(connect = "connect" or connect = "connection")
|
||||
|
|
||||
result = pred.getAMethodCall(m) and
|
||||
t = t2.continue()
|
||||
on = base.getAMethodCall(EventEmitter::on()) and
|
||||
on.getArgument(0).mayHaveStringValue(connect) and
|
||||
this = on.getCallback(1).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the namespace to which this socket belongs. */
|
||||
NamespaceObject getNamespace() { result = ns }
|
||||
|
||||
private DataFlow::SourceNode socket(DataFlow::TypeTracker t) {
|
||||
result = this and t.start()
|
||||
or
|
||||
// invocation of a chainable getter method
|
||||
exists(string m |
|
||||
m = "broadcast" or
|
||||
m = "json" or
|
||||
m = "local" or
|
||||
m = "volatile"
|
||||
|
|
||||
result = pred.getAPropertyRead(m) and
|
||||
t = t2.continue()
|
||||
exists(DataFlow::SourceNode pred, DataFlow::TypeTracker t2 | pred = socket(t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
// invocation of a chainable method
|
||||
exists(string m |
|
||||
m = "binary" or
|
||||
m = "compress" or
|
||||
m = "disconnect" or
|
||||
m = "emit" or
|
||||
m = "in" or
|
||||
m = "join" or
|
||||
m = "leave" or
|
||||
m = "send" or
|
||||
m = "to" or
|
||||
m = "use" or
|
||||
m = "write" or
|
||||
m = EventEmitter::chainableMethod()
|
||||
|
|
||||
result = pred.getAMethodCall(m) and
|
||||
t = t2.continue()
|
||||
)
|
||||
or
|
||||
// invocation of a chainable getter method
|
||||
exists(string m |
|
||||
m = "broadcast" or
|
||||
m = "json" or
|
||||
m = "local" or
|
||||
m = "volatile"
|
||||
|
|
||||
result = pred.getAPropertyRead(m) and
|
||||
t = t2.continue()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode ref() { result = socket(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/** A data flow node that may produce a socket object. */
|
||||
class SocketNode extends DataFlow::SourceNode {
|
||||
NamespaceObject ns;
|
||||
SocketObject socket;
|
||||
|
||||
SocketNode() { this = socket(ns, DataFlow::TypeTracker::end()) }
|
||||
SocketNode() { this = socket.ref() }
|
||||
|
||||
/** Gets the namespace to which this socket belongs. */
|
||||
NamespaceObject getNamespace() { result = ns }
|
||||
NamespaceObject getNamespace() { result = socket.getNamespace() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node representing an API call that receives data from a client.
|
||||
*/
|
||||
class ReceiveNode extends DataFlow::MethodCallNode {
|
||||
SocketNode socket;
|
||||
class ReceiveNode extends EventRegistration::Range, DataFlow::MethodCallNode {
|
||||
override SocketObject emitter;
|
||||
|
||||
ReceiveNode() { this = socket.getAMethodCall(EventEmitter::on()) }
|
||||
ReceiveNode() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
|
||||
|
||||
/** Gets the socket through which data is received. */
|
||||
SocketNode getSocket() { result = socket }
|
||||
|
||||
/** Gets the event name associated with the data, if it can be determined. */
|
||||
string getEventName() { getArgument(0).mayHaveStringValue(result) }
|
||||
SocketObject getSocket() { result = emitter }
|
||||
|
||||
/** Gets the callback that handles data received from a client. */
|
||||
private DataFlow::FunctionNode getListener() { result = getCallback(1) }
|
||||
DataFlow::FunctionNode getListener() { result = getCallback(1) }
|
||||
|
||||
/** Gets the `i`th parameter through which data is received from a client. */
|
||||
DataFlow::SourceNode getReceivedItem(int i) {
|
||||
override DataFlow::SourceNode getReceivedItem(int i) {
|
||||
exists(DataFlow::FunctionNode cb | cb = getListener() and result = cb.getParameter(i) |
|
||||
// exclude last parameter if it looks like a callback
|
||||
result != cb.getLastParameter() or not exists(result.getAnInvocation())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node representing data received from a client. */
|
||||
DataFlow::SourceNode getAReceivedItem() { result = getReceivedItem(_) }
|
||||
override string getChannel() { this.getArgument(0).mayHaveStringValue(result) }
|
||||
}
|
||||
|
||||
/** Gets the acknowledgment callback, if any. */
|
||||
DataFlow::SourceNode getAck() {
|
||||
result = getListener().getLastParameter() and
|
||||
exists(result.getAnInvocation())
|
||||
/** An acknowledgment callback when receiving a message. */
|
||||
class ReceiveCallback extends EventDispatch::Range, DataFlow::SourceNode {
|
||||
ReceiveNode rcv;
|
||||
|
||||
ReceiveCallback() {
|
||||
this = rcv.getListener().getLastParameter() and
|
||||
exists(this.getAnInvocation()) and
|
||||
emitter = rcv.getEmitter()
|
||||
}
|
||||
|
||||
/** Gets a client-side node that may be sending the data received here. */
|
||||
SocketIOClient::SendNode getASender() {
|
||||
result.getSocket().getATargetNamespace() = getSocket().getNamespace() and
|
||||
not result.getEventName() != getEventName()
|
||||
override string getChannel() { result = rcv.getChannel() }
|
||||
|
||||
override DataFlow::Node getSentItem(int i) { result = this.getACall().getArgument(i) }
|
||||
|
||||
override SocketIOClient::SendCallback getAReceiver() {
|
||||
result.getSendNode().getAReceiver() = rcv
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +286,7 @@ module SocketIO {
|
||||
* A data flow node representing data received from a client, viewed as remote user input.
|
||||
*/
|
||||
private class ReceivedItemAsRemoteFlow extends RemoteFlowSource {
|
||||
ReceivedItemAsRemoteFlow() { this = any(ReceiveNode rercv).getAReceivedItem() }
|
||||
ReceivedItemAsRemoteFlow() { this = any(ReceiveNode rercv).getReceivedItem(_) }
|
||||
|
||||
override string getSourceType() { result = "socket.io client data" }
|
||||
|
||||
@@ -252,15 +296,12 @@ module SocketIO {
|
||||
/**
|
||||
* A data flow node representing an API call that sends data to a client.
|
||||
*/
|
||||
class SendNode extends DataFlow::MethodCallNode {
|
||||
DataFlow::SourceNode base;
|
||||
class SendNode extends DataFlow::MethodCallNode, EventDispatch::Range {
|
||||
override SocketIOObject emitter;
|
||||
int firstDataIndex;
|
||||
|
||||
SendNode() {
|
||||
exists(string m |
|
||||
(base instanceof ServerNode or base instanceof NamespaceNode or base instanceof SocketNode) and
|
||||
this = base.getAMethodCall(m)
|
||||
|
|
||||
exists(string m | this = emitter.ref().getAMethodCall(m) |
|
||||
// a call to `emit`
|
||||
m = "emit" and
|
||||
firstDataIndex = 1
|
||||
@@ -276,92 +317,65 @@ module SocketIO {
|
||||
*
|
||||
* This predicate is not defined for broadcasting sends.
|
||||
*/
|
||||
SocketNode getSocket() { result = base }
|
||||
SocketObject getSocket() { result = emitter }
|
||||
|
||||
/**
|
||||
* Gets the namespace to which data is sent.
|
||||
*/
|
||||
NamespaceObject getNamespace() {
|
||||
result = base.(ServerNode).getServer().getDefaultNamespace() or
|
||||
result = base.(NamespaceNode).getNamespace() or
|
||||
result = base.(SocketNode).getNamespace()
|
||||
result = emitter.(ServerObject).getDefaultNamespace() or
|
||||
result = emitter.(NamespaceBase).getNamespace() or
|
||||
result = emitter.(SocketObject).getNamespace()
|
||||
}
|
||||
|
||||
/** Gets the event name associated with the data, if it can be determined. */
|
||||
string getEventName() {
|
||||
override string getChannel() {
|
||||
if firstDataIndex = 1 then getArgument(0).mayHaveStringValue(result) else result = "message"
|
||||
}
|
||||
|
||||
/** Gets the `i`th argument through which data is sent to the client. */
|
||||
DataFlow::Node getSentItem(int i) {
|
||||
override DataFlow::Node getSentItem(int i) {
|
||||
result = getArgument(i + firstDataIndex) and
|
||||
i >= 0 and
|
||||
(
|
||||
// exclude last argument if it looks like a callback
|
||||
result != getLastArgument() or not exists(getAck())
|
||||
result != getLastArgument() or not exists(SendCallback c | c.getSendNode() = this)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node representing data sent to the client. */
|
||||
DataFlow::Node getASentItem() { result = getSentItem(_) }
|
||||
|
||||
/** Gets the acknowledgment callback, if any. */
|
||||
DataFlow::FunctionNode getAck() {
|
||||
// acknowledgments are only available when sending through a socket
|
||||
exists(getSocket()) and
|
||||
result = getLastArgument().getALocalSource()
|
||||
}
|
||||
|
||||
/** Gets a client-side node that may be receiving the data sent here. */
|
||||
SocketIOClient::ReceiveNode getAReceiver() {
|
||||
result.getSocket().getATargetNamespace() = getNamespace() and
|
||||
not result.getEventName() != getEventName()
|
||||
override SocketIOClient::ReceiveNode getAReceiver() {
|
||||
result.getSocket().getATargetNamespace() = getNamespace()
|
||||
}
|
||||
}
|
||||
|
||||
/** A socket.io server, identified by its creation site. */
|
||||
private newtype TServer = MkServer(DataFlow::SourceNode nd) { nd = newServer() }
|
||||
|
||||
/** A socket.io namespace, identified by its server and its path. */
|
||||
private newtype TNamespace =
|
||||
MkNamespace(ServerObject srv, string path) {
|
||||
path = "/"
|
||||
or
|
||||
exists(ServerNode nd | nd.getServer() = srv |
|
||||
nd.getAMethodCall("of").getArgument(0).mayHaveStringValue(path)
|
||||
)
|
||||
srv.ref().getAMethodCall("of").getArgument(0).mayHaveStringValue(path)
|
||||
}
|
||||
|
||||
/** A socket.io server. */
|
||||
class ServerObject extends TServer {
|
||||
DataFlow::SourceNode origin;
|
||||
/** An acknowledgment callback from sending message. */
|
||||
class SendCallback extends EventRegistration::Range, DataFlow::FunctionNode {
|
||||
SendNode send;
|
||||
|
||||
ServerObject() { this = MkServer(origin) }
|
||||
SendCallback() {
|
||||
// acknowledgments are only available when sending through a socket
|
||||
exists(send.getSocket()) and
|
||||
this = send.getLastArgument().getALocalSource() and
|
||||
emitter = send.getEmitter()
|
||||
}
|
||||
|
||||
/** Gets the data flow node where this server is created. */
|
||||
DataFlow::SourceNode getOrigin() { result = origin }
|
||||
override string getChannel() { result = send.getChannel() }
|
||||
|
||||
/** Gets the default namespace of this server. */
|
||||
NamespaceObject getDefaultNamespace() { result = MkNamespace(this, "/") }
|
||||
|
||||
/** Gets the namespace with the given path of this server. */
|
||||
NamespaceObject getNamespace(string path) { result = MkNamespace(this, path) }
|
||||
override DataFlow::Node getReceivedItem(int i) { result = this.getParameter(i) }
|
||||
|
||||
/**
|
||||
* Holds if this server is created at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* Get the send node where this callback was registered.
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
origin.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this server. */
|
||||
string toString() { result = "socket.io server" }
|
||||
SendNode getSendNode() { result = send }
|
||||
}
|
||||
|
||||
/** A socket.io namespace. */
|
||||
@@ -387,42 +401,34 @@ module SocketIO {
|
||||
* (npm package `socket.io-client`).
|
||||
*/
|
||||
module SocketIOClient {
|
||||
/**
|
||||
* Gets a data flow node that may refer to the socket.io socket created at `invk`.
|
||||
*/
|
||||
private DataFlow::SourceNode socket(DataFlow::InvokeNode invk, DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
exists(DataFlow::SourceNode io |
|
||||
io = DataFlow::globalVarRef("io") or
|
||||
io = DataFlow::globalVarRef("io").getAPropertyRead("connect") or
|
||||
io = DataFlow::moduleImport("io") or
|
||||
io = DataFlow::moduleMember("io", "connect") or
|
||||
io = DataFlow::moduleImport("socket.io-client") or
|
||||
io = DataFlow::moduleMember("socket.io-client", "connect")
|
||||
|
|
||||
invk = io.getAnInvocation() and
|
||||
result = invk
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = socket(invk, t2).track(t2, t))
|
||||
}
|
||||
/** A socket object. */
|
||||
class SocketObject extends DataFlow::InvokeNode, EventEmitter::Range {
|
||||
SocketObject() {
|
||||
exists(DataFlow::SourceNode io |
|
||||
io = DataFlow::globalVarRef("io") or
|
||||
io = DataFlow::globalVarRef("io").getAPropertyRead("connect") or
|
||||
io = DataFlow::moduleImport("io") or
|
||||
io = DataFlow::moduleMember("io", "connect") or
|
||||
io = DataFlow::moduleImport("socket.io-client") or
|
||||
io = DataFlow::moduleMember("socket.io-client", "connect")
|
||||
|
|
||||
this = io.getAnInvocation()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NPM package that contains `nd`.
|
||||
*/
|
||||
private NPMPackage getPackage(DataFlow::SourceNode nd) { result.getAFile() = nd.getFile() }
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
t.start() and result = this
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** A data flow node that may produce a socket object. */
|
||||
class SocketNode extends DataFlow::SourceNode {
|
||||
DataFlow::InvokeNode invk;
|
||||
|
||||
SocketNode() { this = socket(invk, DataFlow::TypeTracker::end()) }
|
||||
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets the path of the namespace this socket belongs to, if it can be determined. */
|
||||
string getNamespacePath() {
|
||||
// the path name of the specified URL
|
||||
exists(string url, string pathRegex |
|
||||
invk.getArgument(0).mayHaveStringValue(url) and
|
||||
this.getArgument(0).mayHaveStringValue(url) and
|
||||
pathRegex = "(?<!/)/(?!/)[^?#]*"
|
||||
|
|
||||
result = url.regexpFind(pathRegex, 0, _)
|
||||
@@ -433,7 +439,7 @@ module SocketIOClient {
|
||||
)
|
||||
or
|
||||
// if no URL is specified, the path defaults to "/"
|
||||
not exists(invk.getArgument(0)) and
|
||||
not exists(this.getArgument(0)) and
|
||||
result = "/"
|
||||
}
|
||||
|
||||
@@ -446,7 +452,7 @@ module SocketIOClient {
|
||||
* it can be determined.
|
||||
*/
|
||||
SocketIO::ServerObject getATargetServer() {
|
||||
getPackage(result.getOrigin()) = getPackage(this) and
|
||||
getPackage(result) = getPackage(this) and
|
||||
(
|
||||
not exists(getNamespacePath()) or
|
||||
exists(result.getNamespace(getNamespacePath()))
|
||||
@@ -463,22 +469,51 @@ module SocketIOClient {
|
||||
}
|
||||
|
||||
/** Gets a server-side socket this client-side socket may be communicating with. */
|
||||
SocketIO::SocketNode getATargetSocket() { result.getNamespace() = getATargetNamespace() }
|
||||
SocketIO::SocketObject getATargetSocket() { result.getNamespace() = getATargetNamespace() }
|
||||
}
|
||||
|
||||
/** A data flow node that may produce a socket object. */
|
||||
class SocketNode extends DataFlow::SourceNode {
|
||||
SocketObject socket;
|
||||
|
||||
SocketNode() { this = socket.ref() }
|
||||
|
||||
/** Gets the path of the namespace this socket belongs to, if it can be determined. */
|
||||
string getNamespacePath() { result = socket.getNamespacePath() }
|
||||
|
||||
/**
|
||||
* Gets a server this socket may be communicating with.
|
||||
*
|
||||
* To avoid matching sockets with unrelated servers, we restrict the search to
|
||||
* servers defined in the same npm package. Furthermore, the server is required
|
||||
* to have a namespace with the same path as the namespace of this socket, if
|
||||
* it can be determined.
|
||||
*/
|
||||
SocketIO::ServerObject getATargetServer() { result = socket.getATargetServer() }
|
||||
|
||||
/** Gets a namespace this socket may be communicating with. */
|
||||
SocketIO::NamespaceObject getATargetNamespace() { result = socket.getATargetNamespace() }
|
||||
|
||||
/** Gets a server-side socket this client-side socket may be communicating with. */
|
||||
SocketIO::SocketNode getATargetSocket() { result.getNamespace() = socket.getATargetNamespace() }
|
||||
}
|
||||
|
||||
/** Gets the NPM package that contains `nd`. */
|
||||
private NPMPackage getPackage(DataFlow::SourceNode nd) { result.getAFile() = nd.getFile() }
|
||||
|
||||
/**
|
||||
* A data flow node representing an API call that receives data from the server.
|
||||
*/
|
||||
class ReceiveNode extends DataFlow::MethodCallNode {
|
||||
SocketNode socket;
|
||||
class ReceiveNode extends DataFlow::MethodCallNode, EventRegistration::Range {
|
||||
override SocketObject emitter;
|
||||
|
||||
ReceiveNode() { this = socket.getAMethodCall(EventEmitter::on()) }
|
||||
ReceiveNode() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
|
||||
|
||||
/** Gets the socket through which data is received. */
|
||||
SocketNode getSocket() { result = socket }
|
||||
SocketObject getSocket() { result = emitter }
|
||||
|
||||
/** Gets the event name associated with the data, if it can be determined. */
|
||||
string getEventName() { getArgument(0).mayHaveStringValue(result) }
|
||||
override string getChannel() { getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
private DataFlow::SourceNode getListener(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
@@ -488,43 +523,49 @@ module SocketIOClient {
|
||||
}
|
||||
|
||||
/** Gets the callback that handles data received from the server. */
|
||||
private DataFlow::FunctionNode getListener() {
|
||||
result = getListener(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
DataFlow::FunctionNode getListener() { result = getListener(DataFlow::TypeBackTracker::end()) }
|
||||
|
||||
/** Gets the `i`th parameter through which data is received from the server. */
|
||||
DataFlow::SourceNode getReceivedItem(int i) {
|
||||
override DataFlow::SourceNode getReceivedItem(int i) {
|
||||
exists(DataFlow::FunctionNode cb | cb = getListener() and result = cb.getParameter(i) |
|
||||
// exclude the last parameter if it looks like a callback
|
||||
result != cb.getLastParameter() or not exists(result.getAnInvocation())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a data flow node representing data received from the server. */
|
||||
DataFlow::SourceNode getAReceivedItem() { result = getReceivedItem(_) }
|
||||
/** An acknowledgment callback from a receive node. */
|
||||
class RecieveCallback extends EventDispatch::Range, DataFlow::SourceNode {
|
||||
override SocketObject emitter;
|
||||
ReceiveNode rcv;
|
||||
|
||||
/** Gets the acknowledgment callback, if any. */
|
||||
DataFlow::SourceNode getAck() {
|
||||
result = getListener().getLastParameter() and
|
||||
exists(result.getAnInvocation())
|
||||
RecieveCallback() {
|
||||
this = rcv.getListener().getLastParameter() and
|
||||
exists(this.getAnInvocation()) and
|
||||
emitter = rcv.getEmitter()
|
||||
}
|
||||
|
||||
/** Gets a server-side node that may be sending the data received here. */
|
||||
SocketIO::SendNode getASender() {
|
||||
result.getNamespace() = getSocket().getATargetNamespace() and
|
||||
not result.getEventName() != getEventName()
|
||||
}
|
||||
override string getChannel() { result = rcv.getChannel() }
|
||||
|
||||
override DataFlow::Node getSentItem(int i) { result = this.getACall().getArgument(i) }
|
||||
|
||||
override SocketIO::SendCallback getAReceiver() { result.getSendNode().getAReceiver() = rcv }
|
||||
|
||||
/**
|
||||
* Get the receive node where this callback was registered.
|
||||
*/
|
||||
ReceiveNode getReceiveNode() { result = rcv }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node representing an API call that sends data to the server.
|
||||
*/
|
||||
class SendNode extends DataFlow::MethodCallNode {
|
||||
SocketNode base;
|
||||
class SendNode extends DataFlow::MethodCallNode, EventDispatch::Range {
|
||||
override SocketObject emitter;
|
||||
int firstDataIndex;
|
||||
|
||||
SendNode() {
|
||||
exists(string m | this = base.getAMethodCall(m) |
|
||||
exists(string m | this = emitter.ref().getAMethodCall(m) |
|
||||
// a call to `emit`
|
||||
m = "emit" and
|
||||
firstDataIndex = 1
|
||||
@@ -538,73 +579,50 @@ module SocketIOClient {
|
||||
/**
|
||||
* Gets the socket through which data is sent to the server.
|
||||
*/
|
||||
SocketNode getSocket() { result = base }
|
||||
SocketObject getSocket() { result = emitter }
|
||||
|
||||
/**
|
||||
* Gets the path of the namespace to which data is sent, if it can be determined.
|
||||
*/
|
||||
string getNamespacePath() { result = base.getNamespacePath() }
|
||||
string getNamespacePath() { result = emitter.getNamespacePath() }
|
||||
|
||||
/** Gets the event name associated with the data, if it can be determined. */
|
||||
string getEventName() {
|
||||
override string getChannel() {
|
||||
if firstDataIndex = 1 then getArgument(0).mayHaveStringValue(result) else result = "message"
|
||||
}
|
||||
|
||||
/** Gets the `i`th argument through which data is sent to the server. */
|
||||
DataFlow::Node getSentItem(int i) {
|
||||
override DataFlow::Node getSentItem(int i) {
|
||||
result = getArgument(i + firstDataIndex) and
|
||||
i >= 0 and
|
||||
(
|
||||
// exclude last argument if it looks like a callback
|
||||
result != getLastArgument() or not exists(getAck())
|
||||
result != getLastArgument() or not exists(SendCallback c | c.getSendNode() = this)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node representing data sent to the server. */
|
||||
DataFlow::Node getASentItem() { result = getSentItem(_) }
|
||||
|
||||
/** Gets the acknowledgment callback, if any. */
|
||||
DataFlow::FunctionNode getAck() { result = getLastArgument().getALocalSource() }
|
||||
|
||||
/** Gets a server-side node that may be receiving the data sent here. */
|
||||
SocketIO::ReceiveNode getAReceiver() {
|
||||
result.getSocket().getNamespace() = getSocket().getATargetNamespace() and
|
||||
not result.getEventName() != getEventName()
|
||||
override SocketIO::ReceiveNode getAReceiver() {
|
||||
result.getSocket().getNamespace() = getSocket().getATargetNamespace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow step through socket.io sockets. */
|
||||
private class SocketIoStep extends DataFlow::AdditionalFlowStep {
|
||||
DataFlow::Node pred;
|
||||
DataFlow::Node succ;
|
||||
/** An acknowledgment callback from sending message. */
|
||||
class SendCallback extends EventRegistration::Range, DataFlow::FunctionNode {
|
||||
SendNode send;
|
||||
|
||||
SocketIoStep() {
|
||||
(
|
||||
exists(SocketIO::SendNode send, SocketIOClient::ReceiveNode recv, int i |
|
||||
recv = send.getAReceiver()
|
||||
|
|
||||
pred = send.getSentItem(i) and
|
||||
succ = recv.getReceivedItem(i)
|
||||
or
|
||||
pred = recv.getAck().getACall().getArgument(i) and
|
||||
succ = send.getAck().getParameter(i)
|
||||
)
|
||||
or
|
||||
exists(SocketIOClient::SendNode send, SocketIO::ReceiveNode recv, int i |
|
||||
recv = send.getAReceiver()
|
||||
|
|
||||
pred = send.getSentItem(i) and
|
||||
succ = recv.getReceivedItem(i)
|
||||
or
|
||||
pred = recv.getAck().getACall().getArgument(i) and
|
||||
succ = send.getAck().getParameter(i)
|
||||
)
|
||||
) and
|
||||
this = pred
|
||||
}
|
||||
SendCallback() {
|
||||
this = send.getLastArgument().getALocalSource() and
|
||||
emitter = send.getEmitter()
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node predNode, DataFlow::Node succNode) {
|
||||
predNode = pred and succNode = succ
|
||||
override string getChannel() { result = send.getChannel() }
|
||||
|
||||
override DataFlow::Node getReceivedItem(int i) { result = this.getParameter(i) }
|
||||
|
||||
/**
|
||||
* Get the SendNode where this callback was registered.
|
||||
*/
|
||||
SendNode getSendNode() { result = send }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ import javascript
|
||||
query predicate test_ClientReceiveNode(
|
||||
SocketIOClient::ReceiveNode rn, SocketIOClient::SocketNode res
|
||||
) {
|
||||
res = rn.getSocket()
|
||||
res = rn.getSocket().ref()
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ import javascript
|
||||
query predicate test_ClientReceiveNode_getASender(
|
||||
SocketIOClient::ReceiveNode rn, SocketIO::SendNode res
|
||||
) {
|
||||
res = rn.getASender()
|
||||
res.getAReceiver() = rn
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ClientReceiveNode_getAck(
|
||||
SocketIOClient::ReceiveNode rn, DataFlow::SourceNode res
|
||||
SocketIOClient::ReceiveNode rn, SocketIOClient::RecieveCallback res
|
||||
) {
|
||||
res = rn.getAck()
|
||||
res.getReceiveNode() = rn
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ClientReceiveNode_getEventName(SocketIOClient::ReceiveNode rn, string res) {
|
||||
res = rn.getEventName()
|
||||
res = rn.getChannel()
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ import javascript
|
||||
query predicate test_ClientSendNode(
|
||||
SocketIOClient::SendNode sn, SocketIOClient::SocketNode res0, string res1
|
||||
) {
|
||||
res0 = sn.getSocket() and res1 = sn.getNamespacePath()
|
||||
res0 = sn.getSocket().ref() and res1 = sn.getNamespacePath()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ClientSendNode_getAck(SocketIOClient::SendNode sn, DataFlow::FunctionNode res) {
|
||||
res = sn.getAck()
|
||||
query predicate test_ClientSendNode_getAck(SocketIOClient::SendNode sn, SocketIOClient::SendCallback res) {
|
||||
res.getSendNode() = sn
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ClientSendNode_getEventName(SocketIOClient::SendNode sn, string res) {
|
||||
res = sn.getEventName()
|
||||
res = sn.getChannel()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ReceiveNode_getASender(SocketIO::ReceiveNode rn, SocketIOClient::SendNode res) {
|
||||
res = rn.getASender()
|
||||
res.getAReceiver() = rn
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ReceiveNode_getEventName(SocketIO::ReceiveNode rn, string res) {
|
||||
res = rn.getEventName()
|
||||
res = rn.getChannel()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_SendNode_getAck(SocketIO::SendNode sn, DataFlow::FunctionNode res) {
|
||||
res = sn.getAck()
|
||||
query predicate test_SendNode_getAck(SocketIO::SendNode sn, SocketIO::SendCallback res) {
|
||||
res.getSendNode() = sn
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ import javascript
|
||||
query predicate test_ServerObject(
|
||||
SocketIO::ServerObject srv, DataFlow::SourceNode res0, SocketIO::NamespaceObject res1
|
||||
) {
|
||||
res0 = srv.getOrigin() and res1 = srv.getDefaultNamespace()
|
||||
res0 = srv and res1 = srv.getDefaultNamespace()
|
||||
}
|
||||
|
||||
@@ -31,10 +31,12 @@ test_NamespaceNode
|
||||
| tst.js:68:1:68:32 | ns.on(' ... => {}) | socket.io namespace with path '/' |
|
||||
| tst.js:69:1:73:2 | ns.on(' ... {});\\n}) | socket.io namespace with path '/' |
|
||||
test_ClientReceiveNode_getASender
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:30:1:30:28 | ns.emit ... event') |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:31:1:31:20 | ns.send('a message') |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:39:1:39:31 | io.emit ... ssage') |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:40:1:40:20 | io.send('a message') |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:41:1:41:21 | io.writ ... ssage') |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:51:3:51:22 | socket.emit('event') |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:54:3:54:43 | socket. ... => {}) |
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | tst.js:55:3:55:27 | socket. ... ssage') |
|
||||
| client2.js:8:1:8:33 | sock.on ... => {}) | tst.js:30:1:30:28 | ns.emit ... event') |
|
||||
@@ -46,7 +48,13 @@ test_ClientReceiveNode_getASender
|
||||
| client2.js:8:1:8:33 | sock.on ... => {}) | tst.js:54:3:54:43 | socket. ... => {}) |
|
||||
| client2.js:8:1:8:33 | sock.on ... => {}) | tst.js:55:3:55:27 | socket. ... ssage') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:30:1:30:28 | ns.emit ... event') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:31:1:31:20 | ns.send('a message') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:39:1:39:31 | io.emit ... ssage') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:40:1:40:20 | io.send('a message') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:41:1:41:21 | io.writ ... ssage') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:51:3:51:22 | socket.emit('event') |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:54:3:54:43 | socket. ... => {}) |
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | tst.js:55:3:55:27 | socket. ... ssage') |
|
||||
| client2.js:18:1:18:41 | sock2.o ... dler')) | tst.js:32:1:32:22 | ns2.wri ... ssage') |
|
||||
test_ReceiveNode
|
||||
| tst.js:70:3:70:35 | socket. ... => {}) | tst.js:69:22:69:27 | socket |
|
||||
@@ -127,7 +135,9 @@ test_ClientSocketNode
|
||||
| client4.js:3:1:3:4 | io() | / |
|
||||
| client4.js:4:1:4:23 | io.conn ... sages") | /messages |
|
||||
test_ReceiveNode_getASender
|
||||
| tst.js:70:3:70:35 | socket. ... => {}) | client2.js:14:1:14:32 | sock.em ... there") |
|
||||
| tst.js:70:3:70:35 | socket. ... => {}) | client2.js:16:1:16:36 | sock.wr ... => {}) |
|
||||
| tst.js:71:3:71:46 | socket. ... => {}) | client2.js:14:1:14:32 | sock.em ... there") |
|
||||
| tst.js:71:3:71:46 | socket. ... => {}) | client2.js:16:1:16:36 | sock.wr ... => {}) |
|
||||
| tst.js:72:3:72:43 | socket. ... => {}) | client2.js:14:1:14:32 | sock.em ... there") |
|
||||
| tst.js:72:3:72:43 | socket. ... => {}) | client2.js:16:1:16:36 | sock.wr ... => {}) |
|
||||
@@ -140,20 +150,24 @@ test_SendNode_getSocket
|
||||
| tst.js:54:3:54:43 | socket. ... => {}) | tst.js:50:19:50:24 | socket |
|
||||
| tst.js:55:3:55:27 | socket. ... ssage') | tst.js:50:19:50:24 | socket |
|
||||
test_ServerNode
|
||||
| tst.js:1:12:1:33 | require ... .io')() | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:4:13:4:24 | new Server() | tst.js:4:13:4:24 | socket.io server |
|
||||
| tst.js:6:13:6:27 | Server.listen() | tst.js:6:13:6:27 | socket.io server |
|
||||
| tst.js:9:1:9:21 | io.serv ... (false) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:10:1:10:21 | io.set( ... s', []) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:11:1:11:21 | io.path ... npath') | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:12:1:12:15 | io.adapter(foo) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:13:1:13:14 | io.origins([]) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:14:1:14:15 | io.listen(http) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:15:1:15:15 | io.attach(http) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:16:1:16:15 | io.bind(engine) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:17:1:17:23 | io.onco ... socket) | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:79:1:79:10 | obj.server | tst.js:1:12:1:33 | socket.io server |
|
||||
| tst.js:1:12:1:33 | require ... .io')() | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:4:13:4:24 | new Server() | tst.js:4:13:4:24 | new Server() |
|
||||
| tst.js:6:13:6:27 | Server.listen() | tst.js:6:13:6:27 | Server.listen() |
|
||||
| tst.js:9:1:9:21 | io.serv ... (false) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:10:1:10:21 | io.set( ... s', []) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:11:1:11:21 | io.path ... npath') | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:12:1:12:15 | io.adapter(foo) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:13:1:13:14 | io.origins([]) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:14:1:14:15 | io.listen(http) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:15:1:15:15 | io.attach(http) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:16:1:16:15 | io.bind(engine) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:17:1:17:23 | io.onco ... socket) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:50:1:66:2 | io.on(' ... cal;\\n}) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:67:1:67:35 | io.on(' ... => {}) | tst.js:1:12:1:33 | require ... .io')() |
|
||||
| tst.js:79:1:79:10 | obj.server | tst.js:1:12:1:33 | require ... .io')() |
|
||||
test_ClientSendNode_getAReceiver
|
||||
| client2.js:14:1:14:32 | sock.em ... there") | tst.js:70:3:70:35 | socket. ... => {}) |
|
||||
| client2.js:14:1:14:32 | sock.em ... there") | tst.js:71:3:71:46 | socket. ... => {}) |
|
||||
| client2.js:14:1:14:32 | sock.em ... there") | tst.js:72:3:72:43 | socket. ... => {}) |
|
||||
| client2.js:16:1:16:36 | sock.wr ... => {}) | tst.js:70:3:70:35 | socket. ... => {}) |
|
||||
| client2.js:16:1:16:36 | sock.wr ... => {}) | tst.js:71:3:71:46 | socket. ... => {}) |
|
||||
@@ -167,10 +181,10 @@ test_ClientReceiveNode_getReceivedItem
|
||||
| client2.js:10:1:12:2 | sock.on ... d");\\n}) | 0 | client2.js:10:19:10:19 | x |
|
||||
| client2.js:18:1:18:41 | sock2.o ... dler')) | 0 | handler.js:1:19:1:19 | x |
|
||||
test_NamespaceObject
|
||||
| socket.io namespace with path '/' | tst.js:1:12:1:33 | socket.io server | / |
|
||||
| socket.io namespace with path '/' | tst.js:4:13:4:24 | socket.io server | / |
|
||||
| socket.io namespace with path '/' | tst.js:6:13:6:27 | socket.io server | / |
|
||||
| socket.io namespace with path '/foo/bar' | tst.js:1:12:1:33 | socket.io server | /foo/bar |
|
||||
| socket.io namespace with path '/' | tst.js:1:12:1:33 | require ... .io')() | / |
|
||||
| socket.io namespace with path '/' | tst.js:4:13:4:24 | new Server() | / |
|
||||
| socket.io namespace with path '/' | tst.js:6:13:6:27 | Server.listen() | / |
|
||||
| socket.io namespace with path '/foo/bar' | tst.js:1:12:1:33 | require ... .io')() | /foo/bar |
|
||||
test_ClientReceiveNode
|
||||
| client2.js:4:1:6:2 | sock.on ... y);\\n}) | client2.js:1:12:1:56 | require ... lhost") |
|
||||
| client2.js:8:1:8:33 | sock.on ... => {}) | client2.js:1:12:1:56 | require ... lhost") |
|
||||
@@ -192,24 +206,32 @@ test_SendNode
|
||||
| tst.js:54:3:54:43 | socket. ... => {}) | socket.io namespace with path '/' |
|
||||
| tst.js:55:3:55:27 | socket. ... ssage') | socket.io namespace with path '/' |
|
||||
test_SendNode_getAReceiver
|
||||
| tst.js:30:1:30:28 | ns.emit ... event') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:30:1:30:28 | ns.emit ... event') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:30:1:30:28 | ns.emit ... event') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:31:1:31:20 | ns.send('a message') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:31:1:31:20 | ns.send('a message') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:31:1:31:20 | ns.send('a message') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:32:1:32:22 | ns2.wri ... ssage') | client2.js:18:1:18:41 | sock2.o ... dler')) |
|
||||
| tst.js:39:1:39:31 | io.emit ... ssage') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:39:1:39:31 | io.emit ... ssage') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:39:1:39:31 | io.emit ... ssage') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:40:1:40:20 | io.send('a message') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:40:1:40:20 | io.send('a message') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:40:1:40:20 | io.send('a message') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:41:1:41:21 | io.writ ... ssage') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:41:1:41:21 | io.writ ... ssage') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:41:1:41:21 | io.writ ... ssage') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:51:3:51:22 | socket.emit('event') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:51:3:51:22 | socket.emit('event') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:51:3:51:22 | socket.emit('event') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:54:3:54:43 | socket. ... => {}) | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:54:3:54:43 | socket. ... => {}) | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:54:3:54:43 | socket. ... => {}) | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
| tst.js:55:3:55:27 | socket. ... ssage') | client2.js:4:1:6:2 | sock.on ... y);\\n}) |
|
||||
| tst.js:55:3:55:27 | socket. ... ssage') | client2.js:8:1:8:33 | sock.on ... => {}) |
|
||||
| tst.js:55:3:55:27 | socket. ... ssage') | client2.js:10:1:12:2 | sock.on ... d");\\n}) |
|
||||
test_ServerObject
|
||||
| tst.js:1:12:1:33 | socket.io server | tst.js:1:12:1:33 | require ... .io')() | socket.io namespace with path '/' |
|
||||
| tst.js:4:13:4:24 | socket.io server | tst.js:4:13:4:24 | new Server() | socket.io namespace with path '/' |
|
||||
| tst.js:6:13:6:27 | socket.io server | tst.js:6:13:6:27 | Server.listen() | socket.io namespace with path '/' |
|
||||
| tst.js:1:12:1:33 | require ... .io')() | tst.js:1:12:1:33 | require ... .io')() | socket.io namespace with path '/' |
|
||||
| tst.js:4:13:4:24 | new Server() | tst.js:4:13:4:24 | new Server() | socket.io namespace with path '/' |
|
||||
| tst.js:6:13:6:27 | Server.listen() | tst.js:6:13:6:27 | Server.listen() | socket.io namespace with path '/' |
|
||||
|
||||
Reference in New Issue
Block a user