SEC2-080: A hostile JSON-LD description makes the page reader take quadratic time before anything is cut to size #37
Labels
No labels
bug
documentation
enhancement
performance
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-chromium#37
Loading…
Reference in a new issue
No description provided.
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?
Found on 2026-10-16 in a read-only security review of
postulo-chromiumatb6c1009, checked by a second, independent pass.Severity: Low · Category: Input Validation · Reference: CWE-1333 (Inefficient Regular Expression Complexity); CWE-400 (Uncontrolled Resource Consumption); OWASP A05:2021 (Security Misconfiguration)
Location:
src/lib/parse.js:390-399(fragmentText, the tag-splitting regular expression)src/lib/parse.js:641-665(fromPosting:describedis handed over uncut)src/lib/parse.js:785-800(tidycuts to 40 000 characters, after the reading)src/lib/parse.js:898-947(readPostings: once per posting on a results page)src/lib/browser.js:62-67(the wholeouterHTMLis read, unbounded;slimPagecaps only what is kept)Issue
String.prototype.splitwith a regular expression tries a match at every position. On adescription made of tag openers that never close, each attempt scans to the end of the string and
backtracks, so the time grows with the square of the length. Measured on Node 22 with the
expression copied verbatim: 10 000 characters 0.17 s, 20 000 0.68 s, 40 000 2.7 s; a 400 000-
character description would take minutes, all on the popup's thread.
fromPostingpassesposting.descriptionas the page wrote it (src/lib/parse.js:664-665); the 40 000-character cutin
tidycomes after. A results page is read posting by posting (readPostings), so the costrepeats.
MAX_FIELD,MAX_DESCRIPTIONandMAX_PAGEbound what is kept and sent, not what isread.
Impact
A job page that wants to can freeze the popup when the person presses the toolbar button on it;
the capture is never made. No data leaves, nothing is stored, and only a page the person chose to
capture can do it. Low.
Fix
Bound the input before reading it:
This alone bounds the worst case to about 450 ms. Then make the split linear: in
fragmentText,replace the backtracking split with a single pass that walks the string once (two booleans for
"inside a tag" and "inside a quote") and emits the same pieces. Consider also cutting
tab.htmlto a multiple of
MAX_PAGEinreadCurrentTabbeforeDOMParsersees it.Done when a test feeds a 1 MB description of repeated
<atoreadPostingand it returns withina second with the description truncated.
Related
#24 and #25 (closed) are about cutting lengths, not reading time.