Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactCode injection in Extensions
DescriptionCode injection in Extensions
ComponentExtensions
Sub-componentExtensions
Bug ClassLogic Error
Tracker514072462
Fix commit4b57ab62e477 (chromium/src) +142/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
for
chrome/browser/extensions/api/font_settings/font_settings_api.cc
modified
FontSettingsEventRouter
chrome/browser/extensions/api/font_settings/font_settings_api.cc
modified
FontSettingsEventRouter
chrome/browser/extensions/api/font_settings/font_settings_api.h
modified
TEST
chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc
modified
if
chrome/test/data/extensions/api_test/font_settings/standard/test.js
modified

Files Changed

  • chrome/test/data/webui/settings/appearance_fonts_page_test.ts
  • chrome/browser/extensions/api/font_settings/font_settings_api.cc
  • chrome/test/data/extensions/api_test/font_settings/standard/test.js
  • chrome/browser/resources/settings/appearance_page/appearance_fonts_page.ts
  • chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html.ts
  • chrome/browser/extensions/BUILD.gn
  • chrome/browser/extensions/api/font_settings/font_settings_api.h
  • chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc
From 4b57ab62e4777c52194ab730e6056893ced8fffc Mon Sep 17 00:00:00 2001
From: Eva Su <[email protected]>
Date: Mon, 14 Sep 2026 13:57:05 -0700
Subject: [PATCH] [Extensions] Validate font ID format in fontSettings extension API

Add input validation to FontSettingsSetFontFunction to ensure that font
identifiers passed to chrome.fontSettings.setFont conform to expected
structure and bounds before being stored in user preferences. Before
this, chrome.fontSettings.setFont did not validate the fontId parameter
before storing it in user preferences which can lead to malicious
extensions injecting arbitrary CSS into the chrome://settings/fonts
page.

Introduce IsValidFontName to verify that font names are valid UTF-8, do
not exceed 256 bytes, and contain only supported font naming characters.
Calls with invalid font IDs now fail immediately with an error.

Fixed: 514072462
Change-Id: I09c9afab707763eb52da86fe46305369185552fc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8373778
Reviewed-by: Solomon Kinard <[email protected]>
Reviewed-by: Mickey Burks <[email protected]>
Reviewed-by: Darryl James <[email protected]>
Commit-Queue: Eva Su <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1697530}
---
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index 1cb944162..d25b524 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -8,6 +8,7 @@
 
 #include <stddef.h>
 
+#include <string_view>
 #include <utility>
 
 #include "base/command_line.h"
@@ -59,6 +60,7 @@
 
 const char kSetFromIncognitoError[] =
     "Can't modify regular settings from an incognito context.";
+const char kInvalidFontIdError[] = "Invalid font ID.";
 
 // Gets the font name preference path for |generic_family| and |script|. If
 // |script| is NULL, uses prefs::kWebKitCommonScript.
@@ -103,6 +105,33 @@
 
 }  // namespace
 
+bool IsValidFontName(std::string_view font_name) {
+  if (font_name.empty()) {
+    return true;
+  }
+
+  if (font_name.size() > 256) {
+    return false;
+  }
+
+  if (!base::IsStringUTF8(font_name)) {
+    return false;
+  }
+
+  for (char c : font_name) {
+    if (static_cast<unsigned char>(c) >= 0x80) {
+      continue;
+    }
+
+    if (!(base::IsAsciiAlphaNumeric(c) || c == ' ' || c == '-' || c == '_' ||
+          c == '.' || c == '+')) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 // This class observes pref changed events on a profile and dispatches the
 // corresponding extension API events to extensions.
 class FontSettingsEventRouter {
@@ -328,6 +357,10 @@
       fonts::SetFont::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
 
+  if (!IsValidFontName(params->details.font_id)) {
+    return RespondNow(Error(kInvalidFontIdError));
+  }
+
   std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
                                               params->details.script);
 
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.h b/chrome/browser/extensions/api/font_settings/font_settings_api.h
index a2d0d5f..df4ef83 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.h
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.h
@@ -10,6 +10,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "base/memory/raw_ptr.h"
 #include "base/values.h"
@@ -25,6 +26,10 @@
 
 namespace extensions {
 
+// Returns true if the given font name contains only valid characters and valid
+// UTF-8 encoding.
+bool IsValidFontName(std::string_view font_name);
+
 class FontSettingsEventRouter;
 
 // The profile-keyed service that manages the font_settings extension API.
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html.ts b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html.ts
index 90c072bb..cd0a346 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html.ts
+++ b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html.ts
@@ -32,7 +32,7 @@
       </settings-slider>
       <div id="minimumSizeFontPreview"
           style="font-size:${this.getMinimumFontSize_()}px;
-              font-family:'${this.standardFontPref_?.value || ''}';"
+              font-family:${this.computeFontFamilyStyle_(this.standardFontPref_?.value)};"
           ?hidden="${this.getMinimumSizeHidden_()}">
         ${this.getMinimumFontSize_()}:
         $i18n{quickBrownFox}
@@ -52,7 +52,7 @@
     </div>
     <div id="standardFontPreview" class="list-item cr-padded-text"
         style="font-size:${this.defaultFontSizePref_?.value || 0}px;
-            font-family:'${this.standardFontPref_?.value || ''}';">
+            font-family:${this.computeFontFamilyStyle_(this.standardFontPref_?.value)};">
       ${this.defaultFontSizePref_?.value || 0}:
       $i18n{quickBrownFox}
     </div>
@@ -70,7 +70,7 @@
     </div>
     <div id="serifFontPreview" class="list-item cr-padded-text"
         style="font-size:${this.defaultFontSizePref_?.value || 0}px;
-            font-family:'${this.serifFontPref_?.value || ''}';">
+            font-family:${this.computeFontFamilyStyle_(this.serifFontPref_?.value)};">
       ${this.defaultFontSizePref_?.value || 0}:
       $i18n{quickBrownFox}
     </div>
@@ -88,7 +88,7 @@
     </div>
     <div id="sansSerifFontPreview" class="list-item cr-padded-text"
         style="font-size:${this.defaultFontSizePref_?.value || 0}px;
-            font-family:'${this.sansSerifFontPref_?.value || ''}';">
+            font-family:${this.computeFontFamilyStyle_(this.sansSerifFontPref_?.value)};">
       ${this.defaultFontSizePref_?.value || 0}:
       $i18n{quickBrownFox}
     </div>
@@ -106,7 +106,7 @@
     </div>
     <div id="fixedFontPreview" class="list-item cr-padded-text"
         style="font-size: ${this.defaultFixedFontSizePref_?.value || 0}px;
-            font-family: '${this.fontFamilyValueForFixed_()}';">
+            font-family:${this.computeFontFamilyStyle_(this.fontFamilyValueForFixed_())};">
       ${this.defaultFixedFontSizePref_?.value || 0}:
       $i18n{quickBrownFox}
     </div>
@@ -129,7 +129,7 @@
          layout constants. This is what the formula below tries to do. -->
     <div id="mathFontPreview" class="list-item cr-padded-text"
         style="font-size:${this.defaultFontSizePref_?.value || 0}px;
-            font-family:'${this.mathFontPref_?.value || ''}';">
+            font-family:${this.computeFontFamilyStyle_(this.mathFontPref_?.value)};">
       ${this.defaultFontSizePref_?.value || 0}:
       <math style="font: inherit;" displaystyle="true">
         <mrow>
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.ts b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.ts
index 049b4b8..94a0ff7c 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.ts
+++ b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.ts
@@ -161,6 +161,10 @@
     return this.fixedFontPref_.value;
   }
 
+  protected computeFontFamilyStyle_(fontFamily?: string): string {
+    return fontFamily ? JSON.stringify(fontFamily) : 'inherit';
+  }
+
   // SettingsViewMixinLit implementation.
   override focusBackButton() {
     this.shadowRoot.querySelector('settings-subpage')!.focusBackButton();
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc b/chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc
new file mode 100644
index 0000000..45992e0
--- /dev/null
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/font_settings/font_settings_api.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+TEST(FontSettingsApiTest, FontNameValidation) {
+  EXPECT_TRUE(IsValidFontName(""));
+  EXPECT_TRUE(IsValidFontName("Arial"));
+  EXPECT_TRUE(IsValidFontName("Times New Roman"));
+  EXPECT_TRUE(IsValidFontName("Courier New"));
+  EXPECT_TRUE(IsValidFontName("sans-serif"));
+  EXPECT_TRUE(IsValidFontName("Osaka-Mono"));
+  EXPECT_TRUE(IsValidFontName("M+ 1p"));
+  EXPECT_TRUE(IsValidFontName("custom_standard"));
+  EXPECT_TRUE(IsValidFontName("宋体"));
+
+  EXPECT_FALSE(IsValidFontName("x'; position: fixed;"));
+  EXPECT_FALSE(IsValidFontName("font;"));
+  EXPECT_FALSE(IsValidFontName("font\""));
+  EXPECT_FALSE(IsValidFontName("font'"));
+  EXPECT_FALSE(IsValidFontName("font\\"));
+  EXPECT_FALSE(IsValidFontName("font{color:red}"));
+  EXPECT_FALSE(IsValidFontName("font:10px"));
+  EXPECT_FALSE(IsValidFontName("font/*comment*/"));
+  EXPECT_FALSE(IsValidFontName("font\n"));
+  EXPECT_FALSE(IsValidFontName("font, serif"));
+  EXPECT_FALSE(IsValidFontName(std::string(300, 'a')));
+}
+
+}  // namespace extensions
diff --git a/chrome/test/data/extensions/api_test/font_settings/standard/test.js b/chrome/test/data/extensions/api_test/font_settings/standard/test.js
index c32aa22..c8c1f4b 100644
--- a/chrome/test/data/extensions/api_test/font_settings/standard/test.js
+++ b/chrome/test/data/extensions/api_test/font_settings/standard/test.js
@@ -268,4 +268,40 @@
 
     fs.clearMinimumFontSize({}, chrome.test.callbackPass());
   },
+
+  function setInvalidFontNames() {
+    const invalidNames = [
+      'invalid\'font;--x:',
+      'test"font',
+      'test{font}',
+      'test<font>',
+      'test\\font',
+      'test/font',
+      'test:font',
+      'test;font',
+      'test!font',
+      'test@font',
+      'test`font',
+      'test=font',
+      'test,font',
+      'test\nfont',
+      'test\rfont',
+      'a'.repeat(300),
+    ];
+    function testNext(index) {
+      if (index >= invalidNames.length) {
+        chrome.test.succeed();
+        return;
+      }
+      fs.setFont(
+          {
+            genericFamily: 'standard',
+            fontId: invalidNames[index],
+          },
+          chrome.test.callbackFail('Invalid font ID.', () => {
+            testNext(index + 1);
+          }));
+    }
+    testNext(0);
+  },
 ]);
diff --git a/chrome/test/data/webui/settings/appearance_fonts_page_test.ts b/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
index 75dc714b..5d6f3ef 100644
--- a/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
@@ -241,4 +241,24 @@
           `math preview contains element <${tagName}>`);
     });
   });
+
+  test('font preview special characters', async () => {
+    // Verify that special characters in font names do not modify other CSS
+    // properties.
+    prefsBrowserProxy.fakeApi.sendPrefChanges([
+      {
+        key: 'webkit.webprefs.fonts.standard.Zyyy',
+        value: 'sample\'; position: fixed; inset: 0; --custom:\'',
+      },
+    ]);
+    await microtasksFinished();
+
+    const element = fontsPage.$.standardFontPreview;
+    assertFalse(element.style.position === 'fixed');
+    assertEquals('', element.style.position);
+    assertEquals('', element.style.inset);
+    assertEquals(
+        `"sample'; position: fixed; inset: 0; --custom:'"`,
+        element.style.fontFamily);
+  });
 });
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index ee0e5870..563383f 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1923,6 +1923,7 @@
     "api/developer_private/extension_info_generator_unittest.cc",
     "api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc",
     "api/extension_action/extension_action_api_unittest.cc",
+    "api/font_settings/font_settings_api_unittest.cc",
     "api/gcm/extension_gcm_app_handler_unittest.cc",
     "api/identity/extension_token_key_unittest.cc",
     "api/identity/identity_api_unittest.cc",
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc b/chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc
new file mode 100644
index 0000000..45992e0
--- /dev/null
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/font_settings/font_settings_api.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+TEST(FontSettingsApiTest, FontNameValidation) {
+  EXPECT_TRUE(IsValidFontName(""));
+  EXPECT_TRUE(IsValidFontName("Arial"));
+  EXPECT_TRUE(IsValidFontName("Times New Roman"));
+  EXPECT_TRUE(IsValidFontName("Courier New"));
+  EXPECT_TRUE(IsValidFontName("sans-serif"));
+  EXPECT_TRUE(IsValidFontName("Osaka-Mono"));
+  EXPECT_TRUE(IsValidFontName("M+ 1p"));
+  EXPECT_TRUE(IsValidFontName("custom_standard"));
+  EXPECT_TRUE(IsValidFontName("宋体"));
+
+  EXPECT_FALSE(IsValidFontName("x'; position: fixed;"));
+  EXPECT_FALSE(IsValidFontName("font;"));
+  EXPECT_FALSE(IsValidFontName("font\""));
+  EXPECT_FALSE(IsValidFontName("font'"));
+  EXPECT_FALSE(IsValidFontName("font\\"));
+  EXPECT_FALSE(IsValidFontName("font{color:red}"));
+  EXPECT_FALSE(IsValidFontName("font:10px"));
+  EXPECT_FALSE(IsValidFontName("font/*comment*/"));
+  EXPECT_FALSE(IsValidFontName("font\n"));
+  EXPECT_FALSE(IsValidFontName("font, serif"));
+  EXPECT_FALSE(IsValidFontName(std::string(300, 'a')));
+}
+
+}  // namespace extensions
diff --git a/chrome/test/data/extensions/api_test/font_settings/standard/test.js b/chrome/test/data/extensions/api_test/font_settings/standard/test.js
index c32aa22..c8c1f4b 100644
--- a/chrome/test/data/extensions/api_test/font_settings/standard/test.js
+++ b/chrome/test/data/extensions/api_test/font_settings/standard/test.js
@@ -268,4 +268,40 @@
 
     fs.clearMinimumFontSize({}, chrome.test.callbackPass());
   },
+
+  function setInvalidFontNames() {
+    const invalidNames = [
+      'invalid\'font;--x:',
+      'test"font',
+      'test{font}',
+      'test<font>',
+      'test\\font',
+      'test/font',
+      'test:font',
+      'test;font',
+      'test!font',
+      'test@font',
+      'test`font',
+      'test=font',
+      'test,font',
+      'test\nfont',
+      'test\rfont',
+      'a'.repeat(300),
+    ];
+    function testNext(index) {
+      if (index >= invalidNames.length) {
+        chrome.test.succeed();
+        return;
+      }
+      fs.setFont(
+          {
+            genericFamily: 'standard',
+            fontId: invalidNames[index],
+          },
+          chrome.test.callbackFail('Invalid font ID.', () => {
+            testNext(index + 1);
+          }));
+    }
+    testNext(0);
+  },
 ]);
diff --git a/chrome/test/data/webui/settings/appearance_fonts_page_test.ts b/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
index 75dc714b..5d6f3ef 100644
--- a/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
@@ -241,4 +241,24 @@
           `math preview contains element <${tagName}>`);
     });
   });
+
+  test('font preview special characters', async () => {
+    // Verify that special characters in font names do not modify other CSS
+    // properties.
+    prefsBrowserProxy.fakeApi.sendPrefChanges([
+      {
+        key: 'webkit.webprefs.fonts.standard.Zyyy',
+        value: 'sample\'; position: fixed; inset: 0; --custom:\'',
+      },
+    ]);
+    await microtasksFinished();
+
+    const element = fontsPage.$.standardFontPreview;
+    assertFalse(element.style.position === 'fixed');
+    assertEquals('', element.style.position);
+    assertEquals('', element.style.inset);
+    assertEquals(
+        `"sample'; position: fixed; inset: 0; --custom:'"`,
+        element.style.fontFamily);
+  });
 });
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.