11/**
2- * Provides classes for working with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [ws](https://github.com/websockets/ws).
2+ * Provides classes for working with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), [ws](https://github.com/websockets/ws), and [SockJS](http://sockjs.org ).
33 *
44 * The model is based on the EventEmitter model, and there is therefore a
55 * data-flow step from where a WebSocket event is sent to where the message
@@ -18,26 +18,64 @@ import javascript
1818 */
1919private string channelName ( ) { result = "message" }
2020
21+ /**
22+ * The names of the libraries modelled in this file.
23+ */
24+ private module LibraryNames {
25+ string sockjs ( ) { result = "SockJS" }
26+
27+ string websocket ( ) { result = "WebSocket" }
28+
29+ string ws ( ) { result = "ws" }
30+
31+ class LibraryName extends string {
32+ LibraryName ( ) { this = sockjs ( ) or this = websocket ( ) or this = ws ( ) }
33+ }
34+ }
35+
36+ /**
37+ * Holds if the websocket library named `client` can send a message to the library named `server`.
38+ * Both `client` and `server` are library names defined in `LibraryNames`.
39+ */
40+ private predicate areLibrariesCompatible (
41+ LibraryNames:: LibraryName client , LibraryNames:: LibraryName server
42+ ) {
43+ // sockjs is a WebSocket emulating library, but not actually an implementation of WebSockets.
44+ client = LibraryNames:: sockjs ( ) and server = LibraryNames:: sockjs ( )
45+ or
46+ server = LibraryNames:: ws ( ) and
47+ ( client = LibraryNames:: ws ( ) or client = LibraryNames:: websocket ( ) )
48+ }
49+
2150/**
2251 * Provides classes that model WebSockets clients.
2352 */
2453module ClientWebSocket {
54+ private import LibraryNames
55+
2556 /**
2657 * A class that can be used to instantiate a WebSocket instance.
2758 */
2859 class SocketClass extends DataFlow:: SourceNode {
29- boolean isNode ;
60+ LibraryName library ; // the name of the WebSocket library. Can be one of the libraries defined in `LibraryNames`.
3061
3162 SocketClass ( ) {
32- this = DataFlow:: globalVarRef ( "WebSocket" ) and isNode = false
63+ this = DataFlow:: globalVarRef ( "WebSocket" ) and library = websocket ( )
64+ or
65+ this = DataFlow:: moduleImport ( "ws" ) and library = ws ( )
3366 or
34- this = DataFlow:: moduleImport ( "ws" ) and isNode = true
67+ // the sockjs-client library:https://www.npmjs.com/package/sockjs-client
68+ library = sockjs ( ) and
69+ (
70+ this = DataFlow:: moduleImport ( "sockjs-client" ) or
71+ this = DataFlow:: globalVarRef ( "SockJS" )
72+ )
3573 }
3674
3775 /**
38- * Holds if this class is an import of the "ws" module .
76+ * Gets the WebSocket library name .
3977 */
40- predicate isNode ( ) { isNode = true }
78+ LibraryName getLibrary ( ) { result = library }
4179 }
4280
4381 /**
@@ -49,11 +87,9 @@ module ClientWebSocket {
4987 ClientSocket ( ) { this = socketClass .getAnInstantiation ( ) }
5088
5189 /**
52- * Holds if this ClientSocket is created from the "ws" module.
53- *
54- * The predicate is used to differentiate where the behavior of the "ws" module differs from the native WebSocket in browsers.
90+ * Gets the WebSocket library name.
5591 */
56- predicate isNode ( ) { socketClass .isNode ( ) }
92+ LibraryName getLibrary ( ) { result = socketClass .getLibrary ( ) }
5793 }
5894
5995 /**
@@ -68,7 +104,10 @@ module ClientWebSocket {
68104
69105 override DataFlow:: Node getSentItem ( int i ) { i = 0 and result = this .getArgument ( 0 ) }
70106
71- override ServerWebSocket:: ReceiveNode getAReceiver ( ) { any ( ) }
107+ override ServerWebSocket:: ReceiveNode getAReceiver ( ) {
108+ areLibrariesCompatible ( emitter .getLibrary ( ) ,
109+ result .getEmitter ( ) .( ServerWebSocket:: ServerSocket ) .getLibrary ( ) )
110+ }
72111 }
73112
74113 /**
@@ -116,7 +155,7 @@ module ClientWebSocket {
116155 */
117156 private class WSReceiveNode extends ClientWebSocket:: ReceiveNode {
118157 WSReceiveNode ( ) {
119- emitter .isNode ( ) and
158+ emitter .getLibrary ( ) = ws ( ) and
120159 this = getAMessageHandler ( emitter , EventEmitter:: on ( ) )
121160 }
122161
@@ -128,21 +167,38 @@ module ClientWebSocket {
128167 * Provides classes that model WebSocket servers.
129168 */
130169module ServerWebSocket {
170+ private import LibraryNames
171+
172+ /**
173+ * Gets a server created by a library named `library`.
174+ */
175+ DataFlow:: SourceNode getAServer ( LibraryName library ) {
176+ library = ws ( ) and
177+ result = DataFlow:: moduleImport ( "ws" ) .getAConstructorInvocation ( "Server" )
178+ or
179+ library = sockjs ( ) and
180+ result = DataFlow:: moduleImport ( "sockjs" ) .getAMemberCall ( "createServer" )
181+ }
182+
131183 /**
132184 * A server WebSocket instance.
133185 */
134186 class ServerSocket extends EventEmitter:: Range , DataFlow:: SourceNode {
187+ LibraryName library ;
188+
135189 ServerSocket ( ) {
136190 exists ( DataFlow:: CallNode onCall |
137- onCall =
138- DataFlow:: moduleImport ( "ws" )
139- .getAConstructorInvocation ( "Server" )
140- .getAMemberCall ( EventEmitter:: on ( ) ) and
191+ onCall = getAServer ( library ) .getAMemberCall ( EventEmitter:: on ( ) ) and
141192 onCall .getArgument ( 0 ) .mayHaveStringValue ( "connection" )
142193 |
143194 this = onCall .getCallback ( 1 ) .getParameter ( 0 )
144195 )
145196 }
197+
198+ /**
199+ * Gets the name of the library that created this server socket.
200+ */
201+ LibraryName getLibrary ( ) { result = library }
146202 }
147203
148204 /**
@@ -151,7 +207,13 @@ module ServerWebSocket {
151207 class SendNode extends EventDispatch:: Range , DataFlow:: CallNode {
152208 override ServerSocket emitter ;
153209
154- SendNode ( ) { this = emitter .getAMemberCall ( "send" ) }
210+ SendNode ( ) {
211+ emitter .getLibrary ( ) = ws ( ) and
212+ this = emitter .getAMemberCall ( "send" )
213+ or
214+ emitter .getLibrary ( ) = sockjs ( ) and
215+ this = emitter .getAMemberCall ( "write" )
216+ }
155217
156218 override string getChannel ( ) { result = channelName ( ) }
157219
@@ -160,7 +222,10 @@ module ServerWebSocket {
160222 result = getArgument ( 0 )
161223 }
162224
163- override ClientWebSocket:: ReceiveNode getAReceiver ( ) { any ( ) }
225+ override ClientWebSocket:: ReceiveNode getAReceiver ( ) {
226+ areLibrariesCompatible ( result .getEmitter ( ) .( ClientWebSocket:: ClientSocket ) .getLibrary ( ) ,
227+ emitter .getLibrary ( ) )
228+ }
164229 }
165230
166231 /**
@@ -170,8 +235,14 @@ module ServerWebSocket {
170235 override ServerSocket emitter ;
171236
172237 ReceiveNode ( ) {
173- this = emitter .getAMemberCall ( EventEmitter:: on ( ) ) and
174- this .getArgument ( 0 ) .mayHaveStringValue ( "message" )
238+ exists ( string eventName |
239+ emitter .getLibrary ( ) = ws ( ) and eventName = "message"
240+ or
241+ emitter .getLibrary ( ) = sockjs ( ) and eventName = "data"
242+ |
243+ this = emitter .getAMemberCall ( EventEmitter:: on ( ) ) and
244+ this .getArgument ( 0 ) .mayHaveStringValue ( eventName )
245+ )
175246 }
176247
177248 override string getChannel ( ) { result = channelName ( ) }
0 commit comments