Skip to content

Commit e7fd97e

Browse files
authored
Merge pull request github#3119 from erik-krogh/SockJS
Approved by esbena
2 parents f981ce6 + 4b0bc6b commit e7fd97e

File tree

9 files changed

+150
-91
lines changed

9 files changed

+150
-91
lines changed

change-notes/1.24/analysis-javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
- [request](https://www.npmjs.com/package/request)
4848
- [rimraf](https://www.npmjs.com/package/rimraf)
4949
- [send](https://www.npmjs.com/package/send)
50+
- [SockJS](https://www.npmjs.com/package/sockjs)
51+
- [SockJS-client](https://www.npmjs.com/package/sockjs-client)
5052
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
5153
- [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
5254
- [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)

javascript/ql/src/experimental/SockJS/SockJS.qll

Lines changed: 0 additions & 46 deletions
This file was deleted.

javascript/ql/src/semmle/javascript/frameworks/WebSocket.qll

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
*/
1919
private 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
*/
2453
module 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
*/
130169
module 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() }

javascript/ql/test/library-tests/frameworks/WebSocket/browser.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,31 @@
22
const socket = new WebSocket('ws://localhost:8080');
33

44
socket.addEventListener('open', function (event) {
5-
socket.send('Hi from browser!');
5+
socket.send('Hi from browser!');
66
});
77

88
socket.addEventListener('message', function (event) {
9-
console.log('Message from server ', event.data);
9+
console.log('Message from server ', event.data);
1010
});
11+
12+
socket.onmessage = function (event) {
13+
console.log("Message from server 2", event.data)
14+
};
15+
})();
16+
17+
18+
(function () {
19+
var sock = new SockJS('http://0.0.0.0:9999/echo');
20+
sock.onopen = function () {
21+
sock.send('test');
22+
};
23+
24+
sock.onmessage = function (e) {
25+
console.log('message', e.data);
26+
sock.close();
27+
};
1128

12-
socket.onmessage = function(event) {
13-
console.log("Message from server 2", event.data)
14-
};
15-
})();
29+
sock.addEventListener('message', function (event) {
30+
console.log('Using addEventListener ', event.data);
31+
});
32+
})

javascript/ql/test/library-tests/frameworks/WebSocket/client.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
const ws = new WebSocket('ws://example.org');
55

66
ws.on('open', function open() {
7-
ws.send('Hi from client!');
7+
ws.send('Hi from client!');
88
});
99

1010
ws.on('message', function incoming(data) {
11-
console.log(data);
11+
console.log(data);
1212
});
1313
})();

javascript/ql/test/library-tests/frameworks/WebSocket/server.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
const wss = new WebSocket.Server({ port: 8080 });
55

66
wss.on('connection', function connection(ws) {
7-
ws.on('message', function incoming(message) {
8-
console.log('received: %s', message);
9-
});
7+
ws.on('message', function incoming(message) {
8+
console.log('received: %s', message);
9+
});
1010

11-
ws.send('Hi from server!');
11+
ws.send('Hi from server!');
1212
});
1313
})();

javascript/ql/src/experimental/SockJS/examples/server.js renamed to javascript/ql/test/library-tests/frameworks/WebSocket/sockjs.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ const sockjs = require('sockjs');
55
const app = express();
66
const server = http.createServer(app);
77
const sockjs_echo = sockjs.createServer({});
8-
sockjs_echo.on('connection', function(conn) {
9-
conn.on('data', function(message) {
8+
sockjs_echo.on('connection', function (conn) {
9+
conn.on('data', function (message) {
1010
var data = JSON.parse(message);
1111
conn.write(JSON.stringify(eval(data.test)));
1212
});
1313
});
1414

15-
sockjs_echo.installHandlers(server, {prefix:'/echo'});
15+
sockjs_echo.installHandlers(server, { prefix: '/echo' });
1616
server.listen(9090, '127.0.0.1');
Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
clientSocket
22
| browser.js:2:17:2:52 | new Web ... :8080') |
3+
| browser.js:19:13:19:50 | new Soc ... /echo') |
34
| client.js:4:13:4:45 | new Web ... e.org') |
45
clientSend
5-
| browser.js:5:6:5:36 | socket. ... wser!') |
6-
| client.js:7:5:7:30 | ws.send ... ient!') |
6+
| browser.js:5:3:5:33 | socket. ... wser!') |
7+
| browser.js:21:3:21:19 | sock.send('test') |
8+
| client.js:7:3:7:28 | ws.send ... ient!') |
79
clientReceive
810
| browser.js:8:37:10:2 | functio ... ta);\\n\\t} |
9-
| browser.js:12:21:14:5 | functio ... )\\n } |
11+
| browser.js:12:21:14:2 | functio ... ata)\\n\\t} |
12+
| browser.js:24:19:27:2 | functio ... e();\\n\\t} |
13+
| browser.js:29:35:31:2 | functio ... ta);\\n\\t} |
1014
| client.js:10:19:12:2 | functio ... ta);\\n\\t} |
1115
serverSocket
1216
| server.js:6:43:6:44 | ws |
17+
| sockjs.js:8:40:8:43 | conn |
1318
serverSend
14-
| server.js:11:5:11:30 | ws.send ... rver!') |
19+
| server.js:11:3:11:28 | ws.send ... rver!') |
20+
| sockjs.js:11:9:11:51 | conn.wr ... test))) |
1521
serverReceive
16-
| server.js:7:5:9:6 | ws.on(' ... \\n \\t\\t}) |
22+
| server.js:7:3:9:4 | ws.on(' ... );\\n\\t\\t}) |
23+
| sockjs.js:9:5:12:6 | conn.on ... \\n }) |
1724
taintStep
18-
| browser.js:5:18:5:35 | 'Hi from browser!' | server.js:7:40:7:46 | message |
19-
| client.js:7:13:7:29 | 'Hi from client!' | server.js:7:40:7:46 | message |
20-
| server.js:11:13:11:29 | 'Hi from server!' | browser.js:9:42:9:51 | event.data |
21-
| server.js:11:13:11:29 | 'Hi from server!' | browser.js:13:44:13:53 | event.data |
22-
| server.js:11:13:11:29 | 'Hi from server!' | client.js:10:37:10:40 | data |
25+
| browser.js:5:15:5:32 | 'Hi from browser!' | server.js:7:38:7:44 | message |
26+
| browser.js:21:13:21:18 | 'test' | sockjs.js:9:31:9:37 | message |
27+
| client.js:7:11:7:27 | 'Hi from client!' | server.js:7:38:7:44 | message |
28+
| server.js:11:11:11:27 | 'Hi from server!' | browser.js:9:39:9:48 | event.data |
29+
| server.js:11:11:11:27 | 'Hi from server!' | browser.js:13:40:13:49 | event.data |
30+
| server.js:11:11:11:27 | 'Hi from server!' | client.js:10:37:10:40 | data |
31+
| sockjs.js:11:20:11:50 | JSON.st ... .test)) | browser.js:25:26:25:31 | e.data |
32+
| sockjs.js:11:20:11:50 | JSON.st ... .test)) | browser.js:30:42:30:51 | event.data |
33+
remoteFlow
34+
| server.js:7:38:7:44 | message |
35+
| sockjs.js:9:31:9:37 | message |

javascript/ql/test/library-tests/frameworks/WebSocket/test.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ query ServerWebSocket::ReceiveNode serverReceive() { any() }
1515
query predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) {
1616
any(DataFlow::AdditionalFlowStep s).step(pred, succ)
1717
}
18+
19+
query RemoteFlowSource remoteFlow() { any() }

0 commit comments

Comments
 (0)