fix(chat): remove quadratic backtracking from two regexes - #58
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the two high-severity CodeQL
js/polynomial-redosalerts. Both are in shipped package code, and one is reachable from untrusted input.parseChipSegments— the serious oneCHIP_REF_PATTERN's label group was[^\]]+. Given text with a long run of[and no closing bracket, the engine consumed to end-of-string, failed, backtracked over every position, advanced one character and repeated — quadratic in the text length.It matters because message text is untrusted: it comes from the model.
Message.TextcallsparseChipSegmentson it directly, so an adversarial response could freeze a consumer's tab.Measured, old pattern:
4× the input for 12× the time, then 16× for 161× — textbook quadratic. Extrapolated to 200 000 characters that's roughly fourteen seconds. The new pattern is flat at 0.0 ms across all three sizes.
The fix excludes
[from the label class. No behaviour change: I verified the round-trip both ways, and labels containing brackets already failed to match under the old pattern, becauseescapeMarkdownLinkemits\[…\]and the old class excluded]regardless of the backslash:detectActivePrefix— same shape, self-inflictedleftRunused/\S*$/, which retries from every position when the text ends in whitespace. Input is the user's own composer text, so it isn't remotely triggerable — but it runs on every keystroke through the trigger tracker, so pasting a large single-token blob froze the editor.Measured on a 200 000-character run followed by a space: 16 414 ms old, 0.0 ms new, with byte-identical results. Replaced with a backwards index scan — linear, allocation-free, and it also removed a slice allocation from the word-boundary check that followed.
The
/^\S*/on the other side is start-anchored and cannot backtrack, so it stays.Regression tests
Both are covered, and I checked the tests are actually load-bearing rather than passing either way — measured out-of-band, the old implementations exceed the 100 ms budgets by 164× and 138× respectively. Also added a test that a bracket run followed by a real chip still finds the chip, so the tightened class can't silently over-restrict.
Also here
ci.ymlgains an explicitpermissions: contents: read, closing the mediumactions/missing-workflow-permissionsalert. The repo default is already read-only; stating it means a change to that default can't silently widen CI.Not in scope
js/incomplete-sanitizationatcomposer-prefix.test.ts:11is a false positive — the helper replaces a single caret marker on purpose. Worth dismissing in the UI rather than "fixing".(comes back asparen\(x\). Unrelated to ReDoS, no security impact, wants its own PR.Verified
182 tests (3 new),
tsc --noEmitclean, biome clean, package build + publint clean, app builds. Changeset resolves topatch→ 0.1.1.