CVE-2025-31205 in 60 seconds

A website may load a stylesheet from another origin for rendering, but the same-origin policy should stop JavaScript from reading its CSS rules. CVE-2025-31205 broke that boundary after a lifecycle change: script could keep a reference to a cross-origin CSSStyleSheet, remove its owning element from the document, and then ask WebKit for the rules.

The attack path was:

malicious website
  -> load a cross-origin stylesheet
  -> keep the CSSStyleSheet object and remove its owner element
  -> ownerDocument() becomes null
  -> WebKit's fallback returns "allowed"
  -> read or modify rules that should remain cross-origin

Apple describes the impact as cross-origin data exfiltration. The bug did not make the stylesheet same-origin; WebKit lost the document context used for the comparison and interpreted “unknown” as “allow.”

What are the same-origin policy, CSSOM, and origin-clean state?

The same-origin policy prevents script from freely reading resources belonging to a different scheme, host, or port. A page can often use a cross-origin stylesheet to render, while the CSS Object Model (CSSOM) still denies access to its rule list. Rendering permission and script-readable data are different capabilities.

WebKit’s CSSStyleSheet::canAccessRules() decides whether JavaScript may read or modify those rules. An origin-clean value can preserve the authorization result with the stylesheet object. Without that stored state, the old fallback tried to recover the owning Document and compare its origin with the stylesheet URL.

What happened?

JavaScript could retain a stylesheet obtained from a <link>, <style>, or imported sheet and then remove the owning structure from the document. The stylesheet object survived, but ownerDocument() returned null. In the vulnerable code, that missing document caused canAccessRules() to return true, permitting reads and rule changes without an origin comparison.

WebKit reversed the unsafe default to false, propagated explicit origin-clean state through more construction paths, and made insertRule() and deleteRule() use the same access decision. The new layout tests cover attached and detached same-origin, cross-origin, CORS-enabled, redirected, and imported stylesheets.

Who was actually affected?

Apple fixed the issue in Safari 18.5 and corresponding May 2025 platform updates. The Apple advisory lists Safari on macOS Ventura and Sonoma and states that a malicious website could exfiltrate data cross-origin. WebKitGTK and WPE WebKit fixed the corresponding issue in 2.48.2. Exposure required a vulnerable WebKit build and a page able to drive the affected stylesheet lifecycle; no browser extension or local account was required for the web-content entry point.

What I verified

  • In the vulnerable branch, a missing ownerDocument() ended the check with return true; the patch changes that exact fallback to return false.
  • The change adds an explicit origin-clean value so the access decision can survive after the stylesheet loses its document owner.
  • insertRule() and deleteRule() now consult the same access decision used for reading rules.
  • I inspected the added layout tests. They cover same-origin and cross-origin sheets while attached and detached, plus imported sheets; denied operations are expected to raise SecurityError.
  • This is a static reproduction of the security decision and its fix. I inspected the patch and regression cases; I did not claim discovery or run an exfiltration exploit.

Deep dive: the security decision had two paths

WebKit did not always calculate stylesheet access from scratch. When a CSSStyleSheet was created with an explicit origin-clean value, canAccessRules() could use that recorded result. Otherwise, the method used a fallback:

  1. read the stylesheet’s base URL;
  2. find the document that owned the stylesheet;
  3. compare the document’s security origin with the stylesheet URL;
  4. decide whether script could read or modify the rules.

The dangerous branch sat between steps two and three. If there was no owner document, the method returned true. That answer converted “I no longer have enough context to compare origins” into “access is allowed.”

VULNERABLE FALLBACK CSSStyleSheetorigin flag absent ownerDocument()which page owns it? No ownerreturn true Cross-origin rulesread / insert / delete Origin comparisonnormal attached path The object survived. The context required to authorize access did not.
The vulnerable branch did not confuse two origins. It skipped the comparison when ownership context was missing.

Detaching the element changed context, not the stylesheet’s origin

The public commit describes two routes to the ownerless state. Script could keep a reference to a stylesheet obtained through a <link> or <style> element and then remove that element from the document. A similar lifetime transition was possible for a stylesheet reached through an @import rule when the containing stylesheet was removed.

The security property should survive that lifecycle change. Detaching a DOM node may change rendering and ownership relationships, but it must not make previously cross-origin rules readable. The stylesheet did not become same-origin. Only the convenient object used to perform the origin comparison became unavailable.

This makes the issue a useful variant-hunting model. In browser engines, security decisions are often distributed across an object graph. A node, frame, document, loader, or execution context may carry the authority information. If the protected object outlives that context, fallback behavior becomes part of the security boundary.

The fix changes missing context to denial

The most important line in the patch reverses one default: when the fallback cannot obtain an owner document, access is denied. WebKit also strengthens the surrounding construction paths so more stylesheets carry an explicit same-origin flag, and it applies canAccessRules() consistently to rule insertion and deletion as well as reading.

BEFORE Detached sheetowner missing Fallbackmissing means allow Rules exposedcross-origin data AFTER Detached sheetorigin state retained Explicit stateor deny on unknown SecurityErrorboundary preserved
The fix uses two complementary controls: preserve origin state where possible and fail closed when the state cannot be recovered.

This combination matters. Changing only null to false would protect cross-origin sheets but could break legitimate detached same-origin stylesheets created without an explicit flag. Recording the origin result at construction reduces dependence on a later owner lookup; denial remains the safe fallback when that record is absent.

Variant analysis: search for lost authority context

The reusable pattern is broader than CSS:

A protected object remains reachable after the object that supplied its security context has been detached, destroyed, navigated, or replaced.

A focused WebKit or browser-engine review can search for access-control helpers that retrieve an owner or context through nullable relationships. High-value candidates include code shaped like:

context = object.ownerDocument() | frame() | scriptExecutionContext() | page()
if context is missing:
    allow, skip check, or return a permissive default

Not every such branch is a vulnerability. Some objects are intentionally safe when detached. Candidates become meaningful only when all three conditions hold:

  • the surviving object still exposes data or state-changing operations;
  • the missing context previously supplied origin, permission, or principal information;
  • an attacker can deliberately trigger the lifecycle transition while retaining the object.
VARIANT-HUNTING WORKFLOW Protected objectdata or mutation Lifecycle shiftdetach / navigate Context missingwhat is default? Comparebefore / after Positive controlsame-origin works Negative controlcross-origin denied A crash is not required: the assertion is the authorization result.
Variant analysis starts from the failed invariant, then uses matched positive and negative controls to distinguish a security fix from ordinary lifecycle behavior.

Safe validation does not need real cross-origin data

WebKit’s added layout tests provide the right model. A controlled harness can serve two synthetic origins, each containing non-sensitive test CSS. The test records access while the stylesheet is attached, removes the owning element, and repeats the same operation through the retained object.

The minimum matrix should include:

  • same-origin stylesheet, attached and detached: access remains available;
  • cross-origin stylesheet without CORS, attached and detached: access throws SecurityError;
  • cross-origin stylesheet with successful CORS: access follows the explicit origin-clean state;
  • imported stylesheet after its parent is detached: the same rules remain enforced;
  • cssRules, insertRule(), and deleteRule(): all use the same access decision.

This proves the boundary without reading a third party’s content. It also avoids an incomplete test that checks only cssRules while mutation methods follow a different path.

Evidence matrix

SignalWhat it provesWhat it does not proveMy check
Apple maps CVE-2025-31205 to Bugzilla 290992The vendor connects the CVE to WebKit’s cross-origin data impactThe complete discovery history or weaponized exploitI matched the Safari 18.5 advisory with the public commit
ownerDocument() == nullptr changed from allow to denyMissing ownership context was a permissive access branchThat every ownerless stylesheet was attacker controlledI inspected CSSStyleSheet::canAccessRules() before and after the change
Construction paths now supply origin-clean stateThe fix preserves valid decisions beyond document lifetimeThat all browser objects use the same lifetime modelI reviewed the call-site changes and added layout tests
Read, insert, and delete operations share the gateThe patch closes both disclosure and mutation routesThe exact data selected in any private exploitI traced all three operations to the shared access decision
WebKitGTK/WPE advisory fixes versions before 2.48.2The issue affected ports beyond SafariIdentical exploitability on every embedding applicationI compared the port advisory and fixed-version record

My conclusion

Users should update to Safari 18.5 or the corresponding fixed Apple platform release. WebKitGTK and WPE WebKit consumers should use 2.48.2 or a distributor build that explicitly backports the fix. Version strings alone can mislead when distributions backport patches, so the package security record is the deciding source.

My conclusion is that the bug was not simply “detached CSS.” The unsafe part was the fallback that treated missing ownership information as permission. When an object outlives the context that authorized it, the implementation must carry the earlier decision forward or deny the operation. A null context is a reason to stop, not a successful origin comparison.

Public sources

Sources & limits

Evidence used for this analysis

Sources checkedAugust 24, 2026

Version and exploitation status can change; follow the linked vendor records.

Review statusAuthor review complete

The author completed a technical review. This does not, by itself, claim lab reproduction.

◈ Cite This ResearchBibTeX · Markdown

Reference this analysis, root-cause teardown, or vulnerability discovery in your technical reports or academic publications:

@misc{jankesec_apple_webkit_orphaned_stylesheet_cve_2025_31205_2026,
  author       = {Sevban D\"{o}nmez},
  title        = {CVE-2025-31205: How a Detached Stylesheet Exposed Cross-Origin Data},
  year         = {2026},
  howpublished = {\url{https://jankesec.com/research/apple-webkit-orphaned-stylesheet-cve-2025-31205/}},
  note         = {jankesec technical security research (CVE-2025-31205)}
}