Skip to content

security: cap cloneNode/isEqualNode recursion depth - #275

Merged
tannevaled merged 1 commit into
mainfrom
fix-dom-clone-equal-recursion-depth
Oct 12, 2026
Merged

tannevaled merged 1 commit into
mainfrom
fix-dom-clone-equal-recursion-depth

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Continuing the same sweep as the calc()/:not()/:is() CSS parser fixes (#273/#274), this time across JS-reachable DOM methods: isEqualNode (node.isEqualNode(other)) and cloneNode (node.cloneNode(deep), also document.importNode) each recurse over a node's own Children with no depth check — and unlike round 166's cascade/layout caps, nothing here guards a script calling one of these directly on a tree it already has, independently of whether that tree is ever rendered.

Confirmed with a 10,000-deep synthetic chain (dom.AppendChild has no depth check of its own, by design) that both recurse unboundedly. Fixed with a depth parameter capped at maxDOMWalkDepth = 512, matching the HTML parser's own bound. Past the cap, isEqualNode reports false (never crashing) and cloneNode stops descending, returning a real node with an empty subtree rather than failing the whole call.

Test plan

  • go build ./...
  • go test ./... (all packages pass)
  • New file js/dom_test.go: TestIsEqualNodeDoesNotCrashOnPathologicallyDeepTree, TestIsEqualNodeDeepMismatchPastCapIsNotEqual, TestCloneNodeDoesNotCrashOnPathologicallyDeepTree
  • js package coverage unaffected (95.9% baseline)

🤖 Generated with Claude Code

Continuing the same sweep as the calc()/:not()/:is() CSS parser
fixes, this time across JS-reachable DOM methods: isEqualNode
(node.isEqualNode(other)) and cloneNode (node.cloneNode(deep),
also document.importNode) each recurse over a node's own Children
with no depth check -- and unlike round 166's cascade/layout caps,
nothing here guards a script calling one of these directly on a
tree it already has, independently of whether that tree is ever
rendered.

Confirmed with a 10,000-deep synthetic chain (dom.AppendChild has
no depth check of its own, by design) that both recurse unboundedly.
Fixed with a depth parameter capped at maxDOMWalkDepth = 512,
matching the HTML parser's own bound. Past the cap, isEqualNode
reports false (never crashing) and cloneNode stops descending,
returning a real node with an empty subtree rather than failing
the whole call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 23fac33 into main Oct 12, 2026
7 checks passed
@tannevaled
tannevaled deleted the fix-dom-clone-equal-recursion-depth branch October 12, 2026 07:29
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