← All issues

Cross-origin parent DOM exposed via NavigateEvent.sourceElement

A cross-origin iframe was handed a live pointer to its parent's DOM

0beaf19

LayoutTests/http/wpt/navigation-api/resources/sourceElement-cross-origin-leak-frame.html

+navigation.addEventListener("navigate", event => {
+ const source = event.sourceElement;
+ let leakedEmail = null, leakedAccountId = null;
+ if (source) {
+ try { leakedEmail = source.ownerDocument.querySelector("#profile-email").textContent; } catch (e) {}
+ try { leakedAccountId = source.ownerDocument.querySelector("#account-panel").dataset.accountId; } catch (e) {}
+ }
+ window.parent.postMessage({ sourceElementIsNull: source === null, leakedEmail, leakedAccountId }, "*");
+});

Source/WebCore/page/Navigation.cpp

+#include "NodeDocument.h"
// (fix: null sourceElement when ownerDocument not same-origin-domain with relevant document)

The Navigation API is a modern web platform API for intercepting and controlling same-document navigations. When a navigation fires, a NavigateEvent is dispatched to the navigating realm, and its sourceElement property identifies which element initiated it. The API was designed with a same-origin assumption: sourceElement is meaningful when both parties share an origin. This commit fixes the case where a parent-frame element uses target= to navigate a cross-origin child iframe: the event fires in the child's realm, but the implementation was handing the child a live reference to the parent's DOM element, with ownerDocument granting unrestricted cross-origin document traversal. The fix, placed at innerDispatchNavigateEvent — the central dispatch funnel after the form-submitter/form rewrite — nulls sourceElement when its owning document is not same-origin-domain with the navigation target, uniformly covering anchor, form, form-submitter, and download navigation paths.

Parent (origin A)                        iframe (origin B)
  <a href="..." target="iframe">           navigation.addEventListener("navigate", e => {
    [click] ──────────────────────────►      e.sourceElement            // was parent <A> element
                                             .ownerDocument             // ← full parent Document
                                             .querySelector("#email")   // ← PII extracted
                                           });

After fix (innerDispatchNavigateEvent):
  if (!sourceElement->document().isSameOriginDomain(relevantDocument))
      sourceElement = nullptr;   // ← enforced before event reaches child

This was a web-reachable Same-Origin-Policy bypass requiring no user interaction beyond normal page use — any cross-origin iframe could silently extract parent-document content (emails, account IDs, auth tokens in the DOM) whenever a parent-frame element triggered navigation into it. The WPT test had been a known baseline FAIL, meaning this shipped as a regression visible to spec-watchers.

Several directions are worth pursuing. same-origin-domain is a weaker predicate than same-origin — it respects document.domain relaxation, which lets two cooperating pages widen their origin; verify whether a parent that has set document.domain could cause the check to pass and re-expose sourceElement to an iframe the spec would not consider safe. The fix sits after the form-submitter/form rewrite, so understand whether intermediate representations or early-exit paths before this point leave sourceElement readable on the event object without the null-out having run. The test is guarded with SiteIsolationEnabled=false, suggesting the fix was not validated under process-isolated cross-origin iframes — under full site isolation the check may behave differently or the dispatch path may differ. Finally, inspect other NavigateEvent properties — destination.url, info, the AbortSignal — for analogous cross-origin leakage, since this class of bug (an API designed with same-origin assumptions dispatched cross-origin) can recur across multiple properties.