bank20baht-validator 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +26 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -443,6 +443,25 @@ function assertValidFields(form) {
|
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* True for the textbook ReDoS shape: an unbounded quantifier (`* + {n,}`)
|
|
448
|
+
* applied to a group whose ENTIRE body is one unboundedly-quantified atom —
|
|
449
|
+
* `(a+)+`, `(\d*)*`, `(?:\w+){2,}`. That exact nesting is always either
|
|
450
|
+
* redundant (matches the same language as a single quantifier) or exponential
|
|
451
|
+
* to backtrack, so rejecting it has no legitimate false positives. It is
|
|
452
|
+
* deliberately NARROW: longer bodies like `(abc+)+` are safe (the literal
|
|
453
|
+
* prefix removes the cross-iteration ambiguity) and other catastrophic shapes
|
|
454
|
+
* (e.g. overlapping alternation `(a|a)*`) are out of scope — patterns are
|
|
455
|
+
* builder-authored, so the goal is catching the classic authoring mistake at
|
|
456
|
+
* save time, not sandboxing hostile regexes.
|
|
457
|
+
*/
|
|
458
|
+
function hasNestedUnboundedQuantifier(pattern) {
|
|
459
|
+
// Strip escaped chars and character classes first so `\+`, `[+]` etc.
|
|
460
|
+
// aren't mistaken for quantifiers or group delimiters.
|
|
461
|
+
const stripped = pattern.replace(/\\./g, 'x').replace(/\[[^\]]*\]/g, 'C');
|
|
462
|
+
const unbounded = '(?:[*+]|\\{\\d+,\\})';
|
|
463
|
+
return new RegExp(`\\((?:\\?:)?[^()|]${unbounded}\\??\\)${unbounded}`).test(stripped);
|
|
464
|
+
}
|
|
446
465
|
function assertFieldShape(field, formName) {
|
|
447
466
|
if (typeof field.type !== 'string' || !FIELD_TYPES.has(field.type)) {
|
|
448
467
|
throw new Error(`unknown field type ${JSON.stringify(field.type)} on ${formName}`);
|
|
@@ -473,12 +492,18 @@ function assertFieldShape(field, formName) {
|
|
|
473
492
|
if (v.type === 'pattern') {
|
|
474
493
|
// compile now — an invalid regex would otherwise throw (or hang, if
|
|
475
494
|
// catastrophic) inside validateField on every future submission
|
|
495
|
+
const pattern = String(v.value);
|
|
476
496
|
try {
|
|
477
|
-
new RegExp(
|
|
497
|
+
new RegExp(pattern);
|
|
478
498
|
}
|
|
479
499
|
catch {
|
|
480
500
|
throw new Error(`invalid pattern ${JSON.stringify(v.value)} on ${formName}.${field.key}`);
|
|
481
501
|
}
|
|
502
|
+
if (hasNestedUnboundedQuantifier(pattern)) {
|
|
503
|
+
throw new Error(`pattern on ${formName}.${field.key} nests unbounded quantifiers ` +
|
|
504
|
+
`(e.g. "(a+)+") — this backtracks catastrophically on hostile input; ` +
|
|
505
|
+
`an equivalent single quantifier matches the same strings`);
|
|
506
|
+
}
|
|
482
507
|
}
|
|
483
508
|
}
|
|
484
509
|
}
|
package/package.json
CHANGED