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