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,58 @@ 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+
32+ /**
33+ * Holds if the websocket library named `client` can send a message to the library named `server`.
34+ * Both `client` and `server` are library names defined in `LibraryNames`.
35+ */
36+ private predicate areLibrariesCompatible ( string client , string server ) {
37+ // sockjs is a WebSocket emulating library, but not actually an implementation of WebSockets.
38+ client = LibraryNames:: sockjs ( ) and server = LibraryNames:: sockjs ( )
39+ or
40+ server = LibraryNames:: ws ( ) and
41+ ( client = LibraryNames:: ws ( ) or client = LibraryNames:: websocket ( ) )
42+ }
43+
2144/**
2245 * Provides classes that model WebSockets clients.
2346 */
2447module ClientWebSocket {
48+ private import LibraryNames
49+
2550 /**
2651 * A class that can be used to instantiate a WebSocket instance.
2752 */
2853 class SocketClass extends DataFlow:: SourceNode {
29- boolean isNode ;
54+ string library ; // the name of the WebSocket library. Can be one of the libraries defined in `LibraryNames`.
3055
3156 SocketClass ( ) {
32- this = DataFlow:: globalVarRef ( "WebSocket" ) and isNode = false
57+ this = DataFlow:: globalVarRef ( "WebSocket" ) and library = websocket ( )
58+ or
59+ this = DataFlow:: moduleImport ( "ws" ) and library = ws ( )
3360 or
34- this = DataFlow:: moduleImport ( "ws" ) and isNode = true
61+ // the sockjs-client library:https://www.npmjs.com/package/sockjs-client
62+ library = sockjs ( ) and
63+ (
64+ this = DataFlow:: moduleImport ( "sockjs-client" ) or
65+ this = DataFlow:: globalVarRef ( "SockJS" )
66+ )
3567 }
3668
3769 /**
38- * Holds if this class is an import of the "ws" module .
70+ * Gets the WebSocket library name .
3971 */
40- predicate isNode ( ) { isNode = true }
72+ string getLibrary ( ) { result = library }
4173 }
4274
4375 /**
@@ -49,11 +81,9 @@ module ClientWebSocket {
4981 ClientSocket ( ) { this = socketClass .getAnInstantiation ( ) }
5082
5183 /**
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.
84+ * Gets the WebSocket library name.
5585 */
56- predicate isNode ( ) { socketClass .isNode ( ) }
86+ string getLibrary ( ) { result = socketClass .getLibrary ( ) }
5787 }
5888
5989 /**
@@ -68,7 +98,10 @@ module ClientWebSocket {
6898
6999 override DataFlow:: Node getSentItem ( int i ) { i = 0 and result = this .getArgument ( 0 ) }
70100
71- override ServerWebSocket:: ReceiveNode getAReceiver ( ) { any ( ) }
101+ override ServerWebSocket:: ReceiveNode getAReceiver ( ) {
102+ areLibrariesCompatible ( emitter .getLibrary ( ) ,
103+ result .getEmitter ( ) .( ServerWebSocket:: ServerSocket ) .getLibrary ( ) )
104+ }
72105 }
73106
74107 /**
@@ -116,7 +149,7 @@ module ClientWebSocket {
116149 */
117150 private class WSReceiveNode extends ClientWebSocket:: ReceiveNode {
118151 WSReceiveNode ( ) {
119- emitter .isNode ( ) and
152+ emitter .getLibrary ( ) = ws ( ) and
120153 this = getAMessageHandler ( emitter , EventEmitter:: on ( ) )
121154 }
122155
@@ -128,21 +161,38 @@ module ClientWebSocket {
128161 * Provides classes that model WebSocket servers.
129162 */
130163module ServerWebSocket {
164+ private import LibraryNames
165+
166+ /**
167+ * Gets a server created by a library named `library`.
168+ */
169+ DataFlow:: SourceNode getAServer ( string library ) {
170+ library = ws ( ) and
171+ result = DataFlow:: moduleImport ( "ws" ) .getAConstructorInvocation ( "Server" )
172+ or
173+ library = sockjs ( ) and
174+ result = DataFlow:: moduleImport ( "sockjs" ) .getAMemberCall ( "createServer" )
175+ }
176+
131177 /**
132178 * A server WebSocket instance.
133179 */
134180 class ServerSocket extends EventEmitter:: Range , DataFlow:: SourceNode {
181+ string library ;
182+
135183 ServerSocket ( ) {
136184 exists ( DataFlow:: CallNode onCall |
137- onCall =
138- DataFlow:: moduleImport ( "ws" )
139- .getAConstructorInvocation ( "Server" )
140- .getAMemberCall ( EventEmitter:: on ( ) ) and
185+ onCall = getAServer ( library ) .getAMemberCall ( EventEmitter:: on ( ) ) and
141186 onCall .getArgument ( 0 ) .mayHaveStringValue ( "connection" )
142187 |
143188 this = onCall .getCallback ( 1 ) .getParameter ( 0 )
144189 )
145190 }
191+
192+ /**
193+ * Gets the name of the library that created this server socket.
194+ */
195+ string getLibrary ( ) { result = library }
146196 }
147197
148198 /**
@@ -151,7 +201,13 @@ module ServerWebSocket {
151201 class SendNode extends EventDispatch:: Range , DataFlow:: CallNode {
152202 override ServerSocket emitter ;
153203
154- SendNode ( ) { this = emitter .getAMemberCall ( "send" ) }
204+ SendNode ( ) {
205+ emitter .getLibrary ( ) = ws ( ) and
206+ this = emitter .getAMemberCall ( "send" )
207+ or
208+ emitter .getLibrary ( ) = sockjs ( ) and
209+ this = emitter .getAMemberCall ( "write" )
210+ }
155211
156212 override string getChannel ( ) { result = channelName ( ) }
157213
@@ -160,7 +216,10 @@ module ServerWebSocket {
160216 result = getArgument ( 0 )
161217 }
162218
163- override ClientWebSocket:: ReceiveNode getAReceiver ( ) { any ( ) }
219+ override ClientWebSocket:: ReceiveNode getAReceiver ( ) {
220+ areLibrariesCompatible ( result .getEmitter ( ) .( ClientWebSocket:: ClientSocket ) .getLibrary ( ) ,
221+ emitter .getLibrary ( ) )
222+ }
164223 }
165224
166225 /**
@@ -170,8 +229,14 @@ module ServerWebSocket {
170229 override ServerSocket emitter ;
171230
172231 ReceiveNode ( ) {
173- this = emitter .getAMemberCall ( EventEmitter:: on ( ) ) and
174- this .getArgument ( 0 ) .mayHaveStringValue ( "message" )
232+ exists ( string eventName |
233+ emitter .getLibrary ( ) = ws ( ) and eventName = "message"
234+ or
235+ emitter .getLibrary ( ) = sockjs ( ) and eventName = "data"
236+ |
237+ this = emitter .getAMemberCall ( EventEmitter:: on ( ) ) and
238+ this .getArgument ( 0 ) .mayHaveStringValue ( eventName )
239+ )
175240 }
176241
177242 override string getChannel ( ) { result = channelName ( ) }
0 commit comments