CVE-2026-112997
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/core/css/css_uri_value.cc |
modified | |
ifthird_party/blink/renderer/core/svg/svg_resource.cc |
modified |
Files Changed
third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.cssthird_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.pythird_party/blink/renderer/core/svg/svg_resource.ccthird_party/blink/renderer/core/css/css_uri_value.ccthird_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.htmlthird_party/blink/renderer/core/svg/svg_resource.h
Patch
From f4dce9c0b732f5f4333933a72b3b1a3f48762d41 Mon Sep 17 00:00:00 2001
From: Rune Lillesveen <futhark@chromium.org>
Date: Tue, 15 Sep 2026 07:24:07 -0700
Subject: [PATCH] Pass referrer to ExternalSVGResourceDocumentContent
The document loading the stylesheet would otherwise leak as a referrer
to the SVG request.
Bug: 560664595
Change-Id: I5952fbf2282384384a8d7cebd7f63aa12b6ac16c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8410307
Reviewed-by: Fredrik Söderquist <fs@opera.com>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1698068}
---
diff --git a/third_party/blink/renderer/core/css/css_uri_value.cc b/third_party/blink/renderer/core/css/css_uri_value.cc
index 4f353389..c7cbbae 100644
--- a/third_party/blink/renderer/core/css/css_uri_value.cc
+++ b/third_party/blink/renderer/core/css/css_uri_value.cc
@@ -20,7 +20,7 @@
SVGResource* CSSURIValue::EnsureResourceReference() const {
if (!resource_) {
resource_ = MakeGarbageCollected<ExternalSVGResourceDocumentContent>(
- AbsoluteUrl(), UrlData().GetModifiers());
+ AbsoluteUrl(), UrlData().GetModifiers(), UrlData().GetReferrer());
}
return resource_.Get();
}
diff --git a/third_party/blink/renderer/core/svg/svg_resource.cc b/third_party/blink/renderer/core/svg/svg_resource.cc
index 5c73b0ac..242c83b1 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.cc
+++ b/third_party/blink/renderer/core/svg/svg_resource.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/svg/svg_resource.h"
#include "services/network/public/mojom/content_security_policy.mojom-blink.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/id_target_observer.h"
@@ -235,8 +236,9 @@
ExternalSVGResourceDocumentContent::ExternalSVGResourceDocumentContent(
const KURL& url,
- const CSSUrlRequestModifiers& modifiers)
- : url_(url), modifiers_(modifiers) {}
+ const CSSUrlRequestModifiers& modifiers,
+ const Referrer& referrer)
+ : url_(url), modifiers_(modifiers), referrer_(referrer) {}
void ExternalSVGResourceDocumentContent::Load(
Document& document,
@@ -252,10 +254,15 @@
options.initiator_info.name = fetch_initiator_type_names::kCSS;
FetchParameters params(ResourceRequest(url_), options);
+ ResourceRequest& resource_request = params.MutableResourceRequest();
if (modifiers_.referrer_policy) {
- params.MutableResourceRequest().SetReferrerPolicy(
- *modifiers_.referrer_policy);
+ resource_request.SetReferrerPolicy(*modifiers_.referrer_policy);
+ } else {
+ resource_request.SetReferrerPolicy(
+ ReferrerUtils::MojoReferrerPolicyResolveDefault(
+ referrer_.referrer_policy));
}
+ resource_request.SetReferrerString(referrer_.referrer);
CrossOriginAttributeValue effective_cross_origin =
modifiers_.cross_origin != kCrossOriginAttributeNotSet
diff --git a/third_party/blink/renderer/core/svg/svg_resource.h b/third_party/blink/renderer/core/svg/svg_resource.h
index 7f5bc2e..0967b8a8 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.h
+++ b/third_party/blink/renderer/core/svg/svg_resource.h
@@ -158,7 +158,8 @@
public:
explicit ExternalSVGResourceDocumentContent(
const KURL&,
- const CSSUrlRequestModifiers& modifiers);
+ const CSSUrlRequestModifiers& modifiers,
+ const Referrer& referrer);
void Load(Document&, CrossOriginAttributeValue) override;
void LoadWithoutCSP(Document&) override;
@@ -177,6 +178,7 @@
Member<SVGResourceDocumentContent> document_content_;
KURL url_;
CSSUrlRequestModifiers modifiers_;
+ Referrer referrer_;
};
// External resource reference (see SVGResource) with an ImageResourceContent
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
new file mode 100644
index 0000000..9a85588
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>referrerpolicy=no-referrer should not leak referrer across stylesheet into svg resource fetch</title>
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<link rel="stylesheet" href="filter.css?pipe=sub" referrerpolicy="no-referrer">
+<p>Test passes if there is a filled green square.</p>
+<div id="target" style="width:100px;height:100px;background-color:red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
new file mode 100644
index 0000000..55b90667
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
@@ -0,0 +1,3 @@
+#target {
+ filter: url("filter.py#make-solid-green" cross-origin(anonymous) referrer-policy(unsafe-url));
+}
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
new file mode 100644
index 0000000..73fcb0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
@@ -0,0 +1,38 @@
+# Test runs following request chain:
+#
+# filter-no-referrer.html -> filter.css -> filter.py
+#
+# filter.py returns a filter resource to turn a rectangle green in
+# filter-no-referrer.html if the referer header passed to filter.py does not
+# contains "no-referrer". filter-no-referrer.html sets referrerpolicy to
+# no-referrer, so that should definitely not leak to the svg resource request
+# here.
+#
+def main(request, response):
+ response.add_required_headers = False
+ response.writer.write_status(200)
+ response.writer.write_header(b"access-control-allow-origin", b"*")
+ response.writer.write_header(b"content-type", b"image/svg+xml")
+ response.writer.write_header(b"cache-control",
+ b"no-cache; must-revalidate")
+
+ if request.headers.get(b'referer',
+ b'').decode("utf-8").find('no-referrer') == -1:
+ body = (
+ b'<svg xmlns="http://www.w3.org/2000/svg">'
+ b' <defs>'
+ b' <filter id="make-solid-green" color-interpolation-filters="sRGB">'
+ b' <feColorMatrix type="matrix" values="'
+ b' 0 0 0 0 0'
+ b' 0 0 0 0 0.502'
+ b' 0 0 0 0 0'
+ b' 0 0 0 1 0" />'
+ b' </filter>'
+ b' </defs>'
+ b'</svg>')
+ else:
+ body = b""
+
+ response.writer.write_header(b"content-length", len(body))
+ response.writer.end_headers()
+ response.writer.write(body)
Regression Test / PoC
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
new file mode 100644
index 0000000..9a85588
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter-no-referrer.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>referrerpolicy=no-referrer should not leak referrer across stylesheet into svg resource fetch</title>
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<link rel="stylesheet" href="filter.css?pipe=sub" referrerpolicy="no-referrer">
+<p>Test passes if there is a filled green square.</p>
+<div id="target" style="width:100px;height:100px;background-color:red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
new file mode 100644
index 0000000..55b90667
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.css
@@ -0,0 +1,3 @@
+#target {
+ filter: url("filter.py#make-solid-green" cross-origin(anonymous) referrer-policy(unsafe-url));
+}
diff --git a/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
new file mode 100644
index 0000000..73fcb0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/referrer-policy/css-integration/svg/filter.py
@@ -0,0 +1,38 @@
+# Test runs following request chain:
+#
+# filter-no-referrer.html -> filter.css -> filter.py
+#
+# filter.py returns a filter resource to turn a rectangle green in
+# filter-no-referrer.html if the referer header passed to filter.py does not
+# contains "no-referrer". filter-no-referrer.html sets referrerpolicy to
+# no-referrer, so that should definitely not leak to the svg resource request
+# here.
+#
+def main(request, response):
+ response.add_required_headers = False
+ response.writer.write_status(200)
+ response.writer.write_header(b"access-control-allow-origin", b"*")
+ response.writer.write_header(b"content-type", b"image/svg+xml")
+ response.writer.write_header(b"cache-control",
+ b"no-cache; must-revalidate")
+
+ if request.headers.get(b'referer',
+ b'').decode("utf-8").find('no-referrer') == -1:
+ body = (
+ b'<svg xmlns="http://www.w3.org/2000/svg">'
+ b' <defs>'
+ b' <filter id="make-solid-green" color-interpolation-filters="sRGB">'
+ b' <feColorMatrix type="matrix" values="'
+ b' 0 0 0 0 0'
+ b' 0 0 0 0 0.502'
+ b' 0 0 0 0 0'
+ b' 0 0 0 1 0" />'
+ b' </filter>'
+ b' </defs>'
+ b'</svg>')
+ else:
+ body = b""
+
+ response.writer.write_header(b"content-length", len(body))
+ response.writer.end_headers()
+ response.writer.write(body)
Original Bug Report
External stylesheet SVG filters can disclose the embedding document URL despite `no-referrer`
Report description
External stylesheet SVG filters can disclose the embedding document URL despite no-referrer
Bug location
Where do you want to report your vulnerability?
Chrome VRP – Report security issues affecting the Chrome browser. See program rules
Which URL (or repository) have you found the vulnerability in?
The problem
Please describe the technical details of the vulnerability
CSSURIValue::EnsureResourceReference() drops the referrer stored in
CSSUrlData when it creates an ExternalSVGResourceDocumentContent. The SVG
loader applies a referrer-policy() URL modifier, but it does not set the
request’s referrer string. The remaining client-referrer sentinel is therefore
resolved against the embedding document instead of the external stylesheet.
This differs from ordinary CSS images. CSSImageValue::PrepareFetch() sets the
request’s referrer string from CSSUrlData::GetReferrer(). Its source comment
also states that this value is the stylesheet URL for an external stylesheet,
including when a URL modifier overrides the policy.
An attacker can use the mismatch as follows:
- A victim page with a sensitive value in its path or query loads a cross-origin stylesheet controlled by the attacker.
- The page and its
<link>both useno-referrer, so the stylesheet request does not disclose the page URL. - The stylesheet applies an external SVG filter with
cross-origin(anonymous) referrer-policy(unsafe-url)to an element. - Chrome requests the attacker’s SVG with the full embedding document URL in
the
Refererheader.
The attached PoC uses two loopback origins. Port 8891 is the victim and port 8892 is the stylesheet and SVG origin. The server records only synthetic request metadata.
Tested with stock Google Chrome Stable 152.0.7977.83 on Windows 11. The
browser binary was
C:\Program Files\Google\Chrome\Application\chrome.exe, SHA-256
CAF423E184F0BCEFE2EE5BEF40539A3C005C63BEB44E3321C0834929A13AF733.
Reproduction:
- Place
poc.htmlandserver.pyin the same directory. - Run
py -3 server.py. - Open
http://127.0.0.1:8891/victim/DOC_SECRET?token=URL_CANARYin Chrome. - Click
Run complete matrix.
The page reports PASS and shows:
| Case | Stylesheet Referer |
Nested-resource Referer |
|---|---|---|
SVG filter with unsafe-url |
none | full victim URL, including URL_CANARY |
image with unsafe-url |
none | external stylesheet URL |
SVG filter with no-referrer |
none | none |
The image case confirms the expected external-stylesheet referrer source. The
no-referrer filter case confirms that the server is not recovering the URL
through another channel. All three stylesheet requests omit Referer.
Expected behavior: the SVG-filter request should use the external stylesheet
URL as its referrer source before applying the URL-level policy, as the image
request does. A narrow fix is to preserve CSSUrlData::GetReferrer() when
creating the external SVG resource and set that referrer string on its
ResourceRequest.
Relevant source at the tested revision:
CSSURIValue::EnsureResourceReference()ExternalSVGResourceDocumentContent::Load()CSSImageValue::PrepareFetch()control path
Impact analysis
The attacker prerequisite is control of a cross-origin stylesheet loaded by the victim page. This can occur for a third-party stylesheet provider or after that provider is compromised.
The observed consequence is disclosure of the full embedding document URL to
the stylesheet provider even when both the document and stylesheet link use
no-referrer. The path and query can contain capability URLs, reset or login
tokens, share identifiers, document identifiers, or other application data.
The PoC demonstrates the browser primitive with a synthetic URL_CANARY; it
does not access third-party data.
No script execution, user interaction, permission, browser flag, renderer compromise, or custom build is required.
The cause
What version of Chrome have you found the security issue in?
152.0.7977.83 Stable
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?
caveeroo
- http://127.0.0.1:8891/victim/DOC_SECRET?token=URL_CANARY
- https://bughunters.google.com/about/rules/5745167867576320/chrome-vulnerability-reward-program-rules
- https://chromium.googlesource.com/chromium/src/+/aac36664dea8b69305103d736d14d413af1d33e2/third_party/blink/renderer/core/css/css_image_value.cc#51
- https://chromium.googlesource.com/chromium/src/+/aac36664dea8b69305103d736d14d413af1d33e2/third_party/blink/renderer/core/css/css_uri_value.cc#20
- https://chromium.googlesource.com/chromium/src/+/aac36664dea8b69305103d736d14d413af1d33e2/third_party/blink/renderer/core/svg/svg_resource.cc#241