blob: a3b6ba7927f81121fa9beb6acbca9d369ae5de86 [file] [log] [blame]
// Copyright (c) 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_reuse_detection_manager.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
using testing::AnyNumber;
using testing::_;
namespace password_manager {
namespace {
constexpr size_t kMaxNumberOfCharactersToStore = 30;
class MockPasswordManagerClient : public StubPasswordManagerClient {
public:
MockPasswordManagerClient() = default;
~MockPasswordManagerClient() override = default;
MOCK_CONST_METHOD0(GetPasswordStore, PasswordStore*());
private:
DISALLOW_COPY_AND_ASSIGN(MockPasswordManagerClient);
};
class PasswordReuseDetectionManagerTest : public ::testing::Test {
public:
PasswordReuseDetectionManagerTest() {}
void SetUp() override {
store_ = new testing::StrictMock<MockPasswordStore>;
CHECK(store_->Init(syncer::SyncableService::StartSyncFlare(), nullptr));
}
void TearDown() override {
store_->ShutdownOnUIThread();
store_ = nullptr;
}
protected:
// It's needed for an initialisation of thread runners that are used in
// MockPasswordStore.
base::test::ScopedTaskEnvironment scoped_task_environment_;
MockPasswordManagerClient client_;
scoped_refptr<MockPasswordStore> store_;
DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetectionManagerTest);
};
// Verify that CheckReuse is called on each key pressed event with an argument
// equal to the last 30 keystrokes typed after the last main frame navigation.
TEST_F(PasswordReuseDetectionManagerTest, CheckReuseCalled) {
const GURL gurls[] = {GURL("https://www.example.com"),
GURL("https://www.otherexample.com")};
const base::string16 input[] = {
base::ASCIIToUTF16(
"1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"),
base::ASCIIToUTF16("?<>:'{}ABCDEF")};
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(testing::Return(store_.get()));
PasswordReuseDetectionManager manager(&client_);
for (size_t test = 0; test < base::size(gurls); ++test) {
manager.DidNavigateMainFrame(gurls[test]);
for (size_t i = 0; i < input[test].size(); ++i) {
base::string16 expected_input = input[test].substr(0, i + 1);
if (expected_input.size() > kMaxNumberOfCharactersToStore)
expected_input = expected_input.substr(expected_input.size() -
kMaxNumberOfCharactersToStore);
EXPECT_CALL(
*store_,
CheckReuse(expected_input, gurls[test].GetOrigin().spec(), &manager));
manager.OnKeyPressed(input[test].substr(i, 1));
testing::Mock::VerifyAndClearExpectations(store_.get());
}
}
}
// Verify that the keystroke buffer is cleared after 10 seconds of user
// inactivity.
TEST_F(PasswordReuseDetectionManagerTest,
CheckThatBufferClearedAfterInactivity) {
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(testing::Return(store_.get()));
PasswordReuseDetectionManager manager(&client_);
base::SimpleTestClock clock;
base::Time now = base::Time::Now();
clock.SetNow(now);
manager.SetClockForTesting(&clock);
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("1"));
// Simulate 10 seconds of inactivity.
clock.SetNow(now + base::TimeDelta::FromSeconds(10));
// Expect that a keystroke typed before inactivity is cleared.
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("2"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("2"));
}
// Verify that the keystroke buffer is cleared after user presses enter.
TEST_F(PasswordReuseDetectionManagerTest, CheckThatBufferClearedAfterEnter) {
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(testing::Return(store_.get()));
PasswordReuseDetectionManager manager(&client_);
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("1"));
base::string16 enter_text(1, ui::VKEY_RETURN);
EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
manager.OnKeyPressed(enter_text);
// Expect only a keystroke typed after enter.
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("2"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("2"));
}
// Verify that after reuse found, no reuse checking happens till next main frame
// navigation.
TEST_F(PasswordReuseDetectionManagerTest, NoReuseCheckingAfterReuseFound) {
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(testing::Return(store_.get()));
PasswordReuseDetectionManager manager(&client_);
// Simulate that reuse found.
manager.OnReuseFound(0ul, base::nullopt, {"https://example.com"}, 0);
// Expect no checking of reuse.
EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
manager.OnKeyPressed(base::ASCIIToUTF16("1"));
// Expect that after main frame navigation checking is restored.
manager.DidNavigateMainFrame(GURL("https://www.example.com"));
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("1"));
}
// Verify that keystroke buffer is cleared only on cross host navigation.
TEST_F(PasswordReuseDetectionManagerTest, DidNavigateMainFrame) {
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(testing::Return(store_.get()));
PasswordReuseDetectionManager manager(&client_);
manager.DidNavigateMainFrame(GURL("https://www.example1.com/123"));
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("1"));
// Check that the buffer is not cleared on the same host navigation.
manager.DidNavigateMainFrame(GURL("https://www.example1.com/456"));
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("12"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("2"));
// Check that the buffer is cleared on the cross host navigation.
manager.DidNavigateMainFrame(GURL("https://www.example2.com/123"));
EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("3"), _, _));
manager.OnKeyPressed(base::ASCIIToUTF16("3"));
}
} // namespace
} // namespace password_manager