blob: c133577bfd41b172f92caad8c0ba0dda7ddd1ce1 [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 "chrome/browser/permissions/crowd_deny_safe_browsing_request.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "components/safe_browsing/core/db/database_manager.h"
#include "components/safe_browsing/core/db/test_database_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace {
constexpr char kTestOriginFoo[] = "https://foo.com";
constexpr char kTestOriginBar[] = "https://bar.com";
class FakeSafeBrowsingDatabaseManager
: public safe_browsing::TestSafeBrowsingDatabaseManager {
public:
FakeSafeBrowsingDatabaseManager() = default;
void SetSimulatedMetadataForUrl(
const GURL& url,
const safe_browsing::ThreatMetadata& metadata) {
url_to_simulated_threat_metadata_.emplace(url, metadata);
}
void RemoveAllBlacklistedUrls() { url_to_simulated_threat_metadata_.clear(); }
void set_simulate_timeout(bool simulate_timeout) {
simulate_timeout_ = simulate_timeout;
}
void set_simulate_synchronous_result(bool simulate_synchronous_result) {
simulate_synchronous_result_ = simulate_synchronous_result;
}
protected:
~FakeSafeBrowsingDatabaseManager() override {
EXPECT_THAT(pending_clients_, testing::IsEmpty());
}
safe_browsing::ThreatMetadata GetSimulatedMetadataOrSafe(const GURL& url) {
auto it = url_to_simulated_threat_metadata_.find(url);
return it != url_to_simulated_threat_metadata_.end()
? it->second
: safe_browsing::ThreatMetadata();
}
// safe_browsing::TestSafeBrowsingDatabaseManager:
bool CheckApiBlacklistUrl(const GURL& url, Client* client) override {
if (simulate_synchronous_result_)
return true;
if (simulate_timeout_) {
EXPECT_THAT(pending_clients_, testing::Not(testing::Contains(client)));
pending_clients_.insert(client);
} else {
auto result = GetSimulatedMetadataOrSafe(url);
client->OnCheckApiBlacklistUrlResult(url, std::move(result));
}
return false;
}
bool CancelApiCheck(Client* client) override {
EXPECT_THAT(pending_clients_, testing::Contains(client));
pending_clients_.erase(client);
return true;
}
bool IsSupported() const override { return true; }
bool ChecksAreAlwaysAsync() const override { return false; }
private:
void OnCheckUrlForSubresourceFilterComplete(Client* client, const GURL& url);
std::set<Client*> pending_clients_;
std::map<GURL, safe_browsing::ThreatMetadata>
url_to_simulated_threat_metadata_;
bool simulate_timeout_ = false;
bool simulate_synchronous_result_ = false;
base::WeakPtrFactory<FakeSafeBrowsingDatabaseManager> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingDatabaseManager);
};
} // namespace
class CrowdDenySafeBrowsingRequestTest : public testing::Test {
public:
using Verdict = CrowdDenySafeBrowsingRequest::Verdict;
CrowdDenySafeBrowsingRequestTest()
: fake_database_manager_(
base::MakeRefCounted<FakeSafeBrowsingDatabaseManager>()) {}
~CrowdDenySafeBrowsingRequestTest() override = default;
protected:
content::BrowserTaskEnvironment* task_environment() {
return &task_environment_;
}
FakeSafeBrowsingDatabaseManager* fake_database_manager() {
return fake_database_manager_.get();
}
const base::Clock* test_clock() { return task_environment_.GetMockClock(); }
void StartRequestForOriginAndExpectVerdict(const url::Origin& origin,
Verdict expected_verdict) {
base::HistogramTester histogram_tester;
base::MockOnceCallback<void(Verdict)> mock_callback_receiver;
CrowdDenySafeBrowsingRequest request(fake_database_manager(), test_clock(),
origin, mock_callback_receiver.Get());
EXPECT_CALL(mock_callback_receiver, Run(expected_verdict));
task_environment()->RunUntilIdle();
histogram_tester.ExpectTotalCount(
"Permissions.CrowdDeny.SafeBrowsing.RequestDuration", 1);
histogram_tester.ExpectUniqueSample(
"Permissions.CrowdDeny.SafeBrowsing.Verdict",
static_cast<int>(expected_verdict), 1);
}
private:
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<FakeSafeBrowsingDatabaseManager> fake_database_manager_;
DISALLOW_COPY_AND_ASSIGN(CrowdDenySafeBrowsingRequestTest);
};
TEST_F(CrowdDenySafeBrowsingRequestTest, Acceptable_SynchronousCompletion) {
fake_database_manager()->set_simulate_synchronous_result(true);
StartRequestForOriginAndExpectVerdict(
url::Origin::Create(GURL(kTestOriginFoo)), Verdict::kAcceptable);
}
TEST_F(CrowdDenySafeBrowsingRequestTest, Acceptable_UnavailableMetaData) {
StartRequestForOriginAndExpectVerdict(
url::Origin::Create(GURL(kTestOriginFoo)), Verdict::kAcceptable);
}
TEST_F(CrowdDenySafeBrowsingRequestTest, Acceptable_UnknownAPIName) {
const GURL kTestURL(kTestOriginFoo);
safe_browsing::ThreatMetadata test_metadata;
test_metadata.api_permissions.emplace("");
test_metadata.api_permissions.emplace("Stuff");
test_metadata.api_permissions.emplace("NOTIFICATION"); // Singular.
test_metadata.api_permissions.emplace("notifications"); // Lowercase.
fake_database_manager()->SetSimulatedMetadataForUrl(kTestURL, test_metadata);
StartRequestForOriginAndExpectVerdict(url::Origin::Create(kTestURL),
Verdict::kAcceptable);
}
TEST_F(CrowdDenySafeBrowsingRequestTest, Spammy) {
const GURL kTestURL(kTestOriginFoo);
safe_browsing::ThreatMetadata test_metadata;
test_metadata.api_permissions.emplace("BANANAS");
test_metadata.api_permissions.emplace("NOTIFICATIONS");
test_metadata.api_permissions.emplace("ORANGES");
fake_database_manager()->SetSimulatedMetadataForUrl(kTestURL, test_metadata);
StartRequestForOriginAndExpectVerdict(
url::Origin::Create(kTestURL),
Verdict::kKnownToShowUnsolicitedNotificationPermissionRequests);
StartRequestForOriginAndExpectVerdict(
url::Origin::Create(GURL(kTestOriginBar)), Verdict::kAcceptable);
}
TEST_F(CrowdDenySafeBrowsingRequestTest, Timeout) {
fake_database_manager()->set_simulate_timeout(true);
base::HistogramTester histogram_tester;
base::MockOnceCallback<void(Verdict)> mock_callback_receiver;
CrowdDenySafeBrowsingRequest request(
fake_database_manager(), test_clock(),
url::Origin::Create(GURL(kTestOriginFoo)), mock_callback_receiver.Get());
// Verify the request doesn't time out unreasonably fast.
EXPECT_CALL(mock_callback_receiver, Run(testing::_)).Times(0);
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
testing::Mock::VerifyAndClearExpectations(&mock_callback_receiver);
// But that it eventually does.
EXPECT_CALL(mock_callback_receiver, Run(Verdict::kAcceptable));
task_environment()->FastForwardUntilNoTasksRemain();
histogram_tester.ExpectUniqueSample(
"Permissions.CrowdDeny.SafeBrowsing.RequestDuration", 2000, 1);
histogram_tester.ExpectUniqueSample(
"Permissions.CrowdDeny.SafeBrowsing.Verdict",
static_cast<int>(Verdict::kAcceptable), 1);
}
TEST_F(CrowdDenySafeBrowsingRequestTest, AbandonedImmediately) {
fake_database_manager()->set_simulate_synchronous_result(true);
base::HistogramTester histogram_tester;
base::MockOnceCallback<void(Verdict)> mock_callback_receiver;
EXPECT_CALL(mock_callback_receiver, Run(testing::_)).Times(0);
{
CrowdDenySafeBrowsingRequest request(
fake_database_manager(), test_clock(),
url::Origin::Create(GURL(kTestOriginFoo)),
mock_callback_receiver.Get());
}
task_environment()->RunUntilIdle();
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("Permissions."),
testing::IsEmpty());
}
TEST_F(CrowdDenySafeBrowsingRequestTest, AbandonedWhileCheckPending) {
fake_database_manager()->set_simulate_timeout(true);
base::HistogramTester histogram_tester;
base::MockOnceCallback<void(Verdict)> mock_callback_receiver;
EXPECT_CALL(mock_callback_receiver, Run(testing::_)).Times(0);
CrowdDenySafeBrowsingRequest request(
fake_database_manager(), test_clock(),
url::Origin::Create(GURL(kTestOriginFoo)), mock_callback_receiver.Get());
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("Permissions."),
testing::IsEmpty());
}