Skip to content

Commit 692ea72

Browse files
committed
feat: parse: add throwOnParameterLimitExceeded option
1 parent d185cee commit 692ea72

File tree

3 files changed

+66
-2
lines changed

3 files changed

+66
-2
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,18 @@ var limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
135135
assert.deepEqual(limited, { a: 'b' });
136136
```
137137

138+
If you want an error to be thrown whenever the parameter limit is exceeded, set the `throwOnParameterLimitExceeded` option to `true`. This option will generate a descriptive error if the query string exceeds the parameterLimit.
139+
```javascript
140+
try {
141+
qs.parse('a=1&b=2&c=3&d=4', { parameterLimit: 3, throwOnParameterLimitExceeded: true });
142+
} catch (err) {
143+
assert(err instanceof Error);
144+
assert.strictEqual(err.message, 'Parameter limit exceeded. Only 3 parameters allowed.');
145+
}
146+
```
147+
148+
When `throwOnParameterLimitExceeded` is set to `false` (default), **qs** will parse up to the specified `parameterLimit` and ignore the rest without throwing an error.
149+
138150
To bypass the leading question mark, use `ignoreQueryPrefix`:
139151

140152
```javascript

lib/parse.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,19 @@ var parseValues = function parseQueryStringValues(str, options) {
5757

5858
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
5959
cleanStr = cleanStr.replace(/%5B/gi, '[').replace(/%5D/gi, ']');
60+
61+
// Calculate limit and split query string
6062
var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;
61-
var parts = cleanStr.split(options.delimiter, limit);
63+
var parts = cleanStr.split(options.delimiter);
64+
65+
// Check if parts exceed the parameter limit and handle it
66+
if (parts.length > limit) {
67+
if (options.throwOnParameterLimitExceeded) {
68+
throw new Error(`Parameter limit exceeded. Only ${limit} parameters allowed.`);
69+
}
70+
parts = parts.slice(0, limit); // Keep only up to the limit
71+
}
72+
6273
var skipIndex = -1; // Keep track of where the utf8 sentinel was found
6374
var i;
6475

@@ -266,7 +277,8 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
266277
parseArrays: opts.parseArrays !== false,
267278
plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects,
268279
strictDepth: typeof opts.strictDepth === 'boolean' ? !!opts.strictDepth : defaults.strictDepth,
269-
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
280+
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling,
281+
throwOnParameterLimitExceeded: typeof opts.throwOnParameterLimitExceeded === 'boolean' ? opts.throwOnParameterLimitExceeded : false
270282
};
271283
};
272284

test/parse.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,46 @@ test('parse()', function (t) {
10351035
st.end();
10361036
});
10371037

1038+
t.test('parameter limit tests', function (st) {
1039+
st.test('does not throw error when within parameter limit', function (sst) {
1040+
const result = qs.parse('a=1&b=2&c=3', { parameterLimit: 5, throwOnParameterLimitExceeded: true });
1041+
sst.deepEqual(result, { a: '1', b: '2', c: '3' }, 'Should parse without errors');
1042+
sst.end();
1043+
});
1044+
1045+
st.test('throws error when parameter limit exceeded', function (sst) {
1046+
sst.throws(
1047+
function () {
1048+
qs.parse('a=1&b=2&c=3&d=4&e=5&f=6', { parameterLimit: 3, throwOnParameterLimitExceeded: true });
1049+
},
1050+
new Error('Parameter limit exceeded. Only 3 parameters allowed.'),
1051+
'Should throw error when parameter limit is exceeded'
1052+
);
1053+
sst.end();
1054+
});
1055+
1056+
st.test('silently truncates when throwOnParameterLimitExceeded is not given', function (sst) {
1057+
const result = qs.parse('a=1&b=2&c=3&d=4&e=5', { parameterLimit: 3 });
1058+
sst.deepEqual(result, { a: '1', b: '2', c: '3' }, 'Should parse and truncate silently');
1059+
sst.end();
1060+
});
1061+
1062+
st.test('silently truncates when parameter limit exceeded without error', function (sst) {
1063+
const result = qs.parse('a=1&b=2&c=3&d=4&e=5', { parameterLimit: 3, throwOnParameterLimitExceeded: false });
1064+
sst.deepEqual(result, { a: '1', b: '2', c: '3' }, 'Should parse and truncate silently');
1065+
sst.end();
1066+
});
1067+
1068+
st.test('allows unlimited parameters when parameterLimit set to Infinity', function (sst) {
1069+
const result = qs.parse('a=1&b=2&c=3&d=4&e=5&f=6', { parameterLimit: Infinity });
1070+
sst.deepEqual(result, { a: '1', b: '2', c: '3', d: '4', e: '5', f: '6' }, 'Should parse all parameters without truncation');
1071+
sst.end();
1072+
});
1073+
1074+
st.end();
1075+
});
1076+
1077+
10381078
t.end();
10391079
});
10401080

0 commit comments

Comments
 (0)