blob: 8684b25ebfd787155faf1cea763ecc1be6836f3e [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ctime>
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "base/test/simple_test_clock.h"
#include "base/time/clock.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/permissions/notification_permission_review_service_factory.h"
#include "chrome/browser/ui/webui/settings/safety_hub_handler.h"
#include "chrome/browser/ui/webui/settings/site_settings_helper.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/permissions/constants.h"
#include "components/permissions/unused_site_permissions_service.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
constexpr char kUnusedTestSite[] = "https://example1.com";
constexpr char kUsedTestSite[] = "https://example2.com";
constexpr ContentSettingsType kUnusedPermission =
ContentSettingsType::GEOLOCATION;
class SafetyHubHandlerTest : public testing::Test {
public:
SafetyHubHandlerTest() {
feature_list_.InitAndEnableFeature(
content_settings::features::kSafetyCheckUnusedSitePermissions);
}
void SetUp() override {
// Fully initialize |profile_| in the constructor since some children
// classes need it right away for SetUp().
TestingProfile::Builder profile_builder;
profile_builder.AddTestingFactory(
HistoryServiceFactory::GetInstance(),
HistoryServiceFactory::GetDefaultFactory());
profile_ = profile_builder.Build();
// Set clock for HostContentSettingsMap.
base::Time time;
ASSERT_TRUE(base::Time::FromString("2022-09-07 13:00", &time));
clock_.SetNow(time);
hcsm_ = HostContentSettingsMapFactory::GetForProfile(profile());
hcsm_->SetClockForTesting(&clock_);
handler_ = std::make_unique<SafetyHubHandler>(profile());
handler()->set_web_ui(web_ui());
handler()->AllowJavascript();
// Create a revoked permission.
base::Value::Dict dict = base::Value::Dict();
base::Value::List permission_type_list = base::Value::List();
permission_type_list.Append(
static_cast<int32_t>(ContentSettingsType::GEOLOCATION));
dict.Set(permissions::kRevokedKey,
base::Value::List(std::move(permission_type_list)));
hcsm()->SetWebsiteSettingDefaultScope(
GURL(kUnusedTestSite), GURL(kUnusedTestSite),
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
base::Value(dict.Clone()));
// There should be only an unused URL in the revoked permissions list.
const auto& revoked_permissions =
handler()->PopulateUnusedSitePermissionsData();
EXPECT_EQ(revoked_permissions.size(), 1UL);
EXPECT_EQ(GURL(kUnusedTestSite),
GURL(*revoked_permissions[0].GetDict().FindString(
site_settings::kOrigin)));
}
void TearDown() override {
if (profile_) {
auto* partition = profile_->GetDefaultStoragePartition();
if (partition) {
partition->WaitForDeletionTasksForTesting();
}
}
}
void ExpectRevokedPermission() {
ContentSettingsForOneType revoked_permissions_list;
hcsm()->GetSettingsForOneType(
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
&revoked_permissions_list);
EXPECT_EQ(1U, revoked_permissions_list.size());
EXPECT_EQ(
ContentSetting::CONTENT_SETTING_ASK,
hcsm()->GetContentSetting(GURL(kUnusedTestSite), GURL(kUnusedTestSite),
kUnusedPermission));
}
void ValidateNotificationPermissionUpdate() {
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
ASSERT_TRUE(data.arg1()->is_string());
EXPECT_EQ("notification-permission-review-list-maybe-changed",
data.arg1()->GetString());
ASSERT_TRUE(data.arg2()->is_list());
}
base::Value::List GetOriginList(int size) {
base::Value::List origins;
for (int i = 0; i < size; i++) {
origins.Append("https://example" + base::NumberToString(i) + ".org:443");
}
return origins;
}
TestingProfile* profile() { return profile_.get(); }
content::TestWebUI* web_ui() { return &web_ui_; }
SafetyHubHandler* handler() { return handler_.get(); }
HostContentSettingsMap* hcsm() { return hcsm_.get(); }
base::SimpleTestClock* clock() { return &clock_; }
private:
base::test::ScopedFeatureList feature_list_;
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<SafetyHubHandler> handler_;
std::unique_ptr<TestingProfile> profile_;
content::TestWebUI web_ui_;
scoped_refptr<HostContentSettingsMap> hcsm_;
base::SimpleTestClock clock_;
};
TEST_F(SafetyHubHandlerTest, PopulateUnusedSitePermissionsData) {
// Add GEOLOCATION setting for url but do not add to revoked list.
content_settings::ContentSettingConstraints constraint;
constraint.set_track_last_visit_for_autoexpiration(true);
hcsm()->SetContentSettingDefaultScope(
GURL(kUsedTestSite), GURL(kUsedTestSite),
ContentSettingsType::GEOLOCATION, ContentSetting::CONTENT_SETTING_ALLOW,
constraint);
// Revoked permissions list should still only contain the initial unused site.
const auto& revoked_permissions =
handler()->PopulateUnusedSitePermissionsData();
EXPECT_EQ(revoked_permissions.size(), 1UL);
EXPECT_EQ(GURL(kUnusedTestSite),
GURL(*revoked_permissions[0].GetDict().FindString(
site_settings::kOrigin)));
}
TEST_F(SafetyHubHandlerTest, HandleAllowPermissionsAgainForUnusedSite) {
base::Value::List initial_unused_site_permissions =
handler()->PopulateUnusedSitePermissionsData();
ExpectRevokedPermission();
// Allow the revoked permission for the unused site again.
base::Value::List args;
args.Append(base::Value(kUnusedTestSite));
handler()->HandleAllowPermissionsAgainForUnusedSite(args);
// Check there is no origin in revoked permissions list.
ContentSettingsForOneType revoked_permissions_list;
hcsm()->GetSettingsForOneType(
ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
&revoked_permissions_list);
EXPECT_EQ(0U, revoked_permissions_list.size());
// Check if the permissions of url is regranted.
EXPECT_EQ(
ContentSetting::CONTENT_SETTING_ALLOW,
hcsm()->GetContentSetting(GURL(kUnusedTestSite), GURL(kUnusedTestSite),
kUnusedPermission));
// Undoing restores the initial state.
handler()->HandleUndoAllowPermissionsAgainForUnusedSite(
std::move(initial_unused_site_permissions));
ExpectRevokedPermission();
}
TEST_F(SafetyHubHandlerTest,
HandleAcknowledgeRevokedUnusedSitePermissionsList) {
const auto& revoked_permissions_before =
handler()->PopulateUnusedSitePermissionsData();
EXPECT_GT(revoked_permissions_before.size(), 0U);
// Acknowledging revoked permissions from unused sites clears the list.
base::Value::List args;
handler()->HandleAcknowledgeRevokedUnusedSitePermissionsList(args);
const auto& revoked_permissions_after =
handler()->PopulateUnusedSitePermissionsData();
EXPECT_EQ(revoked_permissions_after.size(), 0U);
// Undo reverts the list to its initial state.
base::Value::List undo_args;
undo_args.Append(revoked_permissions_before.Clone());
handler()->HandleUndoAcknowledgeRevokedUnusedSitePermissionsList(undo_args);
EXPECT_EQ(revoked_permissions_before,
handler()->PopulateUnusedSitePermissionsData());
}
TEST_F(SafetyHubHandlerTest,
HandleIgnoreOriginsForNotificationPermissionReview) {
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeature(
::features::kSafetyCheckNotificationPermissions);
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
ContentSettingsForOneType ignored_patterns;
content_settings->GetSettingsForOneType(
ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
ASSERT_EQ(0U, ignored_patterns.size());
base::Value::List args;
args.Append(GetOriginList(1));
handler()->HandleIgnoreOriginsForNotificationPermissionReview(args);
// Check there is 1 origin in ignore list.
content_settings->GetSettingsForOneType(
ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
ASSERT_EQ(1U, ignored_patterns.size());
ValidateNotificationPermissionUpdate();
}
TEST_F(SafetyHubHandlerTest,
HandleUndoIgnoreOriginsForNotificationPermissionReview) {
base::Value::List args;
args.Append(GetOriginList(1));
handler()->HandleIgnoreOriginsForNotificationPermissionReview(args);
// Check there is 1 origin in ignore list.
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
ContentSettingsForOneType ignored_patterns;
ASSERT_EQ(0U, ignored_patterns.size());
content_settings->GetSettingsForOneType(
ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
ASSERT_EQ(1U, ignored_patterns.size());
// Check there are no origins in ignore list.
handler()->HandleUndoIgnoreOriginsForNotificationPermissionReview(args);
content_settings->GetSettingsForOneType(
ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, &ignored_patterns);
ASSERT_EQ(0U, ignored_patterns.size());
}
TEST_F(SafetyHubHandlerTest, HandleAllowNotificationPermissionForOrigins) {
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeature(
::features::kSafetyCheckNotificationPermissions);
base::Value::List args;
base::Value::List origins = GetOriginList(2);
args.Append(origins.Clone());
handler()->HandleAllowNotificationPermissionForOrigins(args);
// Check the permission for the two origins is allow.
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
ContentSettingsForOneType notification_permissions;
content_settings->GetSettingsForOneType(ContentSettingsType::NOTIFICATIONS,
&notification_permissions);
auto type = content_settings->GetContentSetting(
GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_ALLOW, type);
type = content_settings->GetContentSetting(
GURL(origins[1].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_ALLOW, type);
ValidateNotificationPermissionUpdate();
}
TEST_F(SafetyHubHandlerTest, HandleBlockNotificationPermissionForOrigins) {
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeature(
::features::kSafetyCheckNotificationPermissions);
base::Value::List args;
base::Value::List origins = GetOriginList(2);
args.Append(origins.Clone());
handler()->HandleBlockNotificationPermissionForOrigins(args);
// Check the permission for the two origins is block.
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
ContentSettingsForOneType notification_permissions;
content_settings->GetSettingsForOneType(ContentSettingsType::NOTIFICATIONS,
&notification_permissions);
auto type = content_settings->GetContentSetting(
GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_BLOCK, type);
type = content_settings->GetContentSetting(
GURL(origins[1].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_BLOCK, type);
ValidateNotificationPermissionUpdate();
}
TEST_F(SafetyHubHandlerTest, HandleResetNotificationPermissionForOrigins) {
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeature(
::features::kSafetyCheckNotificationPermissions);
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile());
base::Value::List args;
base::Value::List origins = GetOriginList(1);
args.Append(origins.Clone());
content_settings->SetContentSettingCustomScope(
ContentSettingsPattern::FromString(origins[0].GetString()),
ContentSettingsPattern::Wildcard(), ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_ALLOW);
handler()->HandleResetNotificationPermissionForOrigins(args);
// Check the permission for the origin is reset.
auto type = content_settings->GetContentSetting(
GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS);
ASSERT_EQ(CONTENT_SETTING_ASK, type);
ValidateNotificationPermissionUpdate();
}