You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Polynomial regular-expression backtracking in the length core is fixed (CodeQL js/polynomial-redos, alerts #28 and #29 — High). The expressions matching ODF length and percentage lexicals shared the numeric core \d+\.?\d*: because the dot was optional, \d+ and \d* were both applicable to the same run of digits, so an n-digit input produced on the order of n²/2 backtracking paths before the anchored suffix failed. Both are reachable from raw document attribute text, which makes a crafted .odt the direct threat model. The rewrites are language-equivalent — \d+(?:\.\d*)? accepts exactly the same lexicals as \d+\.?\d*, including the trailing-dot form — and are proven so by a differential test that compares the old and new expressions for identical accept/reject decisions and identical capture groups, and separately compares the whole pre-change parsing pipeline against the shipped one. Accepted input is unchanged.
The same class in decimal formatting is fixed (alert #30 — moderate). The trailing-zero strip used an expression unanchored at the left, quadratic on long runs of zeros; it is now a character walk. CodeQL traced this one from caller-supplied API arguments rather than from document text, hence the lower severity. Worth recording: the strip's three states are provably unreachable from every public entry point — the rational core normalises to lowest terms, so no computed numerator ends in zero, and the shortest-decimal search returns at the minimal scale, where a trailing zero would contradict minimality. The rewrite removes the cost regardless, and the release adds a sweep asserting the guarantee that makes the strip unreachable: no decimal odf-kit computes ever carries a trailing zero in its fractional part or ends in a bare dot. Values you hand in are unaffected — a lexical such as 1.500cm is still preserved verbatim.
Two further sites of the same class are fixed in CSS dimension parsing (htmlToOdt's px and unitless line-height handling). These were found by a manual idiom sweep of the source rather than by CodeQL, which did not flag them, and they sit on the same untrusted-input path as the alerts above. Language-equivalent rewrites; accepted input unchanged.
New documented input bound: numeric lexicals longer than 64 characters are rejected.parseOdfValue returns undefined; value-constructing APIs throw an error naming the bound, and that error excerpts the offending text rather than echoing unbounded input back. This is a deliberate deviation from the ODF RNG grammar, whose [0-9]* is unbounded. The bound is derived rather than chosen — from odf-kit's own precision ceilings, the 25-fractional-digit emission search and the 15-significant-digit float boundary — and sits far beyond any observed producer output, where the deepest values seen carry about six digits. It exists to bound parse cost against crafted documents, and it also closes a non-regex cost path in which very long inputs drove BigInt and GCD work with no expression involved.
CSS px values with 25 or more fractional digits now degrade instead of converting. This is a narrowing of accepted input, not only a crash fix. Such a value implies an interval narrower than the emission search's finest grid, so before this release roughly a quarter of them threw and the rest converted, decided by the mantissa's digits — behaviour no caller could predict. The threshold is the guaranteed-safe one, derived from the search ceiling and the px-to-pt factor rather than measured, so conversion either succeeds or is declined deterministically. Values at 24 fractional digits and below convert exactly as before; no producer emits at this depth.
htmlToOdt no longer throws on CSS lengths it cannot convert. Its documented contract — unconvertible input yields "property not extracted", never an exception — was violated by two lexical classes reachable from crafted CSS: the trailing-dot form (12.px), which the expression accepted but the length core rejected, and values past the emission ceiling. All three rejection classes — malformed shape, over the 64-character bound, and past the emission depth — now degrade, so the property is simply absent from the output and conversion completes. The bound throws at API boundaries, where a programmer can act on the error, and degrades at document boundaries, where attacker-controlled input must not abort a conversion.