Security [MEDIUM]: Blueprint validator uses native RegExp — admin-set pattern enables ReDoS #52
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
packages/engine/src/blueprint/validator.ts:98doesnew RegExp(v.pattern).test(strVal)where pattern comes from admin-editable BlueprintField. A catastrophic-backtracking pattern (^(a+)+$) plus crafted input freezes the event loop. Relevant if MANAGE_BLUEPRINTS is ever delegated or if an admin-account is compromised.Evidence
packages/engine/src/blueprint/validator.ts:98 — new RegExp(v.pattern).test(strVal)Impact
DoS: single request to create/update resource with crafted value + malicious pattern → full process freeze for multiple seconds per request.
Proposed Fix
Replace
RegExpwithre2(linear-time regex). Also reject unsafe patterns at blueprint save-time viasafe-regex. Fallback: wrap test insetTimeout-cancel pattern with Worker.Acceptance Criteria
Parent Epic: #1
Source: Full-Codebase Security Audit 2026-04-16 (B-8)
Resolved in commit
019702c(security: ReDoS hardening on blueprint field validator).Three-layer defence:
Save-time (
packages/shared/src/schemas/blueprint.schema.ts:33-54) —FieldValidationSchema.patternnow has.max(200)and a.refine()that rejects grouped nested-quantifier shapes ((x+)+,(?:x*)+,(x{n,})*…). Admin UI will show a clear validation error at blueprint save time instead of silently storing the ReDoS pattern.Runtime (
packages/engine/src/blueprint/validator.ts:26-33, 142-171) —isSuspectRegexPattern()re-runs the same heuristic before invokingnew RegExp(). If it fires, the field fails validation OUTRIGHT; the regex is never compiled or run..test()(belt-and-suspenders) so even a benign pattern against a 10 MB payload returns in < 50 ms.new RegExp()compile failures are caught → validation error instead of 500.Tests —
packages/engine/src/__tests__/blueprint-validator-redos.test.ts(10 cases) pins all three behaviours:^(a+)+$+ 30-'a' attack input → errors in < 50 ms^[a-z]+$,^\d{3}-\d{4}$, email-style) pass throughAcceptance-criteria:
Closing.