| Index: components/password_manager/core/browser/login_database_unittest.cc
|
| diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
|
| index b15468f57bbbb4768c7fd2e107d07dfe461721b5..f219fd8be9eeac7104e9502439bd06a38c1624ca 100644
|
| --- a/components/password_manager/core/browser/login_database_unittest.cc
|
| +++ b/components/password_manager/core/browser/login_database_unittest.cc
|
| @@ -21,6 +21,7 @@
|
| #include "build/build_config.h"
|
| #include "components/autofill/core/common/password_form.h"
|
| #include "components/os_crypt/os_crypt_mocker.h"
|
| +#include "components/password_manager/core/browser/password_manager_test_utils.h"
|
| #include "components/password_manager/core/browser/psl_matching_helper.h"
|
| #include "sql/connection.h"
|
| #include "sql/statement.h"
|
| @@ -789,6 +790,146 @@ TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingRegexp) {
|
| EXPECT_EQ(0U, result.size());
|
| }
|
|
|
| +TEST_F(LoginDatabaseTest,
|
| + GetLoginsForSameOrganizationName_OnlyWebHTTPFormsAreConsidered) {
|
| + static constexpr const struct {
|
| + const PasswordFormData form_data;
|
| + const char* other_queried_signon_realm;
|
| + bool expected_matches_itself;
|
| + bool expected_matches_other_realm;
|
| + } kTestCases[] = {
|
| + {{PasswordForm::SCHEME_HTML, "https://example.com/",
|
| + "https://example.com/origin", "", L"", L"", L"", L"u", L"p", false, 1},
|
| + nullptr,
|
| + true,
|
| + true},
|
| + {{PasswordForm::SCHEME_BASIC, "http://example.com/realm",
|
| + "http://example.com/", "", L"", L"", L"", L"u", L"p", false, 1},
|
| + nullptr,
|
| + false,
|
| + false},
|
| + {{PasswordForm::SCHEME_OTHER, "ftp://example.com/realm",
|
| + "ftp://example.com/", "", L"", L"", L"", L"u", L"p", false, 1},
|
| + "http://example.com/realm",
|
| + false,
|
| + false},
|
| + {{PasswordForm::SCHEME_HTML,
|
| + "federation://example.com/accounts.google.com",
|
| + "https://example.com/orgin", "", L"", L"", L"", L"u",
|
| + kTestingFederatedLoginMarker, false, 1},
|
| + "http://example.com/",
|
| + false,
|
| + false},
|
| + {{PasswordForm::SCHEME_HTML, "android://hash@example.com/",
|
| + "android://hash@example.com/", "", L"", L"", L"", L"u", L"p", false, 1},
|
| + "http://example.com/",
|
| + false,
|
| + false},
|
| + };
|
| +
|
| + for (const auto& test_case : kTestCases) {
|
| + SCOPED_TRACE(test_case.form_data.signon_realm);
|
| +
|
| + std::unique_ptr<PasswordForm> form =
|
| + CreatePasswordFormFromDataForTesting(test_case.form_data);
|
| + ASSERT_EQ(AddChangeForForm(*form), db().AddLogin(*form));
|
| +
|
| + std::vector<std::unique_ptr<PasswordForm>> same_organization_forms;
|
| + EXPECT_TRUE(db().GetLoginsForSameOrganizationName(
|
| + form->signon_realm, &same_organization_forms));
|
| + EXPECT_EQ(test_case.expected_matches_itself ? 1u : 0u,
|
| + same_organization_forms.size());
|
| +
|
| + if (test_case.other_queried_signon_realm) {
|
| + same_organization_forms.clear();
|
| + EXPECT_TRUE(db().GetLoginsForSameOrganizationName(
|
| + test_case.other_queried_signon_realm, &same_organization_forms));
|
| + EXPECT_EQ(test_case.expected_matches_other_realm ? 1u : 0u,
|
| + same_organization_forms.size());
|
| + }
|
| +
|
| + ASSERT_TRUE(db().RemoveLogin(*form));
|
| + }
|
| +}
|
| +
|
| +TEST_F(LoginDatabaseTest, GetLoginsForSameOrganizationName_DetailsOfMatching) {
|
| + const struct {
|
| + const char* saved_signon_realm;
|
| + const char* queried_signon_realm;
|
| + bool expected_matches;
|
| + } kTestCases[] = {
|
| + // PSL matches are also same-organization-name matches.
|
| + {"http://psl.example.com/", "http://example.com/", true},
|
| + {"http://example.com/", "http://sub.example.com/", true},
|
| + {"https://a.b.example.co.uk/", "https://c.d.e.example.co.uk/", true},
|
| +
|
| + // Non-PSL but same-organization-name matches. Also an illustration why it
|
| + // would be unsafe to offer these credentials for filling.
|
| + {"https://example.com/", "https://example.co.uk/", true},
|
| + {"https://example.co.uk/", "https://example.com/", true},
|
| + {"https://a.example.appspot.com/", "https://b.example.co.uk/", true},
|
| +
|
| + // Same-organization-name matches are HTTP/HTTPS-agnostic.
|
| + {"https://example.com/", "http://example.com/", true},
|
| + {"http://example.com/", "https://example.com/", true},
|
| +
|
| + {"http://www.foo-bar.com/", "http://sub.foo-bar.com", true},
|
| + {"http://www.foo_bar.com/", "http://sub.foo_bar.com", true},
|
| + {"http://www.foo-bar.com/", "http://sub.foo%2Dbar.com", true},
|
| + {"http://www.foo%21bar.com/", "http://sub.foo!bar.com", true},
|
| + {"http://a.xn--sztr-7na0i.co.uk/", "http://xn--sztr-7na0i.com/", true},
|
| + {"http://a.xn--sztr-7na0i.co.uk/", "http://www.sz\xc3\xb3t\xc3\xa1r.com/",
|
| + true},
|
| +
|
| + {"http://www.foo+bar.com/", "http://sub.foo+bar.com", true},
|
| + {"http://www.foooobar.com/", "http://sub.foo+bar.com", false},
|
| + {"http://www.fobar.com/", "http://sub.foo?bar.com", false},
|
| + {"http://www.foozbar.com/", "http://sub.foo.bar.com", false},
|
| + {"http://www.foozbar.com/", "http://sub.foo[a-z]bar.com", false},
|
| +
|
| + {"https://notexample.com/", "https://example.com/", false},
|
| + {"https://a.notexample.com/", "https://example.com/", false},
|
| + {"https://example.com/", "https://notexample.com/", false},
|
| + {"https://example.com/", "https://example.bar.com/", false},
|
| + {"https://example.foo.com/", "https://example.com/", false},
|
| + {"https://example.foo.com/", "https://example.bar.com/", false},
|
| +
|
| + // URLs without host portions, hosts without registry controlled domains
|
| + // or hosts consisting of a registry.
|
| + {"http://localhost/", "http://localhost/", false},
|
| + {"https://example/", "https://example/", false},
|
| + {"https://co.uk/", "https://co.uk/", false},
|
| + {"https://example/", "https://example.com/", false},
|
| + {"https://a.example/", "https://example.com/", false},
|
| + {"https://example.com/", "https://example/", false},
|
| + {"https://127.0.0.1/", "https://127.0.0.1/", false},
|
| + {"https:/[3ffe:2a00:100:7031::1]/", "https:/[3ffe:2a00:100:7031::1]/",
|
| + false},
|
| +
|
| + // Queried |signon-realms| are invalid URIs.
|
| + {"https://example.com/", "", false},
|
| + {"https://example.com/", "bad url", false},
|
| + {"https://example.com/", "https://", false},
|
| + {"https://example.com/", "http://www.foo;bar.com", false},
|
| + {"https://example.com/", "example", false},
|
| + };
|
| +
|
| + for (const auto& test_case : kTestCases) {
|
| + SCOPED_TRACE(test_case.saved_signon_realm);
|
| + SCOPED_TRACE(test_case.queried_signon_realm);
|
| +
|
| + std::unique_ptr<PasswordForm> form = CreatePasswordFormFromDataForTesting(
|
| + {PasswordForm::SCHEME_HTML, test_case.saved_signon_realm,
|
| + test_case.saved_signon_realm, "", L"", L"", L"", L"u", L"p", true, 1});
|
| + std::vector<std::unique_ptr<PasswordForm>> result;
|
| + ASSERT_EQ(AddChangeForForm(*form), db().AddLogin(*form));
|
| + EXPECT_TRUE(db().GetLoginsForSameOrganizationName(
|
| + test_case.queried_signon_realm, &result));
|
| + EXPECT_EQ(test_case.expected_matches ? 1u : 0u, result.size());
|
| + ASSERT_TRUE(db().RemoveLogin(*form));
|
| + }
|
| +}
|
| +
|
| static bool AddTimestampedLogin(LoginDatabase* db,
|
| std::string url,
|
| const std::string& unique_string,
|
|
|