Medium watch CVSS 5.3 chrome Info Disclosure 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactObservable discrepancy in HTML
DescriptionObservable discrepancy in HTML
ComponentHTML
Bug ClassInfo Disclosure
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker517697155
Fix commit60e801e23034 (chromium/src) +236/-20
CWECWE-203
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-29 (per vendor advisory)
Fix landed2026-07-06 (public git)
Disclosed2026-08-25
Patch gap50 days — fix public in source before the advisory

Background

Autofill preview state
the transient rendering where Chrome shows suggested (not-yet-committed) autofill values in a form control, exposed via internal pseudo-elements/classes such as ::-internal-input-suggested and :-internal-autofill-previewed.
Author `@font-face` with `unicode-range`
CSS that lets a page declare a font used only for specific characters; a single-character unicode-range makes a font apply if and only if that character is present, turning font matching into a per-character oracle.
`AutofillPreviewIgnoreAuthorFont`
the new stable runtime feature gating a hardened rule set that forces built-in fonts on all autofill-preview surfaces.

Root Cause Analysis

Autofill preview text must not be styled with author-controlled fonts, because a page can define an @font-face with a narrow unicode-range and detect—via load side effects or measurable layout differences—whether specific characters appear in the preview, disclosing values the user has not yet submitted. The pre-patch html.css only pinned a generic font on input::-internal-input-suggested and textarea::-internal-input-suggested, leaving <select> autofill previews (select:-internal-autofill-previewed) and the many -webkit-datetime-edit-* fields of date/time inputs still subject to the author’s font-family. This is an observable-discrepancy/info-disclosure gap: for those uncovered controls a single-character unicode-range @font-face could reveal previewed characters.

The fix wraps a broadened rule set in @supports blink-feature(AutofillPreviewIgnoreAuthorFont) that forces font: -webkit-small-control, a generic sans-serif/monospace font-family, and normalized font-feature-settings across select, input/textarea suggestions, and every datetime-edit sub-field, so no author font can apply to preview text.

Key insight
The mistake was hard-coding the preview font only for text input/textarea while <select> and date/time input sub-fields still honored author @font-face rules; the fix extends the forced built-in font to those controls under the AutofillPreviewIgnoreAuthorFont feature, closing the font-based oracle.

Attack Path

  1. Declare a per-character font oracle A malicious page defines an @font-face with a single-character unicode-range (and observable side effects such as a distinct metric or font-load signal).
  2. Present an autofillable select or date/time control The page includes a <select> or a date/time <input> (date, month, week, time, datetime-local) styled with the author font, which the pre-patch CSS does not override in preview state.
  3. Enter preview state Autofill previews a suggested value, populating select:-internal-autofill-previewed or the -webkit-datetime-edit-* fields with the previewed characters using the author-controlled font.
  4. Observe the discrepancy The page detects whether the character-specific font applied (via layout measurement or load signal), inferring the previewed characters and thereby disclosing not-yet-submitted autofill data.

Impact Assessment

An attacker-controlled web page gains an information-disclosure side channel in the renderer: it can infer characters of autofill preview values for <select> and date/time inputs before the user commits them, by exploiting author-font matching that was still permitted on those preview surfaces. The advisory classifies this as an observable discrepancy / info disclosure in HTML; no exploited-in-the-wild status is stated. The precondition is only that the user has autofill data eligible for these control types and the page can render such a control with a crafted @font-face.

Changed Functions

FunctionChangeNotes
while
third_party/blink/web_tests/fast/forms/autofill-preview-font.html
modified
if
third_party/blink/web_tests/fast/forms/autofill-preview-font.html
modified

Files Changed

  • third_party/blink/renderer/platform/runtime_enabled_features.json5
  • third_party/blink/renderer/core/html/resources/html.css
  • third_party/blink/web_tests/fast/forms/autofill-preview-font.html

Audit Directions

  • Author styling on privileged/preview UI
    Audit UA stylesheet coverage for any internal preview or suggestion pseudo-element/class that does not force built-in fonts/colors, especially newer control types added after the original hardening.
  • `unicode-range` / `@font-face` oracles
    Treat author-controllable font matching over sensitive text (autofill, passwords, credentials previews) as a side channel; ensure such surfaces use generic families that avoid @font-face lookups.
  • Composite shadow-DOM controls
    Review multi-part controls (<select>, date/time inputs) whose sub-elements each carry their own pseudo-element styling, since hardening applied to a parent control may miss individually styleable sub-fields.
From 60e801e23034c2828079cfcc6511e9fb5322f4b8 Mon Sep 17 00:00:00 2001
From: Joey Arhar <[email protected]>
Date: Mon, 06 Jul 2026 10:18:50 -0700
Subject: [PATCH] Hard code autofill preview font for select and date/time inputs

Allowing the author to set a custom font on autofill preview text can
lead to a privacy issue.

Fixed: 517697155, 517404644, 40057398
Change-Id: I089ffe93048038f42ac4e2c18bb88668cb050b41
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7885624
Commit-Queue: Joey Arhar <[email protected]>
Reviewed-by: Joey Arhar <[email protected]>
Reviewed-by: David Baron <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1657280}
---
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index 41e7d28c..0cf08036 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -634,29 +634,84 @@
     overflow: hidden;
 }
 
-input::-internal-input-suggested,
-textarea::-internal-input-suggested {
-    font: -webkit-small-control !important;
-    /* font: -webkit-small-control resolves to Arial on every platform. Ideally
-       we'd keep Arial, but a concrete family is matched against author
-       @font-face, so a single-character unicode-range could disclose the
-       preview text; a generic family avoids that lookup. crbug.com/517710554. */
-    @supports blink-feature(AutofillPreviewGenericFontFamily) {
-      font-family: sans-serif !important;
+@supports blink-feature(AutofillPreviewIgnoreAuthorFont) {
+    input::-internal-input-suggested,
+    textarea::-internal-input-suggested {
+        font: -webkit-small-control !important;
     }
-    /* -webkit-small-control does not pin the font-feature-settings and we want
-       previews to look consistent. */
-    font-feature-settings: normal !important;
-    /* Prevent that overflow affects the scrollable area. Without this,
-       LayoutBox::*Scroll{Height,Width}() may determine the scroll width/height
-       from the scrollable area instead of from the overrides in
-       LayoutTextControl{Single,Multi}Line::Scroll{Height,Width}(). */
-    overflow: hidden !important;
-    overflow-anchor: none;
+    select:-internal-autofill-previewed,
+    input::-internal-input-suggested,
+    textarea::-internal-input-suggested {
+        /* font: -webkit-small-control resolves to Arial on every platform. Ideally
+           we'd keep Arial, but a concrete family is matched against author
+           @font-face, so a single-character unicode-range could disclose the
+           preview text; a generic family avoids that lookup. crbug.com/517710554. */
+        font-family: sans-serif !important;
+    }
+    textarea::-internal-input-suggested,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-ampm-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-day-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-hour-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-millisecond-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-minute-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-month-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-second-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-week-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-year-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-text,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-fields-wrapper {
+        font-family: monospace !important;
+    }
+    input::-internal-input-suggested,
+    textarea::-internal-input-suggested,
+    select:-internal-autofill-previewed,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-ampm-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-day-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-hour-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-millisecond-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-minute-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-month-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-second-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-week-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-year-field,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-text,
+    input:-internal-autofill-previewed::-webkit-datetime-edit-fields-wrapper {
+        /* -webkit-small-control does not pin the font-feature-settings and we want
+           previews to look consistent. */
+        font-feature-settings: normal !important;
+        /* Prevent overflow from affecting the scrollable area. Without this,
+           LayoutBox::*Scroll{Height,Width}() may determine the scroll width/height
+           from the scrollable area instead of from the overrides in
+           LayoutTextControl{Single,Multi}Line::Scroll{Height,Width}(). */
+        overflow: hidden !important;
+        overflow-anchor: none;
+    }
 }
 
-textarea::-internal-input-suggested {
-  font-family: monospace !important;
+@supports not blink-feature(AutofillPreviewIgnoreAuthorFont) {
+    input::-internal-input-suggested,
+    textarea::-internal-input-suggested {
+        font: -webkit-small-control !important;
+        /* font: -webkit-small-control resolves to Arial on every platform. Ideally
+           we'd keep Arial, but a concrete family is matched against author
+           @font-face, so a single-character unicode-range could disclose the
+           preview text; a generic family avoids that lookup. crbug.com/517710554. */
+        @supports blink-feature(AutofillPreviewGenericFontFamily) {
+          font-family: sans-serif !important;
+        }
+        /* -webkit-small-control does not pin the font-feature-settings and we want
+           previews to look consistent. */
+        font-feature-settings: normal !important;
+        /* Prevent overflow from affecting the scrollable area. Without this,
+           LayoutBox::*Scroll{Height,Width}() may determine the scroll width/height
+           from the scrollable area instead of from the overrides in
+           LayoutTextControl{Single,Multi}Line::Scroll{Height,Width}(). */
+        overflow: hidden !important;
+        overflow-anchor: none;
+    }
+    textarea::-internal-input-suggested {
+        font-family: monospace !important;
+    }
 }
 
 input[type="password" i] {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8b9ed5d5..7e3e27c 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -790,6 +790,12 @@
       status: "stable",
     },
     {
+      // Makes form control elements have a hard coded font when they are in
+      // their autofill previewed state.
+      name: "AutofillPreviewIgnoreAuthorFont",
+      status: "stable",
+    },
+    {
       name: "AutomationControlled",
       base_feature: "none",
       public: true,
diff --git a/third_party/blink/web_tests/fast/forms/autofill-preview-font.html b/third_party/blink/web_tests/fast/forms/autofill-preview-font.html
new file mode 100644
index 0000000..a42985c
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/autofill-preview-font.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:[email protected]">
+<link rel=help href="https://issues.chromium.org/issues/517697155">
+<link rel=help href="https://issues.chromium.org/issues/517404644">
+<link rel=help href="https://issues.chromium.org/issues/40057398">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+<style>
+  @font-face {
+    font-family: Ahem;
+    src: url(../../resources/Ahem.ttf);
+  }
+  .use-ahem {
+    font-family: Ahem;
+  }
+</style>
+
+<select id=my-select class=use-ahem>
+  <option value=v1>Value 1</option>
+  <option value=v2>Value 2</option>
+</select>
+
+<input id=my-date type=date class=use-ahem>
+<textarea id=my-textarea class=use-ahem></textarea>
+<input id=my-text type=text class=use-ahem>
+<input id=my-search type=search class=use-ahem>
+<input id=my-tel type=tel class=use-ahem>
+<input id=my-url type=url class=use-ahem>
+<input id=my-email type=email class=use-ahem>
+<input id=my-password type=password class=use-ahem>
+<input id=my-number type=number class=use-ahem>
+<input id=my-month type=month class=use-ahem>
+<input id=my-week type=week class=use-ahem>
+<input id=my-time type=time class=use-ahem>
+<input id=my-datetime-local type=datetime-local class=use-ahem>
+
+<script>
+function getElementByTextContent(root, text) {
+    var node = root;
+    while (node) {
+        if (node.nodeType === Node.ELEMENT_NODE && node.textContent === text)
+            return node;
+        node = traverseNextNode(node, root);
+    }
+    return null;
+}
+
+promise_test(async () => {
+  await document.fonts.ready;
+  const select = document.getElementById('my-select');
+  const initialSelectStyle = window.getComputedStyle(select);
+  assert_equals(initialSelectStyle.fontFamily, 'Ahem', 'Select should use author font initially');
+
+  internals.setSuggestedValue(select, 'v2');
+  const selectStyle = window.getComputedStyle(select);
+  assert_not_equals(selectStyle.fontFamily, 'Ahem',
+    'Select should not use author font in preview state.');
+}, 'Autofill preview should not use author fonts for select');
+
+const simpleTypes = [
+  { type: 'textarea', value: 'suggested value' },
+  { type: 'text', value: 'suggested value' },
+  { type: 'search', value: 'suggested value' },
+  { type: 'tel', value: '123-456-7890' },
+  { type: 'url', value: 'https://example.com' },
+  { type: 'email', value: '[email protected]' },
+  { type: 'password', value: 'secret' },
+  { type: 'number', value: '42' }
+];
+
+simpleTypes.forEach(({type, value}) => {
+  promise_test(async () => {
+    await document.fonts.ready;
+    const id = type === 'textarea' ? 'my-textarea' : `my-${type}`;
+    const element = document.getElementById(id);
+    const initialStyle = window.getComputedStyle(element);
+    assert_equals(initialStyle.fontFamily, 'Ahem', `${type} should use author font initially`);
+
+    internals.setSuggestedValue(element, value);
+    const shadowRoot = internals.shadowRoot(element);
+    const pseudoIds = [];
+    var node = shadowRoot;
+    while (node) {
+        if (node.nodeType === Node.ELEMENT_NODE) {
+            pseudoIds.push(internals.shadowPseudoId(node));
+        }
+        node = traverseNextNode(node, shadowRoot);
+    }
+    const suggestedElement = getElementByPseudoId(shadowRoot, '-internal-input-suggested')
+      || getElementByTextContent(shadowRoot, value);
+    const style = window.getComputedStyle(suggestedElement || element);
+    assert_not_equals(style.fontFamily, 'Ahem',
+      `${type} should not use author font in preview state. Pseudo IDs: ${pseudoIds.join(', ')}`);
+  }, `Autofill preview should not use author fonts for ${type}`);
+});
+
+const complexTypes = [
+  { type: 'month', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-month-field'
+    ], value: '2026-05' },
+  { type: 'week', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-week-field'
+    ], value: '2026-W22' },
+  { type: 'time', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-hour-field',
+      '-webkit-datetime-edit-minute-field',
+      '-webkit-datetime-edit-ampm-field'
+    ], value: '10:05' },
+  { type: 'datetime-local', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-month-field',
+      '-webkit-datetime-edit-day-field',
+      '-webkit-datetime-edit-hour-field',
+      '-webkit-datetime-edit-minute-field',
+      '-webkit-datetime-edit-ampm-field'
+    ], value: '2026-05-29T10:05' },
+  { type: 'date', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-month-field',
+      '-webkit-datetime-edit-day-field'
+    ], value: '2026-05-29' },
+];
+
+complexTypes.forEach(({type, fields, value}) => {
+  promise_test(async () => {
+    await document.fonts.ready;
+    const element = document.getElementById(`my-${type}`);
+    const shadowRoot = internals.shadowRoot(element);
+
+    const firstField = getElementByPseudoId(shadowRoot, fields[1]);
+    assert_true(!!firstField, `Should find ${fields[1]}`);
+    const initialStyle = window.getComputedStyle(firstField);
+    assert_equals(initialStyle.fontFamily, 'Ahem', `${type} field should use author font initially`);
+
+    internals.setSuggestedValue(element, value);
+
+    fields.forEach(pseudoId => {
+      const el = getElementByPseudoId(shadowRoot, pseudoId);
+      if (el) {
+        const style = window.getComputedStyle(el);
+        assert_not_equals(style.fontFamily, 'Ahem',
+          `${pseudoId} should not use author font in preview state for ${type}.`);
+      }
+    });
+  }, `Autofill preview should not use author fonts for ${type}`);
+});
+</script>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/fast/forms/autofill-preview-font.html b/third_party/blink/web_tests/fast/forms/autofill-preview-font.html
new file mode 100644
index 0000000..a42985c
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/autofill-preview-font.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:[email protected]">
+<link rel=help href="https://issues.chromium.org/issues/517697155">
+<link rel=help href="https://issues.chromium.org/issues/517404644">
+<link rel=help href="https://issues.chromium.org/issues/40057398">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+<style>
+  @font-face {
+    font-family: Ahem;
+    src: url(../../resources/Ahem.ttf);
+  }
+  .use-ahem {
+    font-family: Ahem;
+  }
+</style>
+
+<select id=my-select class=use-ahem>
+  <option value=v1>Value 1</option>
+  <option value=v2>Value 2</option>
+</select>
+
+<input id=my-date type=date class=use-ahem>
+<textarea id=my-textarea class=use-ahem></textarea>
+<input id=my-text type=text class=use-ahem>
+<input id=my-search type=search class=use-ahem>
+<input id=my-tel type=tel class=use-ahem>
+<input id=my-url type=url class=use-ahem>
+<input id=my-email type=email class=use-ahem>
+<input id=my-password type=password class=use-ahem>
+<input id=my-number type=number class=use-ahem>
+<input id=my-month type=month class=use-ahem>
+<input id=my-week type=week class=use-ahem>
+<input id=my-time type=time class=use-ahem>
+<input id=my-datetime-local type=datetime-local class=use-ahem>
+
+<script>
+function getElementByTextContent(root, text) {
+    var node = root;
+    while (node) {
+        if (node.nodeType === Node.ELEMENT_NODE && node.textContent === text)
+            return node;
+        node = traverseNextNode(node, root);
+    }
+    return null;
+}
+
+promise_test(async () => {
+  await document.fonts.ready;
+  const select = document.getElementById('my-select');
+  const initialSelectStyle = window.getComputedStyle(select);
+  assert_equals(initialSelectStyle.fontFamily, 'Ahem', 'Select should use author font initially');
+
+  internals.setSuggestedValue(select, 'v2');
+  const selectStyle = window.getComputedStyle(select);
+  assert_not_equals(selectStyle.fontFamily, 'Ahem',
+    'Select should not use author font in preview state.');
+}, 'Autofill preview should not use author fonts for select');
+
+const simpleTypes = [
+  { type: 'textarea', value: 'suggested value' },
+  { type: 'text', value: 'suggested value' },
+  { type: 'search', value: 'suggested value' },
+  { type: 'tel', value: '123-456-7890' },
+  { type: 'url', value: 'https://example.com' },
+  { type: 'email', value: '[email protected]' },
+  { type: 'password', value: 'secret' },
+  { type: 'number', value: '42' }
+];
+
+simpleTypes.forEach(({type, value}) => {
+  promise_test(async () => {
+    await document.fonts.ready;
+    const id = type === 'textarea' ? 'my-textarea' : `my-${type}`;
+    const element = document.getElementById(id);
+    const initialStyle = window.getComputedStyle(element);
+    assert_equals(initialStyle.fontFamily, 'Ahem', `${type} should use author font initially`);
+
+    internals.setSuggestedValue(element, value);
+    const shadowRoot = internals.shadowRoot(element);
+    const pseudoIds = [];
+    var node = shadowRoot;
+    while (node) {
+        if (node.nodeType === Node.ELEMENT_NODE) {
+            pseudoIds.push(internals.shadowPseudoId(node));
+        }
+        node = traverseNextNode(node, shadowRoot);
+    }
+    const suggestedElement = getElementByPseudoId(shadowRoot, '-internal-input-suggested')
+      || getElementByTextContent(shadowRoot, value);
+    const style = window.getComputedStyle(suggestedElement || element);
+    assert_not_equals(style.fontFamily, 'Ahem',
+      `${type} should not use author font in preview state. Pseudo IDs: ${pseudoIds.join(', ')}`);
+  }, `Autofill preview should not use author fonts for ${type}`);
+});
+
+const complexTypes = [
+  { type: 'month', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-month-field'
+    ], value: '2026-05' },
+  { type: 'week', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-week-field'
+    ], value: '2026-W22' },
+  { type: 'time', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-hour-field',
+      '-webkit-datetime-edit-minute-field',
+      '-webkit-datetime-edit-ampm-field'
+    ], value: '10:05' },
+  { type: 'datetime-local', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-month-field',
+      '-webkit-datetime-edit-day-field',
+      '-webkit-datetime-edit-hour-field',
+      '-webkit-datetime-edit-minute-field',
+      '-webkit-datetime-edit-ampm-field'
+    ], value: '2026-05-29T10:05' },
+  { type: 'date', fields: [
+      '-webkit-datetime-edit-text',
+      '-webkit-datetime-edit-year-field',
+      '-webkit-datetime-edit-month-field',
+      '-webkit-datetime-edit-day-field'
+    ], value: '2026-05-29' },
+];
+
+complexTypes.forEach(({type, fields, value}) => {
+  promise_test(async () => {
+    await document.fonts.ready;
+    const element = document.getElementById(`my-${type}`);
+    const shadowRoot = internals.shadowRoot(element);
+
+    const firstField = getElementByPseudoId(shadowRoot, fields[1]);
+    assert_true(!!firstField, `Should find ${fields[1]}`);
+    const initialStyle = window.getComputedStyle(firstField);
+    assert_equals(initialStyle.fontFamily, 'Ahem', `${type} field should use author font initially`);
+
+    internals.setSuggestedValue(element, value);
+
+    fields.forEach(pseudoId => {
+      const el = getElementByPseudoId(shadowRoot, pseudoId);
+      if (el) {
+        const style = window.getComputedStyle(el);
+        assert_not_equals(style.fontFamily, 'Ahem',
+          `${pseudoId} should not use author font in preview state for ${type}.`);
+      }
+    });
+  }, `Autofill preview should not use author fonts for ${type}`);
+});
+</script>
Loading diff…

Original Bug Report

reported by [email protected]

Autofill preview leak via CSS unicode-range font-face oracle on select elements

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: When Chrome autofill previews an address, the suggested select option is rendered in the inner element without a font pin override in html.css. By combining this with size containment to bypass the option pre-measure loop, a page can potentially leak the previewed option via lazy @font-face unicode-range fetches upon hover. This allows side-channel extraction of sensitive autofill fields such as the user’s country or state prior to form submission.

Affected files:

  • third_party/blink/renderer/core/html/resources/html.css
  • third_party/blink/renderer/core/html/forms/select_type.cc
  • third_party/blink/renderer/core/html/forms/menu_list_inner_element.cc
  • third_party/blink/renderer/core/html/forms/html_select_element.cc
  • third_party/blink/renderer/core/layout/layout_box.cc

Estimated timestamp from git blame: 2019-06-14

Description of the Potential Issue

Under default settings (appearance: auto), when Chrome autofill previews an address or user profile, it temporarily sets the suggested value in the <select> element’s inner shadow element (MenuListInnerElement). This inner element inherits CSS properties, including font-family, directly from the parent host <select> element.

Normally, other autofill preview surfaces (such as text inputs/textareas, or the customizable appearance: base-select variant) have their fonts pinned with !important in third_party/blink/renderer/core/html/resources/html.css to prevent author-controlled font styling:

input::-internal-input-suggested, textarea::-internal-input-suggested {
  font: -webkit-small-control !important;
  font-feature-settings: normal !important;
}

select::-internal-select-autofill-preview-text {
  font: -webkit-small-control !important;
  font-feature-settings: normal !important;
}

However, html.css lacks a font pin override for the default appearance: auto inner element (select::-internal-select-inner-element). Consequently, the default preview text renders with the author-controlled font-family from the host <select> element.

Potential Side-Channel / Information Leak Mechanism

Normally, the layout engine shapes every option text to calculate the select’s intrinsic width (via MenuListIntrinsicInlineSize in layout_box.cc), which would immediately trigger all @font-face URL fetches on page load. However, if the page sets contain: size on the <select>, the layout engine skips the option pre-measure loop entirely because size containment is active (box.ShouldApplySizeContainment() evaluates to true).

Because the pre-measure loop is bypassed, the only text shaped on layout is the actively rendered preview value in MenuListInnerElement. This triggers exactly one font-face src fetch via its corresponding unicode-range segment.

Potential Steps to Trigger

An attacker-controlled webpage could potentially perform the following steps to exploit this behavior:

  1. Provide an address-autofill form with a <select> element where each <option> label is appended with a unique Private Use Area (PUA) codepoint (e.g., U+E001 for State 1, U+E002 for State 2).
  2. Register a series of @font-face rules under a custom family name (e.g., Leak), where each rule maps a specific PUA codepoint’s unicode-range to a unique tracking probe URL:
    @font-face {
      font-family: Leak;
      src: url(/probe-state-1);
      unicode-range: U+E001;
    }
    
  3. Style the <select> with the custom font family and size containment: select { font-family: Leak; contain: size; }.
  4. When the user hovers over an Autofill suggestion, the browser previews the address and calls SetSuggestedValue(), updating the text content of MenuListInnerElement with the previewed option.
  5. The inner element renders and shapes the previewed text containing the PUA character using the inherited font family Leak.
  6. The browser initiates a lazy network request for the matching @font-face URL.
  7. The page detects the fetch event (via document.fonts.onloadingdone, a Service Worker, or server logs) and immediately identifies the user’s stored option.

Note: Since our automated security analysis tooling does not currently have the capability to execute code or run dynamic proofs of concept, these steps are theoretical and represent a potential exploit vector.

Suggested Fix

Apply font and text-security overrides to the default select inner element when in a suggested or previewed state. This can be accomplished by adding a dedicated rule in html.css targeting the inner element pseudo-class or styling ::-internal-select-inner-element when the parent select is previewed (e.g., using a pseudo-class such as :-internal-autofill-previewed), ensuring that font: -webkit-small-control !important; and font-feature-settings: normal !important; are enforced to prevent custom author-controlled fonts from being applied during autofill previews.

Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379


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.

View on issue tracker