Chromium Code Reviews| Index: components/password_manager/core/browser/hsts_deleter.cc |
| diff --git a/components/password_manager/core/browser/hsts_deleter.cc b/components/password_manager/core/browser/hsts_deleter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..002ee8a449d7384bd0489485a13ab63c79956629 |
| --- /dev/null |
| +++ b/components/password_manager/core/browser/hsts_deleter.cc |
| @@ -0,0 +1,103 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "components/password_manager/core/browser/hsts_deleter.h" |
| + |
| +#include <algorithm> |
| +#include <iterator> |
| +#include <tuple> |
| + |
| +#include "base/logging.h" |
| +#include "components/autofill/core/common/password_form.h" |
| +#include "components/password_manager/core/browser/password_manager_client.h" |
| +#include "components/password_manager/core/browser/password_store.h" |
| +#include "components/password_manager/core/browser/statistics_table.h" |
| +#include "url/url_constants.h" |
| + |
| +using autofill::PasswordForm; |
| + |
| +namespace password_manager { |
| + |
| +HstsDeleter::HstsDeleter(const PasswordManagerClient* client, |
| + PasswordStore* password_store) |
| + : client_(client), password_store_(password_store) { |
| + DCHECK(client_); |
| + DCHECK(password_store_); |
| +} |
| + |
| +void HstsDeleter::OnGetPasswordStoreResults( |
| + std::vector<std::unique_ptr<PasswordForm>> results) { |
| + // Non HTTP or HTTPS credentials are ignored. |
| + results.erase(std::remove_if(std::begin(results), std::end(results), |
| + [](const std::unique_ptr<PasswordForm>& form) { |
| + return !form->origin.SchemeIsHTTPOrHTTPS(); |
| + }), |
| + std::end(results)); |
| + |
| + // Move HTTPS forms to the end of the container, |
| + auto begin_https = |
| + std::partition(std::begin(results), std::end(results), |
| + [](const std::unique_ptr<PasswordForm>& form) { |
| + return form->origin.SchemeIs(url::kHttpScheme); |
| + }); |
| + |
| + // Move blacklisted HTTP forms to the end of the HTTP form segment. |
| + auto begin_blacklisted_http = |
| + std::partition(std::begin(results), begin_https, |
| + [](const std::unique_ptr<PasswordForm>& form) { |
| + return !form->blacklisted_by_user; |
| + }); |
| + |
| + // Remove blacklisted HTTP forms from the password store when HSTS is active |
| + // for the given host. |
| + std::for_each(begin_blacklisted_http, begin_https, |
| + [this](const std::unique_ptr<PasswordForm>& blacklisted_form) { |
| + if (client_->IsHSTSActiveForOrigin(blacklisted_form->origin)) |
|
vasilii
2017/02/06 16:43:24
Does HSTS request for HTTP also work?
jdoerrie
2017/02/07 13:15:00
Yes, it does. For HSTS queries only the host matte
|
| + password_store_->RemoveLogin(*blacklisted_form); |
| + }); |
|
vasilii
2017/02/06 16:43:24
Why can't you process and remove the blacklisted f
jdoerrie
2017/02/07 13:15:00
I could have done that, but then I also need to ch
|
| + |
| + // Return early if there are no non-blacklisted HTTP forms. |
| + if (std::begin(results) == begin_blacklisted_http) |
| + return; |
| + |
| + // Ignore non HSTS forms. We are explicitly not calling |results.erase| so |
| + // that we don't invalidate |begin_https| in case there are no HSTS forms. |
|
vasilii
2017/02/06 16:43:24
erase() doesn't ivalidate the iterators before |fi
jdoerrie
2017/02/07 13:15:00
As discussed offline this actually does leed to in
|
| + auto end_hsts = |
| + std::remove_if(begin_https, std::end(results), |
| + [this](const std::unique_ptr<PasswordForm>& form) { |
| + return !client_->IsHSTSActiveForOrigin(form->origin); |
| + }); |
| + |
| + // Sort HSTS forms according to custom comparison function. Consider two forms |
| + // equivalent if they have the same host, as well as the same username and |
| + // password. |
| + const auto form_cmp = [](const std::unique_ptr<PasswordForm>& lhs, |
| + const std::unique_ptr<PasswordForm>& rhs) { |
| + return std::forward_as_tuple(lhs->origin.host_piece(), lhs->username_value, |
| + lhs->password_value) < |
| + std::forward_as_tuple(rhs->origin.host_piece(), rhs->username_value, |
| + rhs->password_value); |
| + }; |
| + |
| + std::sort(begin_https, end_hsts, form_cmp); |
| + |
| + // Iterate through HTTP forms and remove them from the password store if there |
| + // exists an equivalent HSTS form. |
| + std::for_each(std::begin(results), begin_blacklisted_http, |
| + [&, this](const std::unique_ptr<PasswordForm>& form) { |
| + if (std::binary_search(begin_https, end_hsts, form, form_cmp)) |
| + password_store_->RemoveLogin(*form); |
| + }); |
| +} |
| + |
| +void HstsDeleter::OnGetSiteStatistics(std::vector<InteractionsStats> stats) { |
| + for (const auto& stat : stats) { |
| + if (stat.origin_domain.SchemeIs(url::kHttpScheme) && |
| + client_->IsHSTSActiveForOrigin(stat.origin_domain)) { |
|
vasilii
2017/02/06 16:43:24
How can HSTS be active for an HTTP origin?
jdoerrie
2017/02/07 13:15:00
See above (scheme is ignored, only host matters).
|
| + password_store_->RemoveSiteStats(stat.origin_domain); |
| + } |
| + } |
| +} |
| + |
| +} // namespace password_manager |