CVE-2026-17878
Overview
Files Changed
third_party/blink/renderer/core/css/css_font_feature_value.ccthird_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
Patch
From de9ee25bb552ef2091697a17e96355bd75cf304a Mon Sep 17 00:00:00 2001
From: Dileep Maurya <dileepmaurya@microsoft.com>
Date: Thu, 18 Jun 2026 13:07:42 -0700
Subject: [PATCH] Escape font-feature-settings tag on serialization
`CSSFontFeatureValue::CustomCSSText()` wrapped the OpenType feature tag
in double quotes without escaping its contents. A feature tag is
parsed as a 4-byte string in which each byte may be any value in
`0x20-0x7E` per OpenType, including '"' (U+0022) \ (U+005C) and '}'
(U+007D). Serialization therefore emitted unbalanced quotes, breaking
the CSSOM serialize/re-parse round-trip and allowing CSS injection
when an application copies `element.style.cssText` into a <style> rule:
a tag such as "}/* could close the surrounding rule and inject
author-controlled CSS.
Serialize the tag with `SerializeString()`, which escapes '"' and '\',
matching `CSSFontVariationValue::CustomCSSText()`.
Test: `external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html`
Bug: 522781838
Change-Id: I5d7b4f87a0f3ea5262cbfd07eeb5f77120466504
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7954618
Reviewed-by: Kevin Babbitt <kbabbitt@microsoft.com>
Commit-Queue: Dileep Maurya <dileepmaurya@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1649252}
---
diff --git a/third_party/blink/renderer/core/css/css_font_feature_value.cc b/third_party/blink/renderer/core/css/css_font_feature_value.cc
index 98b6311..7d61ede 100644
--- a/third_party/blink/renderer/core/css/css_font_feature_value.cc
+++ b/third_party/blink/renderer/core/css/css_font_feature_value.cc
@@ -25,6 +25,7 @@
#include "third_party/blink/renderer/core/css/css_font_feature_value.h"
+#include "third_party/blink/renderer/core/css/css_markup.h"
#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -37,9 +38,7 @@
String CSSFontFeatureValue::CustomCSSText() const {
StringBuilder builder;
- builder.Append('"');
- builder.Append(tag_);
- builder.Append('"');
+ SerializeString(tag_, builder);
// Omit the value if it's 1 as 1 is implied by default.
if (!value_->IsNumericLiteralValue() ||
ClampTo<int>(To<CSSNumericLiteralValue>(*value_).ClampedDoubleValue()) !=
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
index 9ed4b5c..d35771c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
@@ -22,6 +22,11 @@
test_valid_value('font-feature-settings', '"PKRN"');
+// A feature tag may contain any byte in 0x20-0x7E per OpenType; quotes and
+// backslashes must be escaped on serialization.
+test_valid_value('font-feature-settings', '"\\22\\7d\\2f\\2a"', '"\\"}/*"');
+test_valid_value('font-feature-settings', '"\\22\\22\\22\\22"', '"\\"\\"\\"\\""');
+
test_valid_value('font-feature-settings', '"dlig" 1, "smcp" on, "dlig" 0', '"dlig", "smcp", "dlig" 0');
</script>
</body>
Regression Test / PoC
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
index 9ed4b5c..d35771c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-feature-settings-valid.html
@@ -22,6 +22,11 @@
test_valid_value('font-feature-settings', '"PKRN"');
+// A feature tag may contain any byte in 0x20-0x7E per OpenType; quotes and
+// backslashes must be escaped on serialization.
+test_valid_value('font-feature-settings', '"\\22\\7d\\2f\\2a"', '"\\"}/*"');
+test_valid_value('font-feature-settings', '"\\22\\22\\22\\22"', '"\\"\\"\\"\\""');
+
test_valid_value('font-feature-settings', '"dlig" 1, "smcp" on, "dlig" 0', '"dlig", "smcp", "dlig" 0');
</script>
</body>
Original Bug Report
CSS Injection / mXSS via unescaped quotes in CSSFontFeatureValue::CustomCSSText()
Flapjack, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: The CSSFontFeatureValue::CustomCSSText() method fails to escape double quotes when serializing the font-feature-settings property. An attacker can use CSS hex escapes to inject a tag containing quotes, which is valid per OpenType specs. When the element’s cssText is serialized and reused in a <style> block, it can lead to arbitrary CSS injection.
Affected files:
third_party/blink/renderer/core/css/css_font_feature_value.ccthird_party/blink/renderer/core/css/properties/css_parsing_utils.cc
Estimated timestamp from git blame: 2018-01-29
Summary
A serialization bug in CSSFontFeatureValue::CustomCSSText() allows for potential CSS injection and mutation XSS (mXSS). The method manually serializes the font feature tag_ member by wrapping it in double quotes without escaping internal characters. Because the CSS parser correctly allows characters in the range [0x20, 0x7E] for OpenType tags (including quotes and braces), an attacker can craft a 4-character tag that breaks out of the CSS string context upon serialization.
Root Cause
In third_party/blink/renderer/core/css/properties/css_parsing_utils.cc (ConsumeFontSettingsTagAndValue), the parser validates the font feature tag. It ensures the tag is exactly 4 characters long and that each character is between 0x20 and 0x7E. This range intentionally includes characters like " (0x22), \ (0x5C), and } (0x7D).
However, in third_party/blink/renderer/core/css/css_font_feature_value.cc, the CustomCSSText() function manually concatenates the tag value during serialization:
String CSSFontFeatureValue::CustomCSSText() const {
StringBuilder builder;
builder.Append('"');
builder.Append(tag_);
builder.Append('"');
// ...
Because tag_ is not properly escaped, an attacker can inject quotes that prematurely close the serialized string.
Potential Exploitation Scenario
Note: These are suggested steps based on code analysis; a working proof of concept has not been executed.
An attacker can exploit this if a web application or DOM sanitizer reads an element’s cssText and places it into a <style> block (a common pattern for CSP compliance or shadow DOM styling).
- The attacker controls an inline style and injects a 4-character payload using CSS hex escapes:
// Inject the tag `"}/*`
element.style.fontFeatureSettings = '"\\22\\7d\\2f\\2a"';
// Inject a custom property to hold the rest of the payload
element.style.setProperty('--payload', '*/ body { background: red; } /*');
-
The browser parses these successfully. When the application reads
element.style.cssText, Blink’sStylePropertySerializerserializes the properties. -
Due to the lack of escaping, the resulting
cssTextbecomes:font-feature-settings: ""}/*"; --payload: */ body { background: red; } /*; -
The application inserts this into a
<style>block, e.g.:<style>.user-rule { font-feature-settings: ""}/*"; --payload: */ body { background: red; } /*; }</style> -
When the browser parses this new
<style>block:""completes thefont-feature-settingsstring.}prematurely closes the.user-ruleCSS block./*starts a CSS comment that swallows the remainder of the first line.*/(from the custom property) closes the comment.- The attacker’s injected CSS
body { background: red; }is evaluated as active CSS in the global scope.
Impact
This vulnerability can be leveraged to bypass application-level CSS sanitizers that rely on the browser’s CSS serialization. This can lead to CSS-based data exfiltration or UI spoofing.
Suggested Fix
In third_party/blink/renderer/core/css/css_font_feature_value.cc, use SerializeString(tag_, builder) (available via third_party/blink/renderer/core/css/css_markup.h) instead of manually appending quotes. This ensures the tag is properly escaped, matching the correct behavior already implemented in CSSFontVariationValue::CustomCSSText().
Evaluated with Chrome root at commit: 2155cb00003ec35716a76ed3246eae995f87b7ff
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.