blob: 5139b084fbeac625502878defd1e48f5b902283c [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <memory>
#include "base/test/gtest_util.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/content_settings/mock_settings_observer.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/browser/cookie_settings.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/pref_names.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/site_for_cookies.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/webui/webui_allowlist.h"
#include "ui/webui/webui_allowlist_provider.h"
#include "url/origin.h"
using ::testing::_;
class WebUIAllowlistProviderTest : public ChromeRenderViewHostTestHarness {
public:
HostContentSettingsMap* GetHostContentSettingsMap(Profile* profile) {
return HostContentSettingsMapFactory::GetForProfile(profile);
}
};
TEST_F(WebUIAllowlistProviderTest, RegisterChrome) {
auto* map = GetHostContentSettingsMap(profile());
map->SetDefaultContentSetting(ContentSettingsType::BLUETOOTH_GUARD,
CONTENT_SETTING_BLOCK);
map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_BLOCK);
map->SetDefaultContentSetting(ContentSettingsType::GEOLOCATION,
CONTENT_SETTING_BLOCK);
// Check |url_allowed| is not affected by allowlisted_schemes. This mechanism
// take precedence over allowlist provider.
const GURL url_allowed = GURL("chrome://test/");
ASSERT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_allowed, url_allowed,
ContentSettingsType::BLUETOOTH_GUARD));
const GURL url_ordinary = GURL("https://example.com");
ASSERT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_ordinary, url_ordinary,
ContentSettingsType::BLUETOOTH_GUARD));
ASSERT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_ordinary, url_ordinary,
ContentSettingsType::NOTIFICATIONS));
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedPermission(
url::Origin::Create(url_allowed), ContentSettingsType::BLUETOOTH_GUARD);
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url_allowed, url_allowed,
ContentSettingsType::BLUETOOTH_GUARD));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_ordinary, url_ordinary,
ContentSettingsType::BLUETOOTH_GUARD));
const GURL url_no_permission_webui = GURL("chrome://no-perm");
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_no_permission_webui, url_no_permission_webui,
ContentSettingsType::BLUETOOTH_GUARD));
}
TEST_F(WebUIAllowlistProviderTest, RegisterChromeUntrusted) {
auto* map = GetHostContentSettingsMap(profile());
map->SetDefaultContentSetting(ContentSettingsType::BLUETOOTH_GUARD,
CONTENT_SETTING_BLOCK);
map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_BLOCK);
map->SetDefaultContentSetting(ContentSettingsType::GEOLOCATION,
CONTENT_SETTING_BLOCK);
// Check |url_allowed| is not affected by allowlisted_schemes. This mechanism
// take precedence over allowlist provider.
const GURL url_allowed = GURL("chrome-untrusted://test/");
ASSERT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_allowed, url_allowed,
ContentSettingsType::BLUETOOTH_GUARD));
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedPermission(
url::Origin::Create(url_allowed), ContentSettingsType::BLUETOOTH_GUARD);
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url_allowed, url_allowed,
ContentSettingsType::BLUETOOTH_GUARD));
const GURL url_no_permission_webui = GURL("chrome-untrusted://no-perm");
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_no_permission_webui, url_no_permission_webui,
ContentSettingsType::BLUETOOTH_GUARD));
}
#if DCHECK_IS_ON()
#define MAYBE_UnsupportedSchemes UnsupportedSchemes
#else
#define MAYBE_UnsupportedSchemes DISABLED_UnsupportedSchemes
#endif
TEST_F(WebUIAllowlistProviderTest, MAYBE_UnsupportedSchemes) {
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
std::string unsupported_urls[] = {
"http://example.com",
"https://example.com",
"file:///file",
};
for (const auto& url : unsupported_urls) {
EXPECT_DEATH_IF_SUPPORTED(allowlist->RegisterAutoGrantedPermission(
url::Origin::Create(GURL(url)),
ContentSettingsType::BLUETOOTH_GUARD),
std::string());
}
}
#if DCHECK_IS_ON()
#define MAYBE_UnsupportedThirdPartyCookiesSettings \
UnsupportedThirdPartyCookiesSettings
#else
#define MAYBE_UnsupportedThirdPartyCookiesSettings \
DISABLED_UnsupportedThirdPartyCookiesSettings
#endif
TEST_F(WebUIAllowlistProviderTest, MAYBE_UnsupportedThirdPartyCookiesSettings) {
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
std::string unsupported_top_level_origins[] = {
"http://example.com",
"https://example.com",
"file:///file",
};
for (const auto& url : unsupported_top_level_origins) {
EXPECT_DEATH_IF_SUPPORTED(allowlist->RegisterAutoGrantedThirdPartyCookies(
url::Origin::Create(GURL(url)),
{ContentSettingsPattern::FromURL(GURL(url))}),
std::string());
}
}
#if DCHECK_IS_ON()
#define MAYBE_InvalidContentSetting InvalidContentSetting
#else
#define MAYBE_InvalidContentSetting DISABLED_InvalidContentSetting
#endif
TEST_F(WebUIAllowlistProviderTest, MAYBE_InvalidContentSetting) {
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
EXPECT_DEATH_IF_SUPPORTED(
allowlist->RegisterAutoGrantedPermission(
url::Origin::Create(GURL("chrome://test/")),
ContentSettingsType::BLUETOOTH_GUARD, CONTENT_SETTING_DEFAULT),
std::string());
}
TEST_F(WebUIAllowlistProviderTest, AutoGrantPermissionIsPerProfile) {
TestingProfileManager profile_manager(TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager.SetUp());
// Create two profiles.
Profile* profile1 = profile_manager.CreateTestingProfile("1");
auto* map1 = GetHostContentSettingsMap(profile1);
map1->SetDefaultContentSetting(ContentSettingsType::GEOLOCATION,
CONTENT_SETTING_BLOCK);
Profile* profile2 = profile_manager.CreateTestingProfile("2");
auto* map2 = GetHostContentSettingsMap(profile2);
map2->SetDefaultContentSetting(ContentSettingsType::GEOLOCATION,
CONTENT_SETTING_BLOCK);
GURL url = GURL("chrome://test");
// Register GEOLOCATION with |profile1|.
WebUIAllowlist::GetOrCreate(profile1)->RegisterAutoGrantedPermission(
url::Origin::Create(url), ContentSettingsType::GEOLOCATION);
// Check permissions are granted to the correct profile.
EXPECT_EQ(
CONTENT_SETTING_ALLOW,
map1->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION));
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map2->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION));
}
TEST_F(WebUIAllowlistProviderTest, RegisterDevtools) {
auto* map = GetHostContentSettingsMap(profile());
map->SetDefaultContentSetting(ContentSettingsType::BLUETOOTH_GUARD,
CONTENT_SETTING_BLOCK);
// Check |url_allowed| is not affected by allowlisted_schemes. This mechanism
// take precedence over allowlist provider.
const GURL url_allowed = GURL("devtools://devtools");
ASSERT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_allowed, url_allowed,
ContentSettingsType::BLUETOOTH_GUARD));
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedPermission(
url::Origin::Create(url_allowed), ContentSettingsType::BLUETOOTH_GUARD);
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url_allowed, url_allowed,
ContentSettingsType::BLUETOOTH_GUARD));
const GURL url_no_permission_webui = GURL("devtools://other");
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url_no_permission_webui, url_no_permission_webui,
ContentSettingsType::BLUETOOTH_GUARD));
}
TEST_F(WebUIAllowlistProviderTest, RegisterWithPermissionList) {
auto* map = GetHostContentSettingsMap(profile());
map->SetDefaultContentSetting(ContentSettingsType::BLUETOOTH_GUARD,
CONTENT_SETTING_BLOCK);
map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_BLOCK);
const GURL url_chrome = GURL("chrome://test");
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedPermissions(
url::Origin::Create(url_chrome), {ContentSettingsType::BLUETOOTH_GUARD,
ContentSettingsType::NOTIFICATIONS});
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url_chrome, url_chrome,
ContentSettingsType::BLUETOOTH_GUARD));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url_chrome, url_chrome,
ContentSettingsType::NOTIFICATIONS));
}
TEST_F(WebUIAllowlistProviderTest,
RegisterThirdPartyCookiesWithAllCookiesBlocked) {
auto* map = GetHostContentSettingsMap(profile());
map->SetDefaultContentSetting(ContentSettingsType::COOKIES,
CONTENT_SETTING_BLOCK);
map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_BLOCK);
const GURL top_level_url = GURL("chrome-untrusted://test/");
const GURL third_party_url = GURL("https://example.com/");
// Check |url_allowed| is not affected by allowlisted_schemes, which takes
// precedence over allowlist provider.
ASSERT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(third_party_url, top_level_url,
ContentSettingsType::COOKIES));
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedThirdPartyCookies(
url::Origin::Create(top_level_url),
{ContentSettingsPattern::FromURL(third_party_url)});
auto cookies_settings = CookieSettingsFactory::GetForProfile(profile());
// Allowlisted origin embedded in the correct top-level origin can use
// cookies.
EXPECT_TRUE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies::FromUrl(top_level_url),
url::Origin::Create(top_level_url), net::CookieSettingOverrides()));
// Allowlisted origin on its own can't use cookies.
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies::FromUrl(third_party_url),
url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
// Allowlisted origin embedded in Web top-level origin can't use cookies.
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
GURL("https://example2.com"),
net::SiteForCookies::FromUrl(third_party_url),
url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
// Allowlisted origin making subresource request (e.g. image) can't use
// cookies.
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies(), std::nullopt,
net::CookieSettingOverrides()));
// Allowlisted origin embedded in the wrong WebUI origin can't use cookies.
const GURL url_no_permission_webui = GURL("chrome-untrusted://no-perm");
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies::FromUrl(url_no_permission_webui),
url::Origin::Create(url_no_permission_webui),
net::CookieSettingOverrides()));
// Other permissions aren't affected.
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(third_party_url, top_level_url,
ContentSettingsType::NOTIFICATIONS));
}
TEST_F(WebUIAllowlistProviderTest,
RegisterThirdPartyCookiesWithThirdPartyCookiesBlocked) {
auto* map = GetHostContentSettingsMap(profile());
map->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
CONTENT_SETTING_BLOCK);
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
auto cookies_settings = CookieSettingsFactory::GetForProfile(profile());
const GURL top_level_url = GURL("chrome-untrusted://test/");
const GURL third_party_url = GURL("https://example.com/");
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedThirdPartyCookies(
url::Origin::Create(top_level_url),
{ContentSettingsPattern::FromURL(third_party_url)});
EXPECT_TRUE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies::FromUrl(top_level_url),
url::Origin::Create(top_level_url), net::CookieSettingOverrides()));
// Allowlisted origin on its own can use cookies, because only third-party
// cookies are blocked.
EXPECT_TRUE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies::FromUrl(third_party_url),
url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
// Allowlisted origin embedded in Web top-level origin can't use cookies.
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
GURL("https://example2.com"),
net::SiteForCookies::FromUrl(third_party_url),
url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
// Allowlisted origin embedded in the wrong WebUI origin can't use cookies.
const GURL url_no_permission_webui = GURL("chrome-untrusted://no-perm");
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies::FromUrl(url_no_permission_webui),
url::Origin::Create(url_no_permission_webui),
net::CookieSettingOverrides()));
// Allowlisted origin making subresource request (e.g. image) can't use
// cookies.
EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
third_party_url, net::SiteForCookies(), std::nullopt,
net::CookieSettingOverrides()));
// Other permissions aren't affected.
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(third_party_url, top_level_url,
ContentSettingsType::NOTIFICATIONS));
}
TEST_F(WebUIAllowlistProviderTest, RegisterThirdPartyCookiesNotifiesOnlyOnce) {
auto* map = GetHostContentSettingsMap(profile());
MockSettingsObserver observer(map);
const GURL top_level_url = GURL("chrome-untrusted://test/");
const GURL third_party_url1 = GURL("https://example1.com/");
const GURL third_party_url2 = GURL("https://example2.com/");
const GURL third_party_url3 = GURL("https://example3.com/");
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
// The initial call notifies observers.
EXPECT_CALL(observer,
OnContentSettingsChanged(map, ContentSettingsType::COOKIES, false,
_, _, false))
.Times(2);
allowlist->RegisterAutoGrantedThirdPartyCookies(
url::Origin::Create(top_level_url),
{ContentSettingsPattern::FromURL(third_party_url1),
ContentSettingsPattern::FromURL(third_party_url2)});
testing::Mock::VerifyAndClearExpectations(&observer);
// A repeated call does not notify observers.
EXPECT_CALL(observer,
OnContentSettingsChanged(map, ContentSettingsType::COOKIES, false,
_, _, false))
.Times(0);
allowlist->RegisterAutoGrantedThirdPartyCookies(
url::Origin::Create(top_level_url),
{ContentSettingsPattern::FromURL(third_party_url1),
ContentSettingsPattern::FromURL(third_party_url2)});
testing::Mock::VerifyAndClearExpectations(&observer);
// A new URL notifies observers only for this URL.
EXPECT_CALL(observer,
OnContentSettingsChanged(map, ContentSettingsType::COOKIES, false,
_, _, false))
.Times(1);
allowlist->RegisterAutoGrantedThirdPartyCookies(
url::Origin::Create(top_level_url),
{ContentSettingsPattern::FromURL(third_party_url1),
ContentSettingsPattern::FromURL(third_party_url3)});
testing::Mock::VerifyAndClearExpectations(&observer);
}
// Regression test for https://crbug.com/1514740.
TEST_F(WebUIAllowlistProviderTest, CookieSettings) {
const GURL url = GURL("https://google.com/");
const GURL top_level_url = GURL("chrome-untrusted://test/");
const url::Origin top_level_origin = url::Origin::Create(top_level_url);
auto* map = GetHostContentSettingsMap(profile());
auto cookie_settings = CookieSettingsFactory::GetForProfile(profile());
// Enable 3p cookie blocking and clear on exit.
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
map->SetDefaultContentSetting(ContentSettingsType::COOKIES,
CONTENT_SETTING_SESSION_ONLY);
// Check that settings are applied before creating an allowlist entry.
EXPECT_FALSE(cookie_settings->IsFullCookieAccessAllowed(
url, net::SiteForCookies::FromUrl(top_level_url),
url::Origin::Create(top_level_url), {}));
EXPECT_TRUE(cookie_settings->IsCookieSessionOnly(url));
EXPECT_TRUE(cookie_settings->ShouldDeleteCookieOnExit(
cookie_settings->GetCookieSettings(), url.host(),
net::CookieSourceScheme::kSecure));
EXPECT_TRUE(cookie_settings->IsCookieSessionOnly(top_level_url));
EXPECT_TRUE(cookie_settings->ShouldDeleteCookieOnExit(
cookie_settings->GetCookieSettings(), top_level_url.host(),
net::CookieSourceScheme::kSecure));
// Registering a third-party cookie exception should only affect 3p cookies
// in webui and not change clear on exit behavior of regular cookies.
// A first-party exception should still apply to web ui.
auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
allowlist->RegisterAutoGrantedPermission(top_level_origin,
ContentSettingsType::COOKIES);
allowlist->RegisterAutoGrantedThirdPartyCookies(
top_level_origin, {ContentSettingsPattern::FromURLNoWildcard(url)});
EXPECT_TRUE(cookie_settings->IsFullCookieAccessAllowed(
url, net::SiteForCookies::FromUrl(top_level_url),
url::Origin::Create(top_level_url), {}));
EXPECT_TRUE(cookie_settings->IsCookieSessionOnly(url));
EXPECT_TRUE(cookie_settings->ShouldDeleteCookieOnExit(
cookie_settings->GetCookieSettings(), url.host(),
net::CookieSourceScheme::kSecure));
EXPECT_FALSE(cookie_settings->IsCookieSessionOnly(top_level_url));
EXPECT_FALSE(cookie_settings->ShouldDeleteCookieOnExit(
cookie_settings->GetCookieSettings(), top_level_url.host(),
net::CookieSourceScheme::kSecure));
}