blob: c00da2b70c3b0c0c6b06fedf60c04356557fedd1 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/site_access_requests_helper.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/permissions/active_tab_permission_granter.h"
#include "chrome/browser/extensions/permissions/scripting_permissions_modifier.h"
#include "chrome/browser/extensions/permissions/site_permissions_helper.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/test_browser_window.h"
#include "components/crx_file/id_util.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/browser/unloaded_extension_reason.h"
#include "extensions/common/extension_builder.h"
#include "extensions/test/permissions_manager_waiter.h"
namespace extensions {
class SiteAccessRequestsHelperUnittest : public ExtensionServiceTestBase {
public:
SiteAccessRequestsHelperUnittest() = default;
~SiteAccessRequestsHelperUnittest() override = default;
SiteAccessRequestsHelperUnittest(const SiteAccessRequestsHelperUnittest&) =
delete;
SiteAccessRequestsHelperUnittest& operator=(
const SiteAccessRequestsHelperUnittest&) = delete;
// Installs an extension with `host_permission` and withhelds them.
scoped_refptr<const Extension> InstallExtensionAndWithholdHostPermissions(
const std::string& name,
const std::string& host_permission);
// Installs an extension with activeTab permission.
scoped_refptr<const Extension> InstallExtensionWithActiveTab(
const std::string& name);
// Adds a new tab with `url` to the tab strip, and returns the WebContents
// associated with it.
content::WebContents* AddTab(const GURL& url);
// Returns the browser. Creates a new one if it doesn't exist.
Browser* browser();
PermissionsManager* permissions_manager() { return permissions_manager_; }
// ExtensionServiceTestBase:
void SetUp() override;
void TearDown() override;
private:
// The browser and accompaying window.
std::unique_ptr<Browser> browser_;
std::unique_ptr<TestBrowserWindow> browser_window_;
raw_ptr<PermissionsManager> permissions_manager_;
};
scoped_refptr<const Extension>
SiteAccessRequestsHelperUnittest::InstallExtensionAndWithholdHostPermissions(
const std::string& name,
const std::string& host_permission) {
auto extension =
ExtensionBuilder(name)
.SetManifestVersion(3)
.SetManifestKey("host_permissions",
base::Value::List().Append(host_permission))
.SetID(crx_file::id_util::GenerateId(name))
.Build();
service()->AddExtension(extension.get());
ScriptingPermissionsModifier(profile(), extension)
.SetWithholdHostPermissions(true);
return extension;
}
scoped_refptr<const Extension>
SiteAccessRequestsHelperUnittest::InstallExtensionWithActiveTab(
const std::string& name) {
auto extension = ExtensionBuilder(name)
.SetManifestVersion(3)
.SetID(crx_file::id_util::GenerateId(name))
.AddPermission("activeTab")
.Build();
service()->AddExtension(extension.get());
return extension;
}
content::WebContents* SiteAccessRequestsHelperUnittest::AddTab(
const GURL& url) {
std::unique_ptr<content::WebContents> web_contents(
content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
content::WebContents* raw_contents = web_contents.get();
browser()->tab_strip_model()->AppendWebContents(std::move(web_contents),
true);
EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), raw_contents);
content::NavigationSimulator::NavigateAndCommitFromBrowser(raw_contents, url);
EXPECT_EQ(url, raw_contents->GetLastCommittedURL());
return raw_contents;
}
Browser* SiteAccessRequestsHelperUnittest::browser() {
if (!browser_) {
Browser::CreateParams params(profile(), true);
browser_window_ = std::make_unique<TestBrowserWindow>();
params.window = browser_window_.get();
browser_.reset(Browser::Create(params));
}
return browser_.get();
}
void SiteAccessRequestsHelperUnittest::SetUp() {
ExtensionServiceTestBase::SetUp();
InitializeEmptyExtensionService();
permissions_manager_ = PermissionsManager::Get(profile());
}
void SiteAccessRequestsHelperUnittest::TearDown() {
// Remove any tabs in the tab strip; else the test crashes.
if (browser_) {
while (!browser_->tab_strip_model()->empty()) {
browser_->tab_strip_model()->DetachAndDeleteWebContentsAt(0);
}
}
permissions_manager_ = nullptr;
ExtensionServiceTestBase::TearDown();
}
// Tests site access requests are properly added and removed.
TEST_F(SiteAccessRequestsHelperUnittest, AddAndRemoveRequests) {
auto extension_A =
InstallExtensionAndWithholdHostPermissions("Extension A", "<all_urls>");
auto extension_B =
InstallExtensionAndWithholdHostPermissions("Extension B", "<all_urls>");
content::WebContents* web_contents = AddTab(GURL("http://www.example.com/"));
int tab_id = ExtensionTabUtil::GetTabId(web_contents);
// Add site access request for extension A. Verify only extension A has an
// active request.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id,
*extension_A);
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
// Add site access request for extension B. Verify both extensions have active
// requests.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id,
*extension_B);
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
// Remove site access request for extension A. Verify only extension B has an
// active request.
permissions_manager()->RemoveSiteAccessRequest(tab_id, extension_A->id());
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
}
// Tests that site access requests dismissed by the user are not active
// requests.
TEST_F(SiteAccessRequestsHelperUnittest, UserDismissedRequest) {
auto extension =
InstallExtensionAndWithholdHostPermissions("Extension", "<all_urls>");
content::WebContents* web_contents = AddTab(GURL("http://www.example.com/"));
int tab_id = ExtensionTabUtil::GetTabId(web_contents);
// Add site access request for extension A. Verify extension has an active
// request.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id, *extension);
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
// Dismiss site access request for extension. Verify request is not an active
// request.
permissions_manager()->UserDismissedSiteAccessRequest(web_contents, tab_id,
extension->id());
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
}
// Tests request is removed on cross-origin navigations.
TEST_F(SiteAccessRequestsHelperUnittest,
RequestRemovedOnCrossOriginNavigation) {
auto extension =
InstallExtensionAndWithholdHostPermissions("Extension", "<all_urls>");
content::WebContents* web_contents =
AddTab(GURL("http://www.same-origin.com/a"));
content::WebContentsTester* web_contents_tester =
content::WebContentsTester::For(web_contents);
int tab_id = ExtensionTabUtil::GetTabId(web_contents);
// Add site access request for extension.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id, *extension);
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
// Same-origin navigation should retain request.
web_contents_tester->NavigateAndCommit(GURL("http://www.same-origin.com/b"));
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
// Cross-origin navigation should remove request.
web_contents_tester->NavigateAndCommit(GURL("http://www.cross-origin.com/c"));
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
}
// Test request is removed when extension is granted "always on this site" site
// access.
TEST_F(SiteAccessRequestsHelperUnittest,
RequestRemovedWhenExtensionHasSiteAccess) {
auto extension =
InstallExtensionAndWithholdHostPermissions("Extension", "<all_urls>");
content::WebContents* web_contents =
AddTab(GURL("http://www.same-origin.com/a"));
int tab_id = ExtensionTabUtil::GetTabId(web_contents);
// Add site access request for extension.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id, *extension);
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
// Grant "always on this site" access to the extension.
PermissionsManagerWaiter waiter(PermissionsManager::Get(profile()));
SitePermissionsHelper permissions(profile());
permissions.UpdateSiteAccess(*extension, web_contents,
PermissionsManager::UserSiteAccess::kOnSite);
waiter.WaitForExtensionPermissionsUpdate();
// Request should be removed since extension has granted site access.
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
}
// Test request is removed when extension is granted one-time site access.
TEST_F(SiteAccessRequestsHelperUnittest,
RequestRemovedWhenExtensionHasGrantedActiveTab) {
auto extension = InstallExtensionWithActiveTab("Extension");
content::WebContents* web_contents =
AddTab(GURL("http://www.same-origin.com/a"));
int tab_id = ExtensionTabUtil::GetTabId(web_contents);
// Add site access request for extension.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id, *extension);
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
// Grant tab permission to extension.
ActiveTabPermissionGranter* active_tab_permission_granter =
TabHelper::FromWebContents(
browser()->tab_strip_model()->GetActiveWebContents())
->active_tab_permission_granter();
ASSERT_TRUE(active_tab_permission_granter);
active_tab_permission_granter->GrantIfRequested(extension.get());
// Request should be removed since extension has granted site access.
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension->id()));
}
// Test request is removed when extension is unloaded.
TEST_F(SiteAccessRequestsHelperUnittest,
RequestRemovedWhenExtensionIsUnloaded) {
auto extension_A =
InstallExtensionAndWithholdHostPermissions("Extension A", "<all_urls>");
auto extension_B =
InstallExtensionAndWithholdHostPermissions("Extension B", "<all_urls>");
content::WebContents* web_contents =
AddTab(GURL("http://www.same-origin.com/a"));
int tab_id = ExtensionTabUtil::GetTabId(web_contents);
// Add site access request for both extensions.
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id,
*extension_A);
permissions_manager()->AddSiteAccessRequest(web_contents, tab_id,
*extension_B);
ASSERT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
ASSERT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
// Uninstall extension A. Verify only extension B should have a site access
// request.
service()->UninstallExtension(
extension_A->id(), extensions::UNINSTALL_REASON_FOR_TESTING, nullptr);
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
EXPECT_TRUE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
// Disable extension B. Verify no extension should have a site access request.
service()->DisableExtension(extension_B->id(),
extensions::disable_reason::DISABLE_USER_ACTION);
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
// Enable extension B. Verify no extension has a site access request. Request
// is not persisted when extension is re-enabled, the extension needs to add
// the request again.
service()->EnableExtension(extension_B->id());
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_A->id()));
EXPECT_FALSE(permissions_manager()->HasActiveSiteAccessRequest(
tab_id, extension_B->id()));
}
} // namespace extensions