Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in CSS
DescriptionInappropriate implementation in CSS
ComponentCSS
Bug ClassLogic Error
Tracker502805170
Fix commit4353c3e32cc2 (chromium/src) +8/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • third_party/blink/renderer/core/css/css_image_set_type_value.cc
  • third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
From 4353c3e32cc28617e3867e7247eeaf76501e477c Mon Sep 17 00:00:00 2001
From: Rune Lillesveen <futhark@chromium.org>
Date: Wed, 15 Apr 2026 06:19:36 -0700
Subject: [PATCH] Escape serialization of image-set() type() if necessary

Bug: 502805170
Change-Id: I637c8929b14fa84648190eb045c1d844fc440630
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7758510
Auto-Submit: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1615100}
---

diff --git a/third_party/blink/renderer/core/css/css_image_set_type_value.cc b/third_party/blink/renderer/core/css/css_image_set_type_value.cc
index e3b8c8b..b2f64fa 100644
--- a/third_party/blink/renderer/core/css/css_image_set_type_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_set_type_value.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/css_image_set_type_value.h"
 
 #include "third_party/blink/public/common/mime_util/mime_util.h"
+#include "third_party/blink/renderer/core/css/css_markup.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -17,9 +18,9 @@
 String CSSImageSetTypeValue::CustomCSSText() const {
   StringBuilder result;
 
-  result.Append("type(\"");
-  result.Append(type_);
-  result.Append("\")");
+  result.Append("type(");
+  SerializeString(type_, result);
+  result.Append(")");
 
   return result.ReleaseString();
 }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
index ee57c85..b082da6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
@@ -265,6 +265,10 @@
     "image-set(url(example.png) 1x type('image/jpeg'))",
     'image-set(url("example.png") 1x type("image/jpeg"))'
   );
+  test_valid_value_variants(
+    'background-image',
+    'image-set(url("x") 1x type("a\\""))',
+  );
 
   test_invalid_value_variants(
     'background-image',
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
index ee57c85..b082da6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/image-set/image-set-parsing.html
@@ -265,6 +265,10 @@
     "image-set(url(example.png) 1x type('image/jpeg'))",
     'image-set(url("example.png") 1x type("image/jpeg"))'
   );
+  test_valid_value_variants(
+    'background-image',
+    'image-set(url("x") 1x type("a\\""))',
+  );
 
   test_invalid_value_variants(
     'background-image',
Loading diff…

Original Bug Report

reported by vm...@google.com

CSS mXSS via Missing Serialization Escaping in CSSImageSetTypeValue

Project Fortify, 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 without the Chrome Security team. Please see go/chrome-ai-generated-security-bugs-faq for more information.

Overview: The serialization logic for CSSImageSetTypeValue fails to escape special characters like double quotes when outputting its .cssText. This breaks the CSSOM round-trip invariant, allowing an attacker to smuggle arbitrary CSS declarations past sanitizers using CSS escape sequences. This constitutes a potential CSS mutation Cross-Site Scripting (mXSS) vulnerability.

Affected files:

  • third_party/blink/renderer/core/css/css_image_set_type_value.cc
  • third_party/blink/renderer/core/css/css_image_set_option_value.cc
  • third_party/blink/renderer/core/css/css_image_set_value.cc

Estimated timestamp from git blame: 2023-03-03

Summary

A potential CSS mutation Cross-Site Scripting (mXSS) vulnerability exists in the CSS serialization logic for the type() component of the image-set() function. The vulnerability allows an attacker to inject arbitrary CSS declarations into a stylesheet by exploiting a serialization flaw that fails to escape special characters.

Technical Details

When a CSS property containing an image-set() function is parsed, the Blink CSS tokenizer correctly decodes CSS escape sequences. For example, \22 is decoded to a literal double quote (") and \29 to a literal closing parenthesis ()).

The parsed string is stored in a CSSImageSetTypeValue object. However, when the CSSOM tree is serialized back to a string (e.g., when a script reads element.style.cssText), the CustomCSSText() method in third_party/blink/renderer/core/css/css_image_set_type_value.cc fails to properly escape this string:

String CSSImageSetTypeValue::CustomCSSText() const {
  StringBuilder result;
  result.Append("type(\"");
  result.Append(type_); // <--- Bug: Raw string appended without escaping
  result.Append("\")");
  return result.ReleaseString();
}

Unlike other string-based CSS values (such as CSSStringValue), CSSImageSetTypeValue does not use the blink::SerializeString() helper function (defined in css_markup.cc). Because it blindly concatenates the unescaped type_ string, any literal quotes or parentheses injected by the attacker are emitted raw into the serialized output.

Attack Scenario & Potential Impact

This flaw breaks the parse -> serialize -> parse idempotency invariant. Attackers can leverage this to bypass DOM sanitizers (like DOMPurify) or server-side CSS validators that rely on the safety of the browser’s CSSOM serialization.

  1. An attacker provides a payload like image-set(url(x) 1x type("a\22 \29 \29 ;color:red;--x:\22 ")) to a vulnerable application.
  2. The initial parser decodes the string safely, storing it in the CSSOM.
  3. A sanitizer reads .cssText to validate or sanitize the DOM. The serialized output becomes background-image: image-set(url("x") 1x type("a" ) ) ;color:red;--x:" "));.
  4. The sanitizer assigns the “safe” string back to an element.
  5. Upon reparsing, the literal quotes and parentheses break out of the type() and image-set() functions, causing the browser to evaluate color:red; as a new, valid CSS declaration.

This can be exploited to inject arbitrary CSS properties, leading to UI redressing or sensitive data exfiltration (e.g., via injected background-image requests based on user interaction).

Suggested Reproduction Steps

Note: These steps demonstrate the logic flaw using browser APIs; our tooling agent cannot execute code to verify the exploit directly.

  1. Open the browser console.
  2. Create a div and assign a crafted image-set payload:
    const d = document.createElement('div');
    d.style.backgroundImage = 'image-set(url(x) 1x type("a\\22 \\29 \\29 ;color:red;--x:\\22 "))';
    
  3. Read the serialized CSS text:
    console.log(d.style.cssText);
    // Output contains literal unescaped characters: background-image: image-set(url("x") 1x type("a" ) ) ;color:red;--x:" "));
    
  4. Trigger the round-trip parse by assigning the serialized text back:
    d.setAttribute('style', d.style.cssText);
    
  5. Verify that the injected property was parsed:
    console.log(d.style.color); // Expected: "red"
    

Suggested Fix

Update CSSImageSetTypeValue::CustomCSSText() to use the blink::SerializeString() function, similar to how CSSStringValue handles string serialization. This will ensure that special characters within the MIME type string are correctly escaped.

#include "third_party/blink/renderer/core/css/css_markup.h"

String CSSImageSetTypeValue::CustomCSSText() const {
  StringBuilder result;
  result.Append("type(");
  SerializeString(type_, result);
  result.Append(")");
  return result.ReleaseString();
}

Evaluated with Chrome root at commit: 661452647ddb2827305122ff3273bd5dea403f09


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.

View on issue tracker