Skip to content

Commit c4fe466

Browse files
committed
[security] Fix DoS vulnerability
Ignore extension and parameter names that are property names of `Object.prototype` when parsing the `Sec-WebSocket-Extensions` header.
1 parent 56f8062 commit c4fe466

2 files changed

Lines changed: 28 additions & 10 deletions

File tree

lib/Extensions.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ const parse = (value) => {
1515
value.split(',').forEach((v) => {
1616
const params = v.split(';');
1717
const token = params.shift().trim();
18-
const paramsList = extensions[token] = extensions[token] || [];
18+
19+
if (extensions[token] === undefined) {
20+
extensions[token] = [];
21+
} else if (!extensions.hasOwnProperty(token)) {
22+
return;
23+
}
24+
1925
const parsedParams = {};
2026

2127
params.forEach((param) => {
@@ -34,10 +40,15 @@ const parse = (value) => {
3440
value = value.slice(0, value.length - 1);
3541
}
3642
}
37-
(parsedParams[key] = parsedParams[key] || []).push(value);
43+
44+
if (parsedParams[key] === undefined) {
45+
parsedParams[key] = [value];
46+
} else if (parsedParams.hasOwnProperty(key)) {
47+
parsedParams[key].push(value);
48+
}
3849
});
3950

40-
paramsList.push(parsedParams);
51+
extensions[token].push(parsedParams);
4152
});
4253

4354
return extensions;

test/Extensions.test.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ const Extensions = require('../lib/Extensions');
66

77
describe('Extensions', function () {
88
describe('parse', function () {
9-
it('should parse', function () {
9+
it('parses a single extension', function () {
1010
const extensions = Extensions.parse('foo');
1111

1212
assert.deepStrictEqual(extensions, { foo: [{}] });
1313
});
1414

15-
it('should parse params', function () {
15+
it('parses params', function () {
1616
const extensions = Extensions.parse('foo; bar; baz=1; bar=2');
1717

1818
assert.deepStrictEqual(extensions, {
1919
foo: [{ bar: [true, '2'], baz: ['1'] }]
2020
});
2121
});
2222

23-
it('should parse multiple extensions', function () {
23+
it('parse multiple extensions', function () {
2424
const extensions = Extensions.parse('foo, bar; baz, foo; baz');
2525

2626
assert.deepStrictEqual(extensions, {
@@ -29,29 +29,36 @@ describe('Extensions', function () {
2929
});
3030
});
3131

32-
it('should parse quoted params', function () {
32+
it('parses quoted params', function () {
3333
const extensions = Extensions.parse('foo; bar="hi"');
3434

3535
assert.deepStrictEqual(extensions, {
3636
foo: [{ bar: ['hi'] }]
3737
});
3838
});
39+
40+
it('ignores names that match Object.prototype properties', function () {
41+
const parse = Extensions.parse;
42+
43+
assert.deepStrictEqual(parse('hasOwnProperty, toString'), {});
44+
assert.deepStrictEqual(parse('foo; constructor'), { foo: [{}] });
45+
});
3946
});
4047

4148
describe('format', function () {
42-
it('should format', function () {
49+
it('formats a single extension', function () {
4350
const extensions = Extensions.format({ foo: {} });
4451

4552
assert.strictEqual(extensions, 'foo');
4653
});
4754

48-
it('should format params', function () {
55+
it('formats params', function () {
4956
const extensions = Extensions.format({ foo: { bar: [true, 2], baz: 1 } });
5057

5158
assert.strictEqual(extensions, 'foo; bar; bar=2; baz=1');
5259
});
5360

54-
it('should format multiple extensions', function () {
61+
it('formats multiple extensions', function () {
5562
const extensions = Extensions.format({
5663
foo: [{}, { baz: true }],
5764
bar: { baz: true }

0 commit comments

Comments
 (0)