CVE-2026-17777
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fcomponents/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc |
modified |
Files Changed
components/autofill/core/browser/foundations/browser_autofill_manager.cccomponents/autofill/core/browser/foundations/browser_autofill_manager_unittest.cccomponents/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cccomponents/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
Patch
From fe5635a0470fa54a8887a106aae42cc5f58af0e3 Mon Sep 17 00:00:00 2001
From: Piotr Kotynia <piotrkotynia@google.com>
Date: Mon, 22 Jun 2026 03:08:47 -0700
Subject: [PATCH] [Autofill] Exclude standalone CVC fields from Autocomplete history
Standalone CVC fields were not correctly excluded from Autocomplete
history because guards only checked for CREDIT_CARD_VERIFICATION_CODE
and missed CREDIT_CARD_STANDALONE_VERIFICATION_CODE. This CL prevents
saving and suppresses suggestions for standalone CVC fields in
BrowserAutofillManager and AutocompleteSuggestionGenerator, and adds
unit tests for both.
Bug: 513462236
Change-Id: I8686c4143b5ba2b3cf53f2b8682aef41b2052064
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7852639
Reviewed-by: Dominic Battré <battre@chromium.org>
Reviewed-by: Florian Leimgruber <fleimgruber@google.com>
Commit-Queue: Piotr Kotynia <piotrkotynia@google.com>
Cr-Commit-Position: refs/heads/main@{#1650215}
---
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index f45e663..9feda74 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -643,8 +643,9 @@
form_structure,
[&](const std::unique_ptr<AutofillField>& autofill_field) {
FormFieldData field = *autofill_field;
- if (autofill_field->Type().GetCreditCardType() ==
- CREDIT_CARD_VERIFICATION_CODE) {
+ FieldType cc_type = autofill_field->Type().GetCreditCardType();
+ if (cc_type == CREDIT_CARD_VERIFICATION_CODE ||
+ cc_type == CREDIT_CARD_STANDALONE_VERIFICATION_CODE) {
// However, if Autofill has recognized a field as CVC, that shouldn't
// be saved.
field.set_should_autocomplete(false);
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
index 598d22b..39cbae5 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -4321,6 +4321,26 @@
EXPECT_FALSE(form_seen_by_ahm.fields()[1].should_autocomplete());
EXPECT_TRUE(form_seen_by_ahm.fields()[2].should_autocomplete());
}
+
+// Test that inputs detected to be standalone CVC inputs are forced to
+// !should_autocomplete for SingleFieldFillRouter::OnWillSubmitForm.
+TEST_F(BrowserAutofillManagerTest, DontSaveStandaloneCvcInAutocompleteHistory) {
+ FormData form_seen_by_ahm;
+ EXPECT_CALL(single_field_fill_router(),
+ OnWillSubmitForm(_, _, /*is_autocomplete_enabled=*/true))
+ .WillOnce(SaveArg<0>(&form_seen_by_ahm));
+
+ FormData form = test::GetFormData(
+ {.fields = {{.role = CREDIT_CARD_STANDALONE_VERIFICATION_CODE,
+ .value = u"123"}}});
+ autofill_manager().AddSeenForm(form,
+ {CREDIT_CARD_STANDALONE_VERIFICATION_CODE});
+ FormSubmitted(form);
+
+ ASSERT_EQ(1u, form_seen_by_ahm.fields().size());
+ EXPECT_FALSE(form_seen_by_ahm.fields()[0].should_autocomplete());
+}
+
// Test that autofilled loyalty card fields are forced to !should_autocomplete.
TEST_F(BrowserAutofillManagerTest,
DontSaveAutofilledLoyaltyCardsInAutocompleteHistory) {
diff --git a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc
index a9af104..584d90e 100644
--- a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cc
@@ -99,15 +99,15 @@
}
// Do not offer autocomplete suggestions for credit card number, cvc, and
- // expiration date related fields. Standalone cvc fields (used to
- // re-authenticate the use of a credit card the website has on file) will be
- // handled separately because those have the field type
- // CREDIT_CARD_STANDALONE_VERIFICATION_CODE.
+ // expiration date related fields, including standalone CVC fields (used to
+ // re-authenticate the use of a credit card the website has on file).
if (FieldType type = trigger_autofill_field
? trigger_autofill_field->Type().GetCreditCardType()
: UNKNOWN_TYPE;
data_util::IsCreditCardExpirationType(type) ||
- type == CREDIT_CARD_VERIFICATION_CODE || type == CREDIT_CARD_NUMBER) {
+ type == CREDIT_CARD_VERIFICATION_CODE ||
+ type == CREDIT_CARD_STANDALONE_VERIFICATION_CODE ||
+ type == CREDIT_CARD_NUMBER) {
std::move(callback).Run({SuggestionDataSource::kAutocomplete, {}});
return;
}
diff --git a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
index 39fea4aa..7dcc444 100644
--- a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
@@ -11,6 +11,7 @@
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/foundations/test_autofill_client.h"
+#include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry.h"
#include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h"
@@ -187,6 +188,29 @@
}
TEST_F(AutocompleteSuggestionGeneratorTest,
+ CreditCardStandaloneCvcField_NoSuggestions) {
+ FormData form = test::GetFormData(
+ {.fields = {{.role = CREDIT_CARD_STANDALONE_VERIFICATION_CODE}}});
+ test_api(form).field(0).set_should_autocomplete(true);
+ FormStructure form_structure(form);
+ form_structure.field(0)->SetTypeTo(
+ AutofillType(CREDIT_CARD_STANDALONE_VERIFICATION_CODE),
+ /*source=*/std::nullopt);
+
+ base::MockCallback<
+ base::OnceCallback<void(SuggestionGenerator::ReturnedSuggestions)>>
+ suggestions_generated_callback;
+
+ EXPECT_CALL(*web_data_service(), GetFormValuesForElementName).Times(0);
+ EXPECT_CALL(suggestions_generated_callback,
+ Run(Pair(SuggestionGenerator::SuggestionDataSource::kAutocomplete,
+ IsEmpty())));
+ generator().GenerateSuggestions(form, form.fields()[0], &form_structure,
+ form_structure.field(0), client(),
+ suggestions_generated_callback.Get());
+}
+
+TEST_F(AutocompleteSuggestionGeneratorTest,
GenerateAutocompleteSuggestionsWithAtMemoryButtonEnabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
Regression Test / PoC
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
index 598d22b..39cbae5 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -4321,6 +4321,26 @@
EXPECT_FALSE(form_seen_by_ahm.fields()[1].should_autocomplete());
EXPECT_TRUE(form_seen_by_ahm.fields()[2].should_autocomplete());
}
+
+// Test that inputs detected to be standalone CVC inputs are forced to
+// !should_autocomplete for SingleFieldFillRouter::OnWillSubmitForm.
+TEST_F(BrowserAutofillManagerTest, DontSaveStandaloneCvcInAutocompleteHistory) {
+ FormData form_seen_by_ahm;
+ EXPECT_CALL(single_field_fill_router(),
+ OnWillSubmitForm(_, _, /*is_autocomplete_enabled=*/true))
+ .WillOnce(SaveArg<0>(&form_seen_by_ahm));
+
+ FormData form = test::GetFormData(
+ {.fields = {{.role = CREDIT_CARD_STANDALONE_VERIFICATION_CODE,
+ .value = u"123"}}});
+ autofill_manager().AddSeenForm(form,
+ {CREDIT_CARD_STANDALONE_VERIFICATION_CODE});
+ FormSubmitted(form);
+
+ ASSERT_EQ(1u, form_seen_by_ahm.fields().size());
+ EXPECT_FALSE(form_seen_by_ahm.fields()[0].should_autocomplete());
+}
+
// Test that autofilled loyalty card fields are forced to !should_autocomplete.
TEST_F(BrowserAutofillManagerTest,
DontSaveAutofilledLoyaltyCardsInAutocompleteHistory) {
diff --git a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
index 39fea4aa..7dcc444 100644
--- a/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/autocomplete_suggestion_generator_unittest.cc
@@ -11,6 +11,7 @@
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/foundations/test_autofill_client.h"
+#include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry.h"
#include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h"
@@ -187,6 +188,29 @@
}
TEST_F(AutocompleteSuggestionGeneratorTest,
+ CreditCardStandaloneCvcField_NoSuggestions) {
+ FormData form = test::GetFormData(
+ {.fields = {{.role = CREDIT_CARD_STANDALONE_VERIFICATION_CODE}}});
+ test_api(form).field(0).set_should_autocomplete(true);
+ FormStructure form_structure(form);
+ form_structure.field(0)->SetTypeTo(
+ AutofillType(CREDIT_CARD_STANDALONE_VERIFICATION_CODE),
+ /*source=*/std::nullopt);
+
+ base::MockCallback<
+ base::OnceCallback<void(SuggestionGenerator::ReturnedSuggestions)>>
+ suggestions_generated_callback;
+
+ EXPECT_CALL(*web_data_service(), GetFormValuesForElementName).Times(0);
+ EXPECT_CALL(suggestions_generated_callback,
+ Run(Pair(SuggestionGenerator::SuggestionDataSource::kAutocomplete,
+ IsEmpty())));
+ generator().GenerateSuggestions(form, form.fields()[0], &form_structure,
+ form_structure.field(0), client(),
+ suggestions_generated_callback.Get());
+}
+
+TEST_F(AutocompleteSuggestionGeneratorTest,
GenerateAutocompleteSuggestionsWithAtMemoryButtonEnabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
Original Bug Report
Cross-origin leak of Standalone CVCs via Autocomplete history
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: A logic flaw in Chrome’s Autofill system fails to exclude standalone CVC fields from the Autocomplete history. This allows sensitive Card Verification Codes (CVCs) to be persisted in plaintext and subsequently disclosed to cross-origin sites that use matching input field names.
Affected files:
components/autofill/core/browser/foundations/browser_autofill_manager.cccomponents/autofill/core/browser/suggestions/autocomplete_suggestion_generator.cccomponents/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cccomponents/autofill/core/browser/form_structure_rationalizer.cc
Estimated timestamp from git blame: 2022-08-12
Summary
A potential logic flaw in Chrome’s Autofill system allows Card Verification Codes (CVCs) from standalone fields—commonly used in re-authentication or “card-on-file” flows—to be saved into the Autocomplete history. Because Autocomplete storage is not origin-scoped and the exclusion lists are inconsistent, this sensitive data can be disclosed to cross-origin sites that present an input field with a matching name.
Technical Analysis
1. Incomplete Exclusion Guard in BrowserAutofillManager
During form submission, BrowserAutofillManager::MaybeImportFromSubmittedForm identifies fields that should be excluded from Autocomplete storage. A guard is intended to prevent CVCs from being saved by setting should_autocomplete to false. However, this guard only checks for the CREDIT_CARD_VERIFICATION_CODE type and fails to account for the CREDIT_CARD_STANDALONE_VERIFICATION_CODE type.
// components/autofill/core/browser/foundations/browser_autofill_manager.cc:631
if (autofill_field->Type().GetCreditCardType() == CREDIT_CARD_VERIFICATION_CODE) {
// Standalone CVC types (CREDIT_CARD_STANDALONE_VERIFICATION_CODE) bypass this check.
field.set_should_autocomplete(false);
}
2. Automatic Conversion to Standalone CVC
In forms used for re-authentication (e.g., a single CVC field), FormStructureRationalizer converts standard CVC fields into the CREDIT_CARD_STANDALONE_VERIFICATION_CODE variant. While this ensures correct classification for Autofill, it inadvertently causes the field to bypass the exclusion check described above.
3. Inconsistent Name-Based Filtering
Fields that bypass the type-based guard are subsequently evaluated by AutocompleteHistoryManager::IsFieldValueSaveable. This manager uses a name-based blocklist (IsFieldNameMeaningfulForAutocomplete) that includes substrings like cvc, cvn, or cvv.
However, Chrome’s heuristic parser for CVC fields (kCardCvcRe) matches a much broader range of names, such as securityCode, csc, ccv, or verificationCode. Fields with these names are correctly classified as CVCs by the browser but are not blocked by the Autocomplete manager’s name filter, resulting in the CVC value being persisted in plaintext to the autofill table of the Web Data database.
4. Cross-Origin Disclosure via Suggestions
Autocomplete storage is keyed only by field name and value, without origin scoping. When a user interacts with a field on a different origin that uses a matching name (e.g., securityCode), AutocompleteSuggestionGenerator::GenerateSuggestions retrieves the stored value. This generator also fails to block the CREDIT_CARD_STANDALONE_VERIFICATION_CODE type, allowing the sensitive CVC to be offered as a suggestion.
Potential Reproduction Steps
- Navigate to a site (Origin A) with a single-field CVC re-authentication form where the input name is
securityCode(e.g.,<input name="securityCode">). - Enter a CVC value and submit the form.
- The value is potentially saved to the local profile’s
Web Datadatabase in theautofilltable. - Navigate to a different origin (Origin B) that contains an
<input name="securityCode">field. - Click on the input field. The previously entered CVC may be offered as an Autocomplete suggestion.
- Selecting the suggestion populates the CVC into the page, where it can be read by scripts on Origin B.
Note: These steps are based on a source code analysis; the behavior should be verified in a running environment.
Suggested Fix
- Update the exclusion guards in
BrowserAutofillManager::MaybeImportFromSubmittedFormandAutocompleteSuggestionGenerator::GenerateSuggestionsto includeCREDIT_CARD_STANDALONE_VERIFICATION_CODE. - Synchronize the name-based blocklist in
AutocompleteHistoryManager::IsFieldNameMeaningfulForAutocompletewith the regexes used inkCardCvcReto ensure consistent blocking of CVC-related field names.
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
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.