Skip to content

Commit f0491af

Browse files
authored
Merge pull request github#5529 from erik-krogh/socketInput
Approved by esbena
2 parents 0c724a8 + c194598 commit f0491af

File tree

3 files changed

+74
-8
lines changed

3 files changed

+74
-8
lines changed

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

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,73 @@ module ServerWebSocket {
200200
result = DataFlow::moduleImport("sockjs").getAMemberCall("createServer")
201201
}
202202

203+
/**
204+
* Gets a `socket.on("connection", (msg, req) => {})` call.
205+
*/
206+
private DataFlow::CallNode getAConnectionCall(LibraryName library) {
207+
result = getAServer(library).getAMemberCall(EventEmitter::on()) and
208+
result.getArgument(0).mayHaveStringValue("connection")
209+
}
210+
203211
/**
204212
* A server WebSocket instance.
205213
*/
206214
class ServerSocket extends EventEmitter::Range, DataFlow::SourceNode {
207215
LibraryName library;
208216

209-
ServerSocket() {
210-
exists(DataFlow::CallNode onCall |
211-
onCall = getAServer(library).getAMemberCall(EventEmitter::on()) and
212-
onCall.getArgument(0).mayHaveStringValue("connection")
213-
|
214-
this = onCall.getCallback(1).getParameter(0)
215-
)
216-
}
217+
ServerSocket() { this = getAConnectionCall(library).getCallback(1).getParameter(0) }
217218

218219
/**
219220
* Gets the name of the library that created this server socket.
220221
*/
221222
LibraryName getLibrary() { result = library }
222223
}
223224

225+
/**
226+
* A `socket.on("connection", (msg, req) => {})` call seen as a HTTP route handler.
227+
* `req` is a `HTTP::IncomingMessage` instance.
228+
*/
229+
class ConnectionCallAsRouteHandler extends HTTP::RouteHandler, DataFlow::CallNode {
230+
ConnectionCallAsRouteHandler() { this = getAConnectionCall(_) }
231+
232+
override HTTP::HeaderDefinition getAResponseHeader(string name) { none() }
233+
}
234+
235+
/**
236+
* The `req` parameter of a `socket.on("connection", (msg, req) => {})` call.
237+
*/
238+
class ServerHTTPRequest extends HTTP::Servers::RequestSource {
239+
ConnectionCallAsRouteHandler handler;
240+
241+
ServerHTTPRequest() { this = handler.getCallback(1).getParameter(1) }
242+
243+
override HTTP::RouteHandler getRouteHandler() { result = handler }
244+
}
245+
246+
/**
247+
* An access user-controlled HTTP request input in a request to a WebSocket server.
248+
*/
249+
class WebSocketRequestInput extends HTTP::RequestInputAccess {
250+
ServerHTTPRequest request;
251+
string kind;
252+
253+
WebSocketRequestInput() {
254+
kind = "url" and
255+
this = request.ref().getAPropertyRead("url")
256+
or
257+
kind = "header" and
258+
this = request.ref().getAPropertyRead(["headers", "rawHeaders"]).getAPropertyRead()
259+
or
260+
// req.headers.cookie
261+
kind = "cookie" and
262+
this = request.ref().getAPropertyRead("headers").getAPropertyRead("cookie")
263+
}
264+
265+
override string getKind() { result = kind }
266+
267+
override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() }
268+
}
269+
224270
/**
225271
* A message sent from a WebSocket server.
226272
*/

javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ nodes
7777
| tst.js:98:29:98:35 | req.url |
7878
| tst.js:100:19:100:25 | tainted |
7979
| tst.js:100:19:100:25 | tainted |
80+
| tst.js:108:11:108:27 | url |
81+
| tst.js:108:17:108:27 | request.url |
82+
| tst.js:108:17:108:27 | request.url |
83+
| tst.js:109:27:109:29 | url |
84+
| tst.js:109:27:109:29 | url |
8085
edges
8186
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
8287
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
@@ -152,6 +157,10 @@ edges
152157
| tst.js:98:19:98:52 | url.par ... ery.url | tst.js:98:9:98:52 | tainted |
153158
| tst.js:98:29:98:35 | req.url | tst.js:98:19:98:42 | url.par ... , true) |
154159
| tst.js:98:29:98:35 | req.url | tst.js:98:19:98:42 | url.par ... , true) |
160+
| tst.js:108:11:108:27 | url | tst.js:109:27:109:29 | url |
161+
| tst.js:108:11:108:27 | url | tst.js:109:27:109:29 | url |
162+
| tst.js:108:17:108:27 | request.url | tst.js:108:11:108:27 | url |
163+
| tst.js:108:17:108:27 | request.url | tst.js:108:11:108:27 | url |
155164
#select
156165
| tst.js:18:5:18:20 | request(tainted) | tst.js:14:29:14:35 | req.url | tst.js:18:13:18:19 | tainted | The $@ of this request depends on $@. | tst.js:18:13:18:19 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
157166
| tst.js:20:5:20:24 | request.get(tainted) | tst.js:14:29:14:35 | req.url | tst.js:20:17:20:23 | tainted | The $@ of this request depends on $@. | tst.js:20:17:20:23 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
@@ -173,3 +182,4 @@ edges
173182
| tst.js:90:5:90:33 | JSDOM.f ... ms.foo) | tst.js:90:19:90:28 | ctx.params | tst.js:90:19:90:32 | ctx.params.foo | The $@ of this request depends on $@. | tst.js:90:19:90:32 | ctx.params.foo | URL | tst.js:90:19:90:28 | ctx.params | a user-provided value |
174183
| tst.js:92:5:92:33 | JSDOM.f ... ms.foo) | tst.js:92:19:92:28 | ctx.params | tst.js:92:19:92:32 | ctx.params.foo | The $@ of this request depends on $@. | tst.js:92:19:92:32 | ctx.params.foo | URL | tst.js:92:19:92:28 | ctx.params | a user-provided value |
175184
| tst.js:100:5:100:26 | new Web ... ainted) | tst.js:98:29:98:35 | req.url | tst.js:100:19:100:25 | tainted | The $@ of this request depends on $@. | tst.js:100:19:100:25 | tainted | URL | tst.js:98:29:98:35 | req.url | a user-provided value |
185+
| tst.js:109:20:109:30 | new ws(url) | tst.js:108:17:108:27 | request.url | tst.js:109:27:109:29 | url | The $@ of this request depends on $@. | tst.js:109:27:109:29 | url | URL | tst.js:108:17:108:27 | request.url | a user-provided value |

javascript/ql/test/query-tests/Security/CWE-918/tst.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,13 @@ var server = http.createServer(async function(req, res) {
9999

100100
new WebSocket(tainted); // NOT OK
101101
});
102+
103+
104+
import * as ws from 'ws';
105+
106+
new ws.Server({ port: 8080 }).on('connection', function(socket, request) {
107+
socket.on('message', function(message) {
108+
const url = request.url;
109+
const socket = new ws(url);
110+
});
111+
});

0 commit comments

Comments
 (0)