fix(js): make createHTMLDocument return a detached document (147)#183
Open
ousamabenyounes wants to merge 1 commit into
Open
fix(js): make createHTMLDocument return a detached document (147)#183ousamabenyounes wants to merge 1 commit into
ousamabenyounes wants to merge 1 commit into
Conversation
jQuery 3.7.x feature-detects via:
var body = document.implementation.createHTMLDocument("").body;
body.innerHTML = "<form></form><form></form>";
Obscura's implementation returned globalThis.document, so the live
<body> was wiped down to two anonymous <form> stubs as soon as jQuery
loaded. Every page that used jQuery plus a navigation menu later in the
script bundle (e.g. WordPress + GeneratePress + offside.js) then
crashed: the `.slideout-navigation` selector matched zero elements,
offside.js's constructor was called with `undefined` as the target, and
`el.classList` threw. The visible symptom on affected pages was a 1-byte
`--dump text` output and the stack trace from issue 147 in stderr.
Switch to a new detached _IframeDocument (already used for srcdoc
iframes), and create a <title> child in the new head when a title arg
is provided. New regression test asserts that the sandbox is NOT the
live document and that the real <body> survives the innerHTML write.
Verification on the reporter's URL:
Before: 1 byte stdout, classList TypeError in stderr.
After: 57838 bytes stdout, no script error.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Fixes issue 147.
document.implementation.createHTMLDocument(title)returned the liveglobalThis.document. jQuery 3.7.x feature-detects via that API and writes<form></form><form></form>into the returned document's body, so the live<body>was wiped down to two anonymous<form>stubs as soon as jQuery loaded. The classListTypeErrorfromoffside.min.jsis a downstream symptom of the same root cause: with the body wiped,.slideout-navigationmatched zero elements and offside.js'sOffsideInstanceconstructor was called withundefinedas the target.Root Cause
crates/obscura-js/js/bootstrap.js, line 1045:Spec: createHTMLDocument must return a new, detached Document. jQuery's
src/selector.jsandsupportmodule rely on that detachment:With Obscura returning the live document, that
innerHTML =assignment cleared the real<body>. Snapshot taken at runtime before the patch:After the wipe, every later script that queried the page (offside.js for
.slideout-navigation, GP menu plugin for.main-navigation, WP-Rocket lazy-loader for image placeholders) saw an empty document and either crashed or no-op'd.--dump textcollapsed to 1 byte.Fix
Return a fresh
_IframeDocument(the same detached-document class already used for<iframe srcdoc>and similar). Create a<title>element in the new document's head when the caller passes a title argument._IframeDocumentalready constructs an<html><head></head><body></body></html>tree viadocument.createElement(...)calls; the resulting nodes are real DOM nodes but they are detached fromdocument.body, soinnerHTMLwrites on the sandbox's body never reach the live tree.createDocument(XML) is left untouched in this change — it has the same problem in principle but no caller in the reproducer exercised it; treating it the same way is a separate cleanup.Verification
Reporter's command, before the patch:
After the patch:
No
classListTypeError in stderr; full article body is dumped to stdout.The 3 other reporter URLs (
/vacuum-wars-best-robot-vacuums/,/top-rated-robot-vacuums-and-three-to-never-get/,/roborock-reviews/) all use the same GP-Premium + jQuery + offside.js stack and recover the same way.Test plan
cargo build --releaseclean (no new warnings)cargo check -p obscura-clicleancargo test -p obscura-js— 86 pass (baseline 85, +1 new regression test)cargo test -p obscura-dom— 31 pass (no change)cargo test -p obscura-browser— 3 pass (no change)create_html_document_returns_detached_documentreproduces the bug — RED (test fails when the one-line fix is reverted) and GREEN (test passes with fix applied). Captured locally on this branch.Risk
Low.
createHTMLDocumentwas previously aliasing the global document, which meant any code that relied on it returning a detached sandbox could not work at all. Switching to a detached_IframeDocumentmakes the API behave per spec; callers that were silently corrupting the live<body>(jQuery feature-detection) now operate on the sandbox as intended.createHTMLDocument(...)return value as if it were the live document; the only existing test forimplementationwas the surroundingDocument.implementationshape._IframeDocumentinstance is unreferenced as soon as the caller drops it; no listeners or timers are attached.