File: sensitive_content_manager.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (181 lines) | stat: -rw-r--r-- 7,691 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/sensitive_content/sensitive_content_manager.h"

#include "base/check_deref.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/integrators/password_form_classification.h"
#include "components/password_manager/content/browser/password_form_classification_util.h"
#include "components/sensitive_content/features.h"
#include "components/sensitive_content/sensitive_content_client.h"
#include "content/public/browser/web_contents.h"

namespace sensitive_content {

namespace {

using LifecycleState = autofill::AutofillDriver::LifecycleState;
using autofill::AutofillField;
using autofill::AutofillManager;
using autofill::FieldType;
using autofill::FieldTypeGroup;
using autofill::FormGlobalId;

bool IsSensitiveAutofillType(FieldType type) {
  static constexpr autofill::FieldTypeSet kNonSensitivePasswordTypes = {
      FieldType::NOT_ACCOUNT_CREATION_PASSWORD, FieldType::NOT_NEW_PASSWORD,
      FieldType::NOT_PASSWORD, FieldType::NOT_USERNAME};
  FieldTypeGroup field_type_group = autofill::GroupTypeOfFieldType(type);
  // Return true if the field is a credit card or password form field.
  // A field is a password form field if it's part of
  // `FieldTypeGroup::kPasswordField`, but it's not prefixed with "NOT_".
  return field_type_group == FieldTypeGroup::kCreditCard ||
         field_type_group == FieldTypeGroup::kStandaloneCvcField ||
         (field_type_group == FieldTypeGroup::kPasswordField &&
          !kNonSensitivePasswordTypes.contains(type));
}

}  // namespace

SensitiveContentManager::SensitiveContentManager(
    content::WebContents* web_contents,
    SensitiveContentClient* client)
    : client_(CHECK_DEREF(client)) {
  autofill_managers_observation_.Observe(
      web_contents, autofill::ScopedAutofillManagersObservation::
                        InitializationPolicy::kObservePreexistingManagers);
}

SensitiveContentManager::~SensitiveContentManager() = default;

bool SensitiveContentManager::UpdateContentSensitivity() {
  const bool content_is_sensitive = !sensitive_fields_.empty();
  // Prevent unnecessary calls to the client.
  if (last_content_was_sensitive_ != content_is_sensitive) {
    client_->SetContentSensitivity(!sensitive_fields_.empty());
    last_content_was_sensitive_ = content_is_sensitive;

    base::UmaHistogramBoolean(
        base::StrCat({client_->GetHistogramPrefix(), "SensitivityChanged"}),
        content_is_sensitive);

    if (content_is_sensitive) {
      content_became_sensitive_timestamp_ = base::TimeTicks::Now();
    } else if (content_became_sensitive_timestamp_.has_value()) {
      base::UmaHistogramLongTimes(
          base::StrCat({client_->GetHistogramPrefix(), "SensitiveTime"}),
          base::TimeTicks::Now() - content_became_sensitive_timestamp_.value());
      content_became_sensitive_timestamp_.reset();
    }
    return true;
  }
  return false;
}

void SensitiveContentManager::OnFieldTypesDetermined(AutofillManager& manager,
                                                     FormGlobalId form_id,
                                                     FieldTypeSource) {
  if (const autofill::FormStructure* form =
          manager.FindCachedFormById(form_id)) {
    for (const std::unique_ptr<AutofillField>& field : form->fields()) {
      const bool field_is_sensitive =
          IsSensitiveAutofillType(field->Type().GetStorableType());
      // The feature param check is done first because reparsing by password
      // manager (calling `ClassifyAsPasswordForm`) can take long. Moreover,
      // this feature param exists only to check whether reparsing has a
      // negative performance impact or not. Otherwise, it is known that
      // reparsing by password manager is more accurate for password forms.
      const bool field_is_sensitive_after_password_manager_reparsing =
          features::kSensitiveContentUsePwmHeuristicsParam.Get() &&
          password_manager::ClassifyAsPasswordForm(manager, form_id,
                                                   field->global_id())
                  .type !=
              autofill::PasswordFormClassification::Type::kNoPasswordForm;

      if (field_is_sensitive ||
          field_is_sensitive_after_password_manager_reparsing) {
        sensitive_fields_.insert(field->global_id());
      } else {
        sensitive_fields_.erase(field->global_id());
      }
    }

    if (UpdateContentSensitivity() && last_content_was_sensitive_) {
      const auto& element = latency_until_sensitive_timer_.find(form_id);
      if (element != latency_until_sensitive_timer_.end()) {
        base::UmaHistogramLongTimes(base::StrCat({client_->GetHistogramPrefix(),
                                                  "LatencyUntilSensitive"}),
                                    base::TimeTicks::Now() - element->second);
      }
    }
  }
}

void SensitiveContentManager::OnBeforeFormsSeen(
    AutofillManager& manager,
    base::span<const FormGlobalId> updated_forms,
    base::span<const FormGlobalId> removed_forms) {
  for (const FormGlobalId& form_id : updated_forms) {
    latency_until_sensitive_timer_[form_id] = base::TimeTicks::Now();
  }
  for (const FormGlobalId& form_id : removed_forms) {
    latency_until_sensitive_timer_.erase(form_id);
    if (const autofill::FormStructure* form =
            manager.FindCachedFormById(form_id)) {
      for (const std::unique_ptr<AutofillField>& field : form->fields()) {
        sensitive_fields_.erase(field->global_id());
      }
    }
  }
  UpdateContentSensitivity();
}

void SensitiveContentManager::OnAutofillManagerStateChanged(
    AutofillManager& manager,
    LifecycleState previous,
    LifecycleState current) {
  autofill::LocalFrameToken local_frame_token =
      manager.driver().GetFrameToken();

  if (previous == LifecycleState::kActive &&
      current != LifecycleState::kActive) {
    // The frame is not active anymore, so its fields are not anymore in the
    // DOM.
    // If needed, the time complexity can be further improved here by exploiting
    // that fields from the same frame are next to each other in the set.
    std::erase_if(sensitive_fields_,
                  [local_frame_token](const autofill::FieldGlobalId& field_id) {
                    return field_id.frame_token == local_frame_token;
                  });
    std::erase_if(latency_until_sensitive_timer_,
                  [local_frame_token](const auto& item) {
                    FormGlobalId form_id = item.first;
                    return form_id.frame_token == local_frame_token;
                  });
  } else if (previous != LifecycleState::kActive &&
             current == LifecycleState::kActive) {
    // The frame became active, so its fields are present in the DOM again.
    const std::map<FormGlobalId, std::unique_ptr<autofill::FormStructure>>&
        forms = manager.form_structures();

    for (const auto& [form_id, form_structure] : forms) {
      const std::vector<std::unique_ptr<AutofillField>>& fields =
          form_structure->fields();
      for (const std::unique_ptr<AutofillField>& field : fields) {
        if (IsSensitiveAutofillType(field->Type().GetStorableType())) {
          sensitive_fields_.insert(field->global_id());
        }
      }
    }
  }

  UpdateContentSensitivity();
}

}  // namespace sensitive_content