1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
6
7 #include "base/lazy_instance.h"
8 #include "base/macros.h"
9 #include "base/no_destructor.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/string_split.h"
12 #include "components/autofill/content/renderer/html_based_username_detector.h"
13 #include "components/autofill/core/common/renderer_id.h"
14 #include "google_apis/gaia/gaia_urls.h"
15 #include "net/base/url_util.h"
16 #include "third_party/blink/public/platform/web_string.h"
17 #include "third_party/blink/public/web/web_document.h"
18 #include "third_party/blink/public/web/web_form_control_element.h"
19 #include "third_party/blink/public/web/web_input_element.h"
20 #include "third_party/blink/public/web/web_local_frame.h"
21 #if defined(OS_BSD)
22 #include <re2/re2.h>
23 #else
24 #include "third_party/re2/src/re2/re2.h"
25 #endif
26 #include "url/gurl.h"
27
28 using blink::WebElement;
29 using blink::WebFormControlElement;
30 using blink::WebFormElement;
31 using blink::WebInputElement;
32 using blink::WebLocalFrame;
33 using blink::WebString;
34
35 namespace autofill {
36
37 namespace {
38
39 const char kPasswordSiteUrlRegex[] =
40 "passwords(?:-[a-z-]+\\.corp)?\\.google\\.com";
41
42 struct PasswordSiteUrlLazyInstanceTraits
43 : public base::internal::DestructorAtExitLazyInstanceTraits<re2::RE2> {
Newautofill::__anon0e73d3080111::PasswordSiteUrlLazyInstanceTraits44 static re2::RE2* New(void* instance) {
45 return CreateMatcher(instance, kPasswordSiteUrlRegex);
46 }
47 };
48
49 base::LazyInstance<re2::RE2, PasswordSiteUrlLazyInstanceTraits>
50 g_password_site_matcher = LAZY_INSTANCE_INITIALIZER;
51
52 // Extracts the username predictions. |control_elements| should be all the DOM
53 // elements of the form, |form_data| should be the already extracted FormData
54 // representation of that form. |username_detector_cache| is optional, and can
55 // be used to spare recomputation if called multiple times for the same form.
GetUsernamePredictions(const std::vector<WebFormControlElement> & control_elements,const FormData & form_data,UsernameDetectorCache * username_detector_cache)56 std::vector<FieldRendererId> GetUsernamePredictions(
57 const std::vector<WebFormControlElement>& control_elements,
58 const FormData& form_data,
59 UsernameDetectorCache* username_detector_cache) {
60 // Dummy cache stores the predictions in case no real cache was passed to
61 // here.
62 UsernameDetectorCache dummy_cache;
63 if (!username_detector_cache)
64 username_detector_cache = &dummy_cache;
65
66 return GetPredictionsFieldBasedOnHtmlAttributes(control_elements, form_data,
67 username_detector_cache);
68 }
69
HasGaiaSchemeAndHost(const WebFormElement & form)70 bool HasGaiaSchemeAndHost(const WebFormElement& form) {
71 GURL form_url = form.GetDocument().Url();
72 GURL gaia_url = GaiaUrls::GetInstance()->gaia_url();
73 return form_url.scheme() == gaia_url.scheme() &&
74 form_url.host() == gaia_url.host();
75 }
76
77 } // namespace
78
CreateMatcher(void * instance,const char * pattern)79 re2::RE2* CreateMatcher(void* instance, const char* pattern) {
80 re2::RE2::Options options;
81 options.set_case_sensitive(false);
82 // Use placement new to initialize the instance in the preallocated space.
83 // The "(instance)" is very important to force POD type initialization.
84 re2::RE2* matcher = new (instance) re2::RE2(pattern, options);
85 DCHECK(matcher->ok());
86 return matcher;
87 }
88
IsGaiaReauthenticationForm(const blink::WebFormElement & form)89 bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
90 if (!HasGaiaSchemeAndHost(form))
91 return false;
92
93 bool has_rart_field = false;
94 bool has_continue_field = false;
95
96 for (const WebFormControlElement& element : form.GetFormControlElements()) {
97 // We're only interested in the presence
98 // of <input type="hidden" /> elements.
99 static base::NoDestructor<WebString> kHidden("hidden");
100 const blink::WebInputElement* input = blink::ToWebInputElement(&element);
101 if (!input || input->FormControlTypeForAutofill() != *kHidden)
102 continue;
103
104 // There must be a hidden input named "rart".
105 if (input->FormControlName() == "rart")
106 has_rart_field = true;
107
108 // There must be a hidden input named "continue", whose value points
109 // to a password (or password testing) site.
110 if (input->FormControlName() == "continue" &&
111 re2::RE2::PartialMatch(input->Value().Utf8(),
112 g_password_site_matcher.Get())) {
113 has_continue_field = true;
114 }
115 }
116 return has_rart_field && has_continue_field;
117 }
118
IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement & form)119 bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) {
120 if (!HasGaiaSchemeAndHost(form))
121 return false;
122
123 GURL url(form.GetDocument().Url());
124 std::string should_skip_password;
125 if (!net::GetValueForKeyInQuery(url, "ssp", &should_skip_password))
126 return false;
127 return should_skip_password == "1";
128 }
129
CreateFormDataFromWebForm(const WebFormElement & web_form,const FieldDataManager * field_data_manager,UsernameDetectorCache * username_detector_cache,form_util::ButtonTitlesCache * button_titles_cache)130 std::unique_ptr<FormData> CreateFormDataFromWebForm(
131 const WebFormElement& web_form,
132 const FieldDataManager* field_data_manager,
133 UsernameDetectorCache* username_detector_cache,
134 form_util::ButtonTitlesCache* button_titles_cache) {
135 if (web_form.IsNull())
136 return nullptr;
137
138 auto form_data = std::make_unique<FormData>();
139 form_data->url =
140 form_util::GetCanonicalOriginForDocument(web_form.GetDocument());
141 form_data->full_url =
142 form_util::GetDocumentUrlWithoutAuth(web_form.GetDocument());
143 form_data->is_gaia_with_skip_save_password_form =
144 IsGaiaWithSkipSavePasswordForm(web_form) ||
145 IsGaiaReauthenticationForm(web_form);
146
147 blink::WebVector<WebFormControlElement> control_elements =
148 web_form.GetFormControlElements();
149 if (control_elements.empty())
150 return nullptr;
151
152 if (!WebFormElementToFormData(web_form, WebFormControlElement(),
153 field_data_manager, form_util::EXTRACT_VALUE,
154 form_data.get(), nullptr /* FormFieldData */)) {
155 return nullptr;
156 }
157 form_data->username_predictions = GetUsernamePredictions(
158 control_elements.ReleaseVector(), *form_data, username_detector_cache);
159 form_data->button_titles = form_util::GetButtonTitles(
160 web_form, web_form.GetDocument(), button_titles_cache);
161
162 return form_data;
163 }
164
CreateFormDataFromUnownedInputElements(const WebLocalFrame & frame,const FieldDataManager * field_data_manager,UsernameDetectorCache * username_detector_cache,form_util::ButtonTitlesCache * button_titles_cache)165 std::unique_ptr<FormData> CreateFormDataFromUnownedInputElements(
166 const WebLocalFrame& frame,
167 const FieldDataManager* field_data_manager,
168 UsernameDetectorCache* username_detector_cache,
169 form_util::ButtonTitlesCache* button_titles_cache) {
170 std::vector<WebElement> fieldsets;
171 std::vector<WebFormControlElement> control_elements =
172 form_util::GetUnownedFormFieldElements(frame.GetDocument().All(),
173 &fieldsets);
174 if (control_elements.empty())
175 return nullptr;
176
177 auto form_data = std::make_unique<FormData>();
178 if (!UnownedPasswordFormElementsAndFieldSetsToFormData(
179 fieldsets, control_elements, nullptr, frame.GetDocument(),
180 field_data_manager, form_util::EXTRACT_VALUE, form_data.get(),
181 nullptr /* FormFieldData */)) {
182 return nullptr;
183 }
184
185 form_data->url =
186 form_util::GetCanonicalOriginForDocument(frame.GetDocument());
187 form_data->full_url =
188 form_util::GetDocumentUrlWithoutAuth(frame.GetDocument());
189 form_data->username_predictions = GetUsernamePredictions(
190 control_elements, *form_data, username_detector_cache);
191 form_data->button_titles = form_util::GetButtonTitles(
192 WebFormElement(), frame.GetDocument(), button_titles_cache);
193
194 return form_data;
195 }
196
GetSignOnRealm(const GURL & origin)197 std::string GetSignOnRealm(const GURL& origin) {
198 GURL::Replacements rep;
199 rep.SetPathStr("");
200 return origin.ReplaceComponents(rep).spec();
201 }
202
203 } // namespace autofill
204