Files
brave-core/browser/permissions/permission_manager_browsertest.cc
cypt4 110754bdc0 [CodeHealth] Brave wallet provider origin refactoring (#33658)
* [CodeHealth] Refactor permissions origin
1) Pass origin to dApp on construction instead of
resolving it from delegate.
2) Pass origin to iOS panel ui from the core side.
2026-02-16 16:12:11 +04:00

453 lines
18 KiB
C++

/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_wallet/common/buildflags/buildflags.h"
#if BUILDFLAG(ENABLE_BRAVE_WALLET)
#include <array>
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/test/test_future.h"
#include "base/types/zip.h"
#include "brave/components/brave_wallet/browser/permission_utils.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
#include "brave/components/permissions/contexts/brave_wallet_permission_context.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.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_types.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/request_type.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "url/gurl.h"
#include "url/origin.h"
using base::test::TestFuture;
using testing::ElementsAreArray;
namespace permissions {
namespace {
class PermissionRequestManagerObserver
: public PermissionRequestManager::Observer {
public:
explicit PermissionRequestManagerObserver(PermissionRequestManager* manager)
: manager_(manager) {
manager_->AddObserver(this);
}
~PermissionRequestManagerObserver() override {
manager_->RemoveObserver(this);
}
// PermissionRequestManager::Observer:
void OnPromptAdded() override { is_showing_bubble_ = true; }
void OnPromptRemoved() override { is_showing_bubble_ = false; }
void OnRequestsFinalized() override { is_requests_finalized_ = true; }
bool IsShowingBubble() { return is_showing_bubble_; }
bool IsRequestsFinalized() { return is_requests_finalized_; }
void Reset() { is_showing_bubble_ = is_requests_finalized_ = false; }
private:
raw_ptr<PermissionRequestManager> manager_ = nullptr;
bool is_showing_bubble_ = false;
bool is_requests_finalized_ = false;
};
} // namespace
class PermissionManagerBrowserTest : public InProcessBrowserTest {
public:
PermissionManagerBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
~PermissionManagerBrowserTest() override = default;
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server()->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_server()->Start());
}
PermissionRequestManager* GetPermissionRequestManager() {
return PermissionRequestManager::FromWebContents(
browser()->tab_strip_model()->GetActiveWebContents());
}
HostContentSettingsMap* host_content_settings_map(Profile* profile) {
return HostContentSettingsMapFactory::GetForProfile(profile);
}
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
url::Origin GetLastCommitedOrigin() {
return url::Origin::Create(web_contents()->GetLastCommittedURL());
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
protected:
net::test_server::EmbeddedTestServer https_server_;
};
IN_PROC_BROWSER_TEST_F(PermissionManagerBrowserTest, RequestPermissions) {
const GURL& url = https_server()->GetURL("a.test", "/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
auto* permission_request_manager = GetPermissionRequestManager();
EXPECT_FALSE(permission_request_manager->IsRequestInProgress());
struct TestEntries {
std::vector<std::string> addresses;
ContentSettingsType type;
blink::PermissionType permission;
};
auto cases = std::to_array<TestEntries>(
{{{"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8A",
"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8B"},
ContentSettingsType::BRAVE_ETHEREUM,
blink::PermissionType::BRAVE_ETHEREUM},
{{"BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8",
"JDqrvDz8d8tFCADashbUKQDKfJZFobNy13ugN65t1wvV"},
ContentSettingsType::BRAVE_SOLANA,
blink::PermissionType::BRAVE_SOLANA},
{{"1815_0_0_0", "1815_1_0_0"},
ContentSettingsType::BRAVE_CARDANO,
blink::PermissionType::BRAVE_CARDANO}});
for (auto& test_case : cases) {
SCOPED_TRACE(testing::Message() << test_case.type);
const std::vector<std::string>& addresses = test_case.addresses;
RequestType request_type = ContentSettingsTypeToRequestType(test_case.type);
std::vector<url::Origin> sub_request_origins;
for (auto& address : addresses) {
SCOPED_TRACE(testing::Message() << address);
auto sub_request_origin = brave_wallet::GetSubRequestOrigin(
request_type, GetLastCommitedOrigin(), address);
ASSERT_TRUE(sub_request_origin) << address;
sub_request_origins.push_back(*sub_request_origin);
}
auto observer = std::make_unique<PermissionRequestManagerObserver>(
permission_request_manager);
TestFuture<std::vector<std::string>> request_permissions_future;
BraveWalletPermissionContext::RequestWalletPermissions(
addresses, test_case.permission, url::Origin::Create(url),
web_contents()->GetPrimaryMainFrame(),
request_permissions_future.GetCallback());
content::RunAllTasksUntilIdle();
EXPECT_TRUE(permission_request_manager->IsRequestInProgress());
EXPECT_TRUE(observer->IsShowingBubble());
// update anchor should not dismiss the bubble
permission_request_manager->UpdateAnchor();
EXPECT_TRUE(observer->IsShowingBubble());
// Check sub-requests are created as expected.
EXPECT_EQ(permission_request_manager->Requests().size(), addresses.size());
for (const auto [request, sub_request_origin] : base::zip(
permission_request_manager->Requests(), sub_request_origins)) {
SCOPED_TRACE(testing::Message() << sub_request_origin);
EXPECT_EQ(request->request_type(), request_type);
EXPECT_EQ(sub_request_origin.GetURL(), request->requesting_origin());
}
// Test dismissing request.
permissions::BraveWalletPermissionContext::Cancel(web_contents());
EXPECT_TRUE(request_permissions_future.Take().empty());
EXPECT_TRUE(observer->IsRequestsFinalized());
EXPECT_TRUE(!observer->IsShowingBubble());
for (const auto& sub_request_origin : sub_request_origins) {
SCOPED_TRACE(testing::Message() << sub_request_origin);
EXPECT_EQ(host_content_settings_map(browser()->profile())
->GetContentSetting(sub_request_origin.GetURL(),
GetLastCommitedOrigin().GetURL(),
test_case.type),
ContentSetting::CONTENT_SETTING_ASK);
}
observer->Reset();
BraveWalletPermissionContext::RequestWalletPermissions(
addresses, test_case.permission, url::Origin::Create(url),
web_contents()->GetPrimaryMainFrame(),
request_permissions_future.GetCallback());
content::RunAllTasksUntilIdle();
EXPECT_TRUE(permission_request_manager->IsRequestInProgress());
EXPECT_TRUE(observer->IsShowingBubble());
// update anchor should not dismiss the bubble
permission_request_manager->UpdateAnchor();
EXPECT_TRUE(observer->IsShowingBubble());
// Check sub-requests are created as expected.
EXPECT_EQ(permission_request_manager->Requests().size(), addresses.size());
for (const auto [request, sub_request_origin] : base::zip(
permission_request_manager->Requests(), sub_request_origins)) {
SCOPED_TRACE(testing::Message() << sub_request_origin);
EXPECT_EQ(request->request_type(), request_type);
EXPECT_EQ(sub_request_origin.GetURL(), request->requesting_origin());
}
// Test accepting request with one of the address.
permissions::BraveWalletPermissionContext::AcceptOrCancel(
std::vector<std::string>{test_case.addresses[1]},
brave_wallet::mojom::PermissionLifetimeOption::kForever,
web_contents());
EXPECT_THAT(request_permissions_future.Take(),
ElementsAreArray({test_case.addresses[1]}));
std::vector<ContentSetting> expected_settings(
{ContentSetting::CONTENT_SETTING_ASK,
ContentSetting::CONTENT_SETTING_ALLOW});
EXPECT_TRUE(observer->IsRequestsFinalized());
EXPECT_TRUE(!observer->IsShowingBubble());
for (const auto [setting, sub_request_origin] :
base::zip(expected_settings, sub_request_origins)) {
SCOPED_TRACE(testing::Message() << sub_request_origin);
EXPECT_EQ(host_content_settings_map(browser()->profile())
->GetContentSetting(sub_request_origin.GetURL(),
GetLastCommitedOrigin().GetURL(),
test_case.type),
setting);
}
}
}
IN_PROC_BROWSER_TEST_F(PermissionManagerBrowserTest,
RequestPermissionsTabClosed) {
const GURL& url = https_server()->GetURL("a.test", "/empty.html");
struct TestEntries {
std::vector<std::string> addresses;
ContentSettingsType type;
blink::PermissionType permission;
};
auto cases = std::to_array<TestEntries>(
{{{"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8C",
"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8D"},
ContentSettingsType::BRAVE_ETHEREUM,
blink::PermissionType::BRAVE_ETHEREUM},
{{"BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8",
"JDqrvDz8d8tFCADashbUKQDKfJZFobNy13ugN65t1wvV"},
ContentSettingsType::BRAVE_SOLANA,
blink::PermissionType::BRAVE_SOLANA},
{{"1815_1_1_0"},
ContentSettingsType::BRAVE_CARDANO,
blink::PermissionType::BRAVE_CARDANO}});
for (auto& test_case : cases) {
SCOPED_TRACE(testing::Message() << test_case.type);
ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 0, url,
ui::PAGE_TRANSITION_TYPED, true));
auto* permission_request_manager = GetPermissionRequestManager();
EXPECT_FALSE(permission_request_manager->IsRequestInProgress());
const std::vector<std::string>& addresses = test_case.addresses;
RequestType request_type = ContentSettingsTypeToRequestType(test_case.type);
std::vector<blink::PermissionType> permissions(addresses.size(),
test_case.permission);
std::vector<url::Origin> sub_request_origins;
for (const auto& address : test_case.addresses) {
url::Origin origin;
auto sub_request_origin = brave_wallet::GetSubRequestOrigin(
request_type, GetLastCommitedOrigin(), address);
ASSERT_TRUE(sub_request_origin) << address;
sub_request_origins.push_back(*sub_request_origin);
}
auto observer = std::make_unique<PermissionRequestManagerObserver>(
permission_request_manager);
BraveWalletPermissionContext::RequestWalletPermissions(
addresses, test_case.permission, url::Origin::Create(url),
web_contents()->GetPrimaryMainFrame(), base::DoNothing());
content::RunAllTasksUntilIdle();
EXPECT_TRUE(permission_request_manager->IsRequestInProgress());
EXPECT_TRUE(observer->IsShowingBubble());
// update anchor should not dismiss the bubble
permission_request_manager->UpdateAnchor();
EXPECT_TRUE(observer->IsShowingBubble());
// Check sub-requests are created as expected.
EXPECT_EQ(permission_request_manager->Requests().size(), addresses.size());
for (const auto [request, sub_request_origin] : base::zip(
permission_request_manager->Requests(), sub_request_origins)) {
SCOPED_TRACE(testing::Message() << sub_request_origin);
EXPECT_EQ(request->request_type(), request_type);
EXPECT_EQ(sub_request_origin.GetURL(), request->requesting_origin());
}
// Remove the observer before closing the tab.
observer.reset();
// Close tab with active request pending.
content::WebContentsDestroyedWatcher tab_destroyed_watcher(web_contents());
browser()->tab_strip_model()->CloseWebContentsAt(0,
TabCloseTypes::CLOSE_NONE);
tab_destroyed_watcher.Wait();
}
}
IN_PROC_BROWSER_TEST_F(PermissionManagerBrowserTest, GetCanonicalOrigin) {
const GURL& url = https_server()->GetURL("a.test", "/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
struct TestEntries {
std::vector<std::string> addresses;
ContentSettingsType type;
};
auto cases = std::to_array<TestEntries>(
{{{"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8A",
"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8B"},
ContentSettingsType::BRAVE_ETHEREUM},
{{"BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8",
"JDqrvDz8d8tFCADashbUKQDKfJZFobNy13ugN65t1wvV"},
ContentSettingsType::BRAVE_SOLANA},
{{"addr1q8gg2r3vf9zggn48g7m8vx62rwf6warcs4k7ej8mdzmqmesj30jz7psduyk6n4n2"
"qrud2xlv9fgj53n6ds3t8cs4fvzs05yzmz",
"Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b"},
ContentSettingsType::BRAVE_CARDANO}});
for (auto& test_case : cases) {
SCOPED_TRACE(testing::Message() << test_case.type);
for (auto& address : test_case.addresses) {
auto origin = brave_wallet::GetSubRequestOrigin(
ContentSettingsTypeToRequestType(test_case.type),
GetLastCommitedOrigin(), address);
ASSERT_TRUE(origin);
EXPECT_EQ(origin->GetURL(),
permissions::PermissionUtil::GetCanonicalOrigin(
test_case.type, origin->GetURL(),
GetLastCommitedOrigin().GetURL()))
<< "GetCanonicalOrigin should return requesting_origin for Ethereum "
"permission.";
}
}
}
class PermissionManagerIncognitoBrowserTest : public InProcessBrowserTest {
public:
PermissionManagerIncognitoBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
~PermissionManagerIncognitoBrowserTest() override = default;
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server()->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_server()->Start());
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
void TestRequestPermissionsDoNotLeak(Browser* browser1, Browser* browser2) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser1, https_server()->GetURL("a.test", "/empty.html")));
auto* web_contents = browser1->tab_strip_model()->GetActiveWebContents();
auto active_origin =
url::Origin::Create(web_contents->GetLastCommittedURL());
const std::string address = "0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8A";
ContentSettingsType type = ContentSettingsType::BRAVE_ETHEREUM;
blink::PermissionType permission = blink::PermissionType::BRAVE_ETHEREUM;
RequestType request_type = ContentSettingsTypeToRequestType(type);
auto sub_request_origin =
brave_wallet::GetSubRequestOrigin(request_type, active_origin, address);
ASSERT_TRUE(sub_request_origin);
auto observer = std::make_unique<PermissionRequestManagerObserver>(
PermissionRequestManager::FromWebContents(web_contents));
TestFuture<std::vector<std::string>> request_permissions_future;
BraveWalletPermissionContext::RequestWalletPermissions(
{address}, permission, active_origin,
web_contents->GetPrimaryMainFrame(),
request_permissions_future.GetCallback());
content::RunAllTasksUntilIdle();
permissions::BraveWalletPermissionContext::AcceptOrCancel(
{address}, brave_wallet::mojom::PermissionLifetimeOption::kForever,
web_contents);
EXPECT_THAT(request_permissions_future.Take(), ElementsAreArray({address}));
EXPECT_TRUE(observer->IsRequestsFinalized());
EXPECT_TRUE(!observer->IsShowingBubble());
// Verify the permission has changed for profile1
EXPECT_EQ(
HostContentSettingsMapFactory::GetForProfile(browser1->GetProfile())
->GetContentSetting(sub_request_origin->GetURL(),
active_origin.GetURL(), type),
ContentSetting::CONTENT_SETTING_ALLOW);
// Verify the permission hasn't leaked to profile2
EXPECT_EQ(
HostContentSettingsMapFactory::GetForProfile(browser2->GetProfile())
->GetContentSetting(sub_request_origin->GetURL(),
active_origin.GetURL(), type),
ContentSetting::CONTENT_SETTING_ASK);
}
protected:
net::test_server::EmbeddedTestServer https_server_;
};
IN_PROC_BROWSER_TEST_F(PermissionManagerIncognitoBrowserTest,
IncognitoPermissionsDoNotLeak) {
auto* incognito_browser = CreateIncognitoBrowser(browser()->profile());
// Verify permissions do not leak from incognito profile into normal profile.
TestRequestPermissionsDoNotLeak(incognito_browser, browser());
}
IN_PROC_BROWSER_TEST_F(PermissionManagerIncognitoBrowserTest,
PermissionsDoNotLeak) {
auto* incognito_browser = CreateIncognitoBrowser(browser()->profile());
// Verify permissions do not leak from normal profile into incognito profile.
TestRequestPermissionsDoNotLeak(browser(), incognito_browser);
}
} // namespace permissions
#endif // BUILDFLAG(ENABLE_BRAVE_WALLET)