Skip to content

Commit a5851e5

Browse files
authoredMay 17, 2024
Merge pull request #37 from coderaiser/fix/vulnerability
fix: vulnerability
2 parents 98414f9 + 2092bd1 commit a5851e5

File tree

5 files changed

+76
-21
lines changed

5 files changed

+76
-21
lines changed
 

‎README.md

+12
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ console.log(braces.expand('a{b}c'));
186186
console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
187187
```
188188

189+
### options.maxSymbols
190+
191+
**Type**: `Number`
192+
193+
**Default**: `1024`
194+
195+
**Description**: Limit the count of unique symbols the input string.
196+
197+
```js
198+
console.log(braces('a/{b,c}/d', { maxSymbols: 2 })); //=> throws an error
199+
```
200+
189201
### options.expand
190202

191203
**Type**: `Boolean`

‎lib/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
module.exports = {
44
MAX_LENGTH: 1024 * 64,
5+
MAX_SYMBOLS: 1024,
56

67
// Digits
78
CHAR_0: '0', /* 0 */

‎lib/parse.js

+41-21
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
'use strict';
22

33
const stringify = require('./stringify');
4+
const {isCorrectBraces, validateInput} = require('./validate-input');
45

56
/**
67
* Constants
78
*/
89

910
const {
1011
MAX_LENGTH,
12+
MAX_SYMBOLS,
1113
CHAR_BACKSLASH, /* \ */
1214
CHAR_BACKTICK, /* ` */
1315
CHAR_COMMA, /* , */
@@ -34,6 +36,11 @@ const parse = (input, options = {}) => {
3436
}
3537

3638
let opts = options || {};
39+
40+
validateInput(input, {
41+
maxSymbols: opts.maxSymbols || MAX_SYMBOLS,
42+
});
43+
3744
let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
3845
if (input.length > max) {
3946
throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
@@ -304,30 +311,43 @@ const parse = (input, options = {}) => {
304311
push({ type: 'text', value });
305312
}
306313

314+
flattenBlocks(stack)
315+
markImbalancedBraces(ast);
316+
push({ type: 'eos' });
317+
318+
return ast;
319+
};
320+
321+
module.exports = parse;
322+
323+
function markImbalancedBraces({nodes}) {
307324
// Mark imbalanced braces and brackets as invalid
325+
for (const node of nodes) {
326+
if (!node.nodes && !node.invalid) {
327+
if (node.type === 'open') node.isOpen = true;
328+
if (node.type === 'close') node.isClose = true;
329+
if (!node.nodes) node.type = 'text';
330+
331+
node.invalid = true;
332+
}
333+
334+
delete node.parent;
335+
delete node.prev;
336+
}
337+
}
338+
339+
function flattenBlocks(stack) {
340+
let block;
308341
do {
309342
block = stack.pop();
310343

311-
if (block.type !== 'root') {
312-
block.nodes.forEach(node => {
313-
if (!node.nodes) {
314-
if (node.type === 'open') node.isOpen = true;
315-
if (node.type === 'close') node.isClose = true;
316-
if (!node.nodes) node.type = 'text';
317-
node.invalid = true;
318-
}
319-
});
344+
if (block.type === 'root')
345+
continue;
320346

321-
// get the location of the block on parent.nodes (block's siblings)
322-
let parent = stack[stack.length - 1];
323-
let index = parent.nodes.indexOf(block);
324-
// replace the (invalid) block with it's nodes
325-
parent.nodes.splice(index, 1, ...block.nodes);
326-
}
347+
// get the location of the block on parent.nodes (block's siblings)
348+
let parent = stack.at(-1);
349+
let index = parent.nodes.indexOf(block);
350+
// replace the (invalid) block with its nodes
351+
parent.nodes.splice(index, 1, ...block.nodes);
327352
} while (stack.length > 0);
328-
329-
push({ type: 'eos' });
330-
return ast;
331-
};
332-
333-
module.exports = parse;
353+
}

‎lib/validate-input.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports.validateInput = (line, {maxSymbols}) => {
2+
const symbols = {};
3+
4+
for (const current of line) {
5+
symbols[current] = (symbols[current] || 0) + 1;
6+
}
7+
8+
for (const [value, count] of Object.entries(symbols)) {
9+
if (count > maxSymbols)
10+
throw SyntaxError(`To many symbols '${value}'. Maximum: ${maxSymbols} allowed. Received: ${count}`);
11+
}
12+
};

‎test/braces.parse.js

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ describe('braces.parse()', () => {
1010
let MAX_LENGTH = 1024 * 64;
1111
assert.throws(() => parse('.'.repeat(MAX_LENGTH + 2)));
1212
});
13+
it('should throw an error when symbols exceeds max symbols count default', () => {
14+
let SYMBOLS= 1024;
15+
assert.throws(() => parse('.'.repeat(MAX_SYMBOLS * 2)));
16+
});
17+
it('should throw an error when symbols exceeds max symbols count ', () => {
18+
let SYMBOLS= 2;
19+
assert.throws(() => parse('...', {
20+
maxSymbols: 2,
21+
}));
22+
});
1323
});
1424

1525
describe('valid', () => {

0 commit comments

Comments
 (0)