[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:

committed by
Chromium LUCI CQ

parent
a9ad4ac349
commit
e44be8352c
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 "password") 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 "password") 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>
|
||||
|
Reference in New Issue
Block a user