blob: 23c28b589cfa2d40a3cc15ccb46e5c63e2956fd7 [file] [log] [blame]
// 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/password_list_sorter.h"
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace password_manager {
namespace {
struct SortEntry {
const char* const origin;
const char* const username;
const char* const password;
const char* const app_display_name;
const char* const federation;
const bool is_blacklisted;
const int expected_position;
};
void SortAndCheckPositions(const std::vector<SortEntry>& test_entries) {
std::vector<std::unique_ptr<autofill::PasswordForm>> list;
size_t expected_number_of_unique_entries = 0;
for (const SortEntry& entry : test_entries) {
auto form = std::make_unique<autofill::PasswordForm>();
form->signon_realm = entry.origin;
form->origin = GURL(entry.origin);
form->blacklisted_by_user = entry.is_blacklisted;
if (!entry.is_blacklisted) {
form->username_value = base::ASCIIToUTF16(entry.username);
form->password_value = base::ASCIIToUTF16(entry.password);
if (entry.federation != nullptr)
form->federation_origin = url::Origin::Create(GURL(entry.federation));
}
if (entry.app_display_name)
form->app_display_name = entry.app_display_name;
list.push_back(std::move(form));
if (entry.expected_position >= 0)
expected_number_of_unique_entries++;
}
DuplicatesMap duplicates;
password_manager::SortEntriesAndHideDuplicates(&list, &duplicates);
ASSERT_EQ(expected_number_of_unique_entries, list.size());
ASSERT_EQ(test_entries.size() - expected_number_of_unique_entries,
duplicates.size());
for (const SortEntry& entry : test_entries) {
if (entry.expected_position >= 0) {
SCOPED_TRACE(testing::Message("position in sorted list: ")
<< entry.expected_position);
EXPECT_EQ(GURL(entry.origin), list[entry.expected_position]->origin);
if (!entry.is_blacklisted) {
EXPECT_EQ(base::ASCIIToUTF16(entry.username),
list[entry.expected_position]->username_value);
EXPECT_EQ(base::ASCIIToUTF16(entry.password),
list[entry.expected_position]->password_value);
if (entry.federation != nullptr)
EXPECT_EQ(url::Origin::Create(GURL(entry.federation)),
list[entry.expected_position]->federation_origin);
}
}
}
}
} // namespace
TEST(PasswordListSorterTest, Sorting_DifferentOrigins) {
const std::vector<SortEntry> test_cases = {
{"http://example-b.com", "user_a", "pwd", nullptr, nullptr, false, 2},
{"http://example-a.com", "user_a1", "pwd", nullptr, nullptr, false, 0},
{"http://example-a.com", "user_a2", "pwd", nullptr, nullptr, false, 1},
{"http://example-c.com", "user_a", "pwd", nullptr, nullptr, false, 3}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_DifferentUsernames) {
const std::vector<SortEntry> test_cases = {
{"http://example.com", "user_a", "pwd", nullptr, nullptr, false, 0},
{"http://example.com", "user_c", "pwd", nullptr, nullptr, false, 2},
{"http://example.com", "user_b", "pwd", nullptr, nullptr, false, 1}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_DifferentPasswords) {
const std::vector<SortEntry> test_cases = {
{"http://example.com", "user_a", "1", nullptr, nullptr, false, 0},
{"http://example.com", "user_a", "2", nullptr, nullptr, false, 1},
{"http://example.com", "user_a", "3", nullptr, nullptr, false, 2}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_DifferentSchemes) {
const std::vector<SortEntry> test_cases = {
{"https://example.com", "user", "1", nullptr, nullptr, false, 1},
{"https://example.com", "user", "1", nullptr, nullptr, false,
-1}, // Hide it.
{"http://example.com", "user", "1", nullptr, nullptr, false, 0}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_HideDuplicates) {
const std::vector<SortEntry> test_cases = {
{"http://example.com", "user_a", "pwd", nullptr, nullptr, false, 0},
// Different username.
{"http://example.com", "user_b", "pwd", nullptr, nullptr, false, 2},
// Different password.
{"http://example.com", "user_a", "secret", nullptr, nullptr, false, 1},
// Different origin.
{"http://sub1.example.com", "user_a", "pwd", nullptr, nullptr, false, 3},
{"http://example.com", "user_a", "pwd", nullptr, nullptr, false,
-1} // Hide it.
};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_Subdomains) {
const std::vector<SortEntry> test_cases = {
{"http://example.com", "u", "p", nullptr, nullptr, false, 0},
{"http://b.example.com", "u", "p", nullptr, nullptr, false, 6},
{"http://a.example.com", "u", "p", nullptr, nullptr, false, 1},
{"http://1.a.example.com", "u", "p", nullptr, nullptr, false, 2},
{"http://2.a.example.com", "u", "p", nullptr, nullptr, false, 3},
{"http://x.2.a.example.com", "u", "p", nullptr, nullptr, false, 4},
{"http://y.2.a.example.com", "u", "p", nullptr, nullptr, false, 5},
};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_PasswordExceptions) {
const std::vector<SortEntry> test_cases = {
{"http://example-b.com", nullptr, nullptr, nullptr, nullptr, true, 1},
{"http://example-a.com", nullptr, nullptr, nullptr, nullptr, true, 0},
{"http://example-a.com", nullptr, nullptr, nullptr, nullptr, true,
-1}, // Hide it.
{"http://example-c.com", nullptr, nullptr, nullptr, nullptr, true, 2}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_AndroidCredentials) {
const std::vector<SortEntry> test_cases = {
// Regular Web Credential.
{"https://alpha.example.com", "user", "secret", nullptr, nullptr, false,
3},
// First Android Credential.
{"android://hash@com.example.alpha", "user", "secret", nullptr, nullptr,
false, 0},
// App display name is irrelevant, this should be hidden as a duplicate
// of the first one.
{"android://hash@com.example.alpha", "user", "secret", "Example App",
nullptr, false, -1},
// Apps with different package names are distinct.
{"android://hash@com.example.beta", "user", "secret", nullptr, nullptr,
false, 1},
// Apps with same package name but different hashes are distinct.
{"android://hash_different@com.example.alpha", "user", "secret", nullptr,
nullptr, false, 2}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_Federations) {
const std::vector<SortEntry> test_cases = {
{"https://example.com", "user", "secret", nullptr, nullptr, false, 0},
{"https://example.com", "user", "secret", nullptr, "https://fed1.com",
false, 1},
{"https://example.com", "user", "secret", nullptr, "https://fed2.com",
false, 2}};
SortAndCheckPositions(test_cases);
}
TEST(PasswordListSorterTest, Sorting_SpecialCharacters) {
// URLs with encoded special characters should not cause crash during sorting.
const std::vector<SortEntry> test_cases = {
{"https://xn--bea5m6d.com/", "user_a", "pwd", nullptr, nullptr, false, 4},
{"https://uoy.com/", "user_a", "pwd", nullptr, nullptr, false, 1},
{"https://zrc.com/", "user_a", "pwd", nullptr, nullptr, false, 6},
{"https://abc.com/", "user_a", "pwd", nullptr, nullptr, false, 0},
{"https://xn--ab-fma.com/", "user_a", "pwd", nullptr, nullptr, false, 2},
{"https://xn--bc-lia.com/", "user_a", "pwd", nullptr, nullptr, false, 3},
{"https://xn--ndalk.com/", "user_a", "pwd", nullptr, nullptr, false, 5},
};
SortAndCheckPositions(test_cases);
}
} // namespace password_manager