Skip to content

security: cap text-level CSS parser recursion/output on calc(), :not(), :is() - #273

Merged
tannevaled merged 1 commit into
mainfrom
fix-calc-paren-depth-stack-overflow
Oct 11, 2026
Merged

tannevaled merged 1 commit into
mainfrom
fix-calc-paren-depth-stack-overflow

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

  • calcParser's grammar (expr/term/factor, the standard mutually-recursive expression-parser shape) recurses from factor straight back into expr for each ( with no depth check.
  • Unlike deep HTML element nesting, nothing upstream bounds how many parens a single calc(...) value's own source text can carry — a style="..." attribute on the main document (capped only at 32MB) can trivially carry millions.
  • Confirmed live: a synthetic 3,000,000-deep calc((((...1...)))) crashes with an unrecoverable fatal error: stack overflow (not a panic — recover() cannot catch it) where 1,000,000 does not yet.
  • factor now caps its own recursion at 256 levels, failing the same way this parser already fails on any other malformed input rather than recursing further.

Test plan

  • go build ./...
  • go test ./... (all packages pass)
  • New test TestResolveCalcDoesNotCrashOnPathologicallyDeepParens
  • css package coverage unaffected (99.5% before/after); factor's own coverage rises slightly (92.0%→95.0%)

🤖 Generated with Claude Code

calcParser's grammar (expr/term/factor, the standard mutually-
recursive expression-parser shape) recurses from factor straight
back into expr for each '(' with no depth check. Unlike deep HTML
element nesting, nothing upstream bounds how many parens a single
calc(...) value's own source text can carry -- a style="..."
attribute on the main document (capped only at 32MB) can trivially
carry millions.

Confirmed live: a synthetic 3,000,000-deep calc((((...1...))))
crashes with an unrecoverable "fatal error: stack overflow" (not a
panic -- recover() cannot catch it) where 1,000,000 does not yet.

factor now caps its own recursion at 256 levels, failing the same
way this parser already fails on any other malformed input rather
than recursing further.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 1df2b47 into main Oct 11, 2026
7 checks passed
@tannevaled
tannevaled deleted the fix-calc-paren-depth-stack-overflow branch October 11, 2026 16:34
@tannevaled tannevaled changed the title security: cap calc() paren-nesting depth to stop a stack overflow security: cap text-level CSS parser recursion/output on calc(), :not(), :is() Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant