0

[Passwords] Add UMA for masked and not masked fields.

This CL adds two UMA histograms:
1) PasswordManager.Parsing.PasswordField.IsMasked - it records
if the field that the model predicted to be a password field
is masked (has HTML type = password). It is needed to evaluate
how big the problem of passwords in cleartext is (because
historically we ignore them, and maybe we shouldn't).

2) PasswordManager.Parsing.UnrelatedFields.AnyFieldIsMasked -
it records if any of the fields predicted to not be a part of
a password flow is masked. It is needed to evaluate how often
the fields that we usually supported for filling and saving
just because of the field HTML type, are getting ignored by the
password manager with the model predictions.

Both metrics are recorded only when the form is parsed for
filling, to avoid recording data on every user keystroke during
the parsing in the saving mode.

In the follow-up CLs I'll add a UKM mirror for this metrics
to allow investigation of the most popular domains in each
category.

Bug: 371933424, 312195966
Change-Id: I6f6b833f068571560c4646d4d5966ba49194438f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6138591
Commit-Queue: Maria Kazinova <kazinova@google.com>
Reviewed-by: Viktor Semeniuk <vsemeniuk@google.com>
Cr-Commit-Position: refs/heads/main@{#1401877}
This commit is contained in:
Maria Kazinova
2025-01-03 10:17:59 -08:00
committed by Chromium LUCI CQ
parent a9ad4ac349
commit e44be8352c
3 changed files with 112 additions and 5 deletions
components/password_manager/core/browser/form_parsing
tools/metrics/histograms/metadata/password

@ -918,6 +918,8 @@ bool ParseUsingModelPredictions(
bool predictions_complete = true;
bool otp_detected = false;
std::optional<bool> password_field_is_masked;
std::optional<bool> unrelated_fields_contain_masked_fields;
for (auto& field : processed_fields) {
auto prediction = predictions.find(field.field->global_id());
@ -931,9 +933,8 @@ bool ParseUsingModelPredictions(
result->username = field.field;
break;
case autofill::PASSWORD:
password_field_is_masked = field.is_password;
if (!field.is_password) {
// TODO(crbug.com/371933424): Log metrics about cleartext fields that
// the model predicted as password fields.
break;
}
result->password = field.field;
@ -953,6 +954,9 @@ bool ParseUsingModelPredictions(
otp_detected = true;
break;
case autofill::UNKNOWN_TYPE:
unrelated_fields_contain_masked_fields =
unrelated_fields_contain_masked_fields.value_or(false) ||
field.is_password;
// The field is unrelated to passwords, do not update the parsing
// result.
break;
@ -979,10 +983,19 @@ bool ParseUsingModelPredictions(
if (predictions_complete && (mode == FormDataParser::Mode::kFilling)) {
ReportFormTypeMetric(result, otp_detected);
}
// TODO(crbug.com/371933424): Log metrics about renderer-recognized
// credential forms the model predicted to be unrelated to passwords.
if (password_field_is_masked.has_value()) {
base::UmaHistogramBoolean(
"PasswordManager.Parsing.PasswordField.IsMasked",
password_field_is_masked.value());
}
if (unrelated_fields_contain_masked_fields.has_value()) {
base::UmaHistogramBoolean(
"PasswordManager.Parsing.UnrelatedFields.AnyFieldIsMasked",
unrelated_fields_contain_masked_fields.value());
}
}
return predictions_complete;
}

@ -3761,6 +3761,76 @@ TEST_F(FormParserTest, PasswordVsOtpMetric) {
}
}
TEST_F(FormParserTest, PasswordFieldIsMaskedMetric_Recorded) {
base::HistogramTester histogram_tester;
CheckTestData({{
.fields =
{
{.role = ElementRole::CURRENT_PASSWORD,
.value = u"password",
.form_control_type = FormControlType::kInputPassword,
.model_predicted_type = autofill::PASSWORD},
},
}});
histogram_tester.ExpectUniqueSample(
"PasswordManager.Parsing.PasswordField.IsMasked", true, 1);
}
TEST_F(FormParserTest, PasswordFieldIsMaskedMetric_NotRecorded) {
base::HistogramTester histogram_tester;
CheckTestData({{
.fields =
{
{.value = u"maybe_password_idk",
.form_control_type = FormControlType::kInputPassword,
.model_predicted_type = autofill::UNKNOWN_TYPE},
},
}});
// No password field prediction, no metric.
histogram_tester.ExpectTotalCount(
"PasswordManager.Parsing.PasswordField.IsMasked", 0);
}
TEST_F(FormParserTest, UnrelatedFieldsAnyFieldIsMaskedMetric_Recorded) {
base::HistogramTester histogram_tester;
CheckTestData({{
.fields =
{
{.value = u"whatever1",
.form_control_type = FormControlType::kInputText,
.model_predicted_type = autofill::UNKNOWN_TYPE},
{.value = u"whatever2",
.form_control_type = FormControlType::kInputPassword,
.model_predicted_type = autofill::UNKNOWN_TYPE},
{.value = u"whatever3",
.form_control_type = FormControlType::kInputText,
.model_predicted_type = autofill::UNKNOWN_TYPE},
},
}});
histogram_tester.ExpectUniqueSample(
"PasswordManager.Parsing.UnrelatedFields.AnyFieldIsMasked", true, 1);
}
TEST_F(FormParserTest, UnrelatedFieldsAnyFieldIsMaskedMetric_NotRecorded) {
base::HistogramTester histogram_tester;
CheckTestData({{
.fields =
{
{.role = ElementRole::USERNAME,
.value = u"username",
.form_control_type = FormControlType::kInputText,
.model_predicted_type = autofill::USERNAME},
{.role = ElementRole::CURRENT_PASSWORD,
.value = u"password",
.form_control_type = FormControlType::kInputPassword,
.model_predicted_type = autofill::PASSWORD},
},
}});
// No unrelated fields - no metric.
histogram_tester.ExpectTotalCount(
"PasswordManager.Parsing.UnrelatedFields.AnyFieldIsMasked", 0);
}
} // namespace
} // namespace password_manager

@ -2470,6 +2470,30 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>
<histogram name="PasswordManager.Parsing.PasswordField.IsMasked" enum="Boolean"
expires_after="2025-07-01">
<owner>kazinova@google.com</owner>
<owner>vasilii@chromium.org</owner>
<summary>
Reports whether the field predicted as password field in a parsed form is
masked (has HTML type &quot;password&quot;) or not. Recorded once per form
when the form is parsed for filling, and the form contains a field with
PASSWORD type prediction.
</summary>
</histogram>
<histogram name="PasswordManager.Parsing.UnrelatedFields.AnyFieldIsMasked"
enum="Boolean" expires_after="2025-07-01">
<owner>kazinova@google.com</owner>
<owner>vasilii@chromium.org</owner>
<summary>
Reports whether any of the fields predicted to be unrelated to password
flows is masked (has HTML type &quot;password&quot;) or not. Recorded once
per form when the form is parsed for filling, and the form contains fields
with UNKNOWN_TYPE prediction.
</summary>
</histogram>
<histogram name="PasswordManager.PasskeyRetrievalWaitDuration" units="ms"
expires_after="2025-06-30">
<owner>kenrb@chromium.org</owner>