Skip to content

Commit 078ddca

Browse files
knagaitsevhiroppy
authored andcommitted
fix(server): stricter headers security check (#2092)
* fix(server): stricter headers security check * fix(server): changed comments explaining how server impl works
1 parent 56274e4 commit 078ddca

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

lib/Server.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,14 @@ class Server {
685685
return;
686686
}
687687

688-
if (headers && (!this.checkHost(headers) || !this.checkOrigin(headers))) {
688+
if (!headers) {
689+
this.log.warn(
690+
'serverMode implementation must pass headers to the callback of onConnection(f) ' +
691+
'via f(connection, headers) in order for clients to pass a headers security check'
692+
);
693+
}
694+
695+
if (!headers || !this.checkHost(headers) || !this.checkOrigin(headers)) {
689696
this.sockWrite([connection], 'error', 'Invalid Host/Origin header');
690697

691698
this.socketServer.close(connection);

lib/servers/SockJSServer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ module.exports = class SockJSServer extends BaseServer {
5757
connection.close();
5858
}
5959

60-
// f should return the resulting connection and, optionally, the connection headers
60+
// f should be passed the resulting connection and the connection headers
6161
onConnection(f) {
6262
this.socket.on('connection', (connection) => {
6363
f(connection, connection.headers);

lib/servers/WebsocketServer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module.exports = class WebsocketServer extends BaseServer {
2727
connection.close();
2828
}
2929

30-
// f should return the resulting connection
30+
// f should be passed the resulting connection and the connection headers
3131
onConnection(f) {
3232
this.wsServer.on('connection', (connection, req) => {
3333
f(connection, req.headers);

test/server/__snapshots__/serverMode-option.test.js.snap

+8
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ Array [
77
"close",
88
]
99
`;
10+
11+
exports[`serverMode option without a header results in an error 1`] = `
12+
Array [
13+
"open",
14+
"{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}",
15+
"close",
16+
]
17+
`;

test/server/serverMode-option.test.js

+91
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,95 @@ describe('serverMode option', () => {
223223
}, 5000);
224224
});
225225
});
226+
227+
describe('without a header', () => {
228+
let mockWarn;
229+
beforeAll((done) => {
230+
server = testServer.start(
231+
config,
232+
{
233+
port,
234+
serverMode: class MySockJSServer extends BaseServer {
235+
constructor(serv) {
236+
super(serv);
237+
this.socket = sockjs.createServer({
238+
// Use provided up-to-date sockjs-client
239+
sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js',
240+
// Limit useless logs
241+
log: (severity, line) => {
242+
if (severity === 'error') {
243+
this.server.log.error(line);
244+
} else {
245+
this.server.log.debug(line);
246+
}
247+
},
248+
});
249+
250+
this.socket.installHandlers(this.server.listeningApp, {
251+
prefix: this.server.sockPath,
252+
});
253+
}
254+
255+
send(connection, message) {
256+
connection.write(message);
257+
}
258+
259+
close(connection) {
260+
connection.close();
261+
}
262+
263+
onConnection(f) {
264+
this.socket.on('connection', (connection) => {
265+
f(connection);
266+
});
267+
}
268+
269+
onConnectionClose(connection, f) {
270+
connection.on('close', f);
271+
}
272+
},
273+
},
274+
done
275+
);
276+
277+
mockWarn = jest.spyOn(server.log, 'warn').mockImplementation(() => {});
278+
});
279+
280+
it('results in an error', (done) => {
281+
const data = [];
282+
const client = new SockJS(`http://localhost:${port}/sockjs-node`);
283+
284+
client.onopen = () => {
285+
data.push('open');
286+
};
287+
288+
client.onmessage = (e) => {
289+
data.push(e.data);
290+
};
291+
292+
client.onclose = () => {
293+
data.push('close');
294+
};
295+
296+
setTimeout(() => {
297+
expect(data).toMatchSnapshot();
298+
const calls = mockWarn.mock.calls;
299+
mockWarn.mockRestore();
300+
301+
let foundWarning = false;
302+
const regExp = /serverMode implementation must pass headers to the callback of onConnection\(f\)/;
303+
calls.every((call) => {
304+
if (regExp.test(call)) {
305+
foundWarning = true;
306+
return false;
307+
}
308+
return true;
309+
});
310+
311+
expect(foundWarning).toBeTruthy();
312+
313+
done();
314+
}, 5000);
315+
});
316+
});
226317
});

0 commit comments

Comments
 (0)