#include "extensions/browser/permissions/active_tab_permission_granter.h"
#include <set>
#include <vector>
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/network_permissions_updater.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/renderer_startup_helper.h"
#include "extensions/browser/script_injection_tracker.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest_handlers/incognito_info.h"
#include "extensions/common/mojom/renderer.mojom.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/url_pattern_set.h"
#include "extensions/common/user_script.h"
#include "url/gurl.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
namespace extensions {
namespace {
using RendererMessageFunction =
base::RepeatingCallback<void(bool, content::RenderProcessHost*)>;
void UpdateTabSpecificPermissions(const ExtensionId& extension_id,
const extensions::URLPatternSet& new_hosts,
int tab_id,
bool update_origin_allowlist,
content::RenderProcessHost* process) {
mojom::Renderer* renderer =
RendererStartupHelperFactory::GetForBrowserContext(
process->GetBrowserContext())
->GetRenderer(process);
if (renderer) {
renderer->UpdateTabSpecificPermissions(extension_id, new_hosts.Clone(),
tab_id, update_origin_allowlist);
}
}
void ClearTabSpecificPermissions(const std::vector<ExtensionId>& extension_ids,
int tab_id,
bool update_origin_allowlist,
content::RenderProcessHost* process) {
mojom::Renderer* renderer =
RendererStartupHelperFactory::GetForBrowserContext(
process->GetBrowserContext())
->GetRenderer(process);
if (renderer) {
renderer->ClearTabSpecificPermissions(extension_ids, tab_id,
update_origin_allowlist);
}
}
void SendRendererMessageToProcesses(
const std::set<content::RenderFrameHost*>& frame_hosts,
content::RenderProcessHost* tab_process,
const RendererMessageFunction& renderer_message) {
std::set<content::RenderProcessHost*> sent_to_hosts;
for (content::RenderFrameHost* frame_host : frame_hosts) {
content::RenderProcessHost* process_host = frame_host->GetProcess();
if (!base::Contains(sent_to_hosts, process_host)) {
renderer_message.Run(true, process_host);
sent_to_hosts.insert(frame_host->GetProcess());
}
}
if (!base::Contains(sent_to_hosts, tab_process)) {
renderer_message.Run(false, tab_process);
}
}
void SetCorsOriginAccessList(content::BrowserContext* browser_context,
const Extension& extension,
base::OnceClosure closure) {
NetworkPermissionsUpdater::ContextSet context_set =
IncognitoInfo::IsSplitMode(&extension)
? NetworkPermissionsUpdater::ContextSet::kCurrentContextOnly
: NetworkPermissionsUpdater::ContextSet::kAllRelatedContexts;
NetworkPermissionsUpdater::UpdateExtension(*browser_context, extension,
context_set, std::move(closure));
}
}
ActiveTabPermissionGranter::ActiveTabPermissionGranter(
content::WebContents* web_contents,
int tab_id,
content::BrowserContext* browser_context)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<ActiveTabPermissionGranter>(*web_contents),
tab_id_(tab_id) {
CHECK_NE(tab_id_, extension_misc::kUnknownTabId);
extension_registry_observation_.Observe(
ExtensionRegistry::Get(browser_context));
}
ActiveTabPermissionGranter::~ActiveTabPermissionGranter() = default;
void ActiveTabPermissionGranter::GrantIfRequested(const Extension* extension) {
if (granted_extensions_.Contains(extension->id())) {
return;
}
APIPermissionSet new_apis;
URLPatternSet new_hosts;
const PermissionsData* permissions_data = extension->permissions_data();
const GURL& url = web_contents()->GetLastCommittedURL();
content::BrowserContext* browser_context =
web_contents()->GetBrowserContext();
if (permissions_data->HasAPIPermission(mojom::APIPermissionID::kActiveTab) ||
permissions_data->withheld_permissions().effective_hosts().MatchesURL(
url)) {
int valid_schemes = UserScript::ValidUserScriptSchemes();
if (!util::AllowFileAccess(extension->id(), browser_context)) {
valid_schemes &= ~URLPattern::SCHEME_FILE;
}
new_hosts.AddOrigin(valid_schemes, url);
new_apis.insert(mojom::APIPermissionID::kTab);
if (permissions_data->HasAPIPermission(
mojom::APIPermissionID::kDeclarativeNetRequest) ||
permissions_data->HasAPIPermission(
mojom::APIPermissionID::kDeclarativeNetRequestWithHostAccess)) {
new_apis.insert(mojom::APIPermissionID::kDeclarativeNetRequestFeedback);
}
}
if (permissions_data->HasAPIPermission(mojom::APIPermissionID::kTabCapture)) {
new_apis.insert(mojom::APIPermissionID::kTabCaptureForTab);
}
if (!new_apis.empty() || !new_hosts.is_empty()) {
granted_extensions_.Insert(extension);
PermissionSet new_permissions(std::move(new_apis), ManifestPermissionSet(),
new_hosts.Clone(), new_hosts.Clone());
permissions_data->UpdateTabSpecificPermissions(tab_id_, new_permissions);
SetCorsOriginAccessList(browser_context, *extension, base::DoNothing());
if (web_contents()->GetController().GetVisibleEntry()) {
ProcessManager* process_manager = ProcessManager::Get(browser_context);
content::RenderProcessHost* process =
web_contents()->GetPrimaryMainFrame()->GetProcess();
ScriptInjectionTracker::WillGrantActiveTab(
base::PassKey<ActiveTabPermissionGranter>(), *extension, *process);
RendererMessageFunction update_message =
base::BindRepeating(&UpdateTabSpecificPermissions, extension->id(),
new_hosts.Clone(), tab_id_);
SendRendererMessageToProcesses(
process_manager->GetRenderFrameHostsForExtension(extension->id()),
process, update_message);
ExtensionsBrowserClient::Get()->OnActiveTabPermissionGranted(
extension, web_contents());
auto* permissions_manager =
PermissionsManager::Get(web_contents()->GetBrowserContext());
permissions_manager->NotifyActiveTabPermisssionGranted(
web_contents(), tab_id_, *extension);
}
}
}
void ActiveTabPermissionGranter::RevokeForTesting() {
ClearGrantedExtensionsAndNotify();
}
void ActiveTabPermissionGranter::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInPrimaryMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
if (navigation_handle->IsSameOrigin()) {
return;
}
ClearGrantedExtensionsAndNotify();
}
void ActiveTabPermissionGranter::WebContentsDestroyed() {
ClearGrantedExtensionsAndNotify();
}
void ActiveTabPermissionGranter::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) {
granted_extensions_.Remove(extension->id());
}
void ActiveTabPermissionGranter::ClearGrantedExtensionsAndNotify() {
ClearGrantedExtensionsAndNotify(granted_extensions_);
}
void ActiveTabPermissionGranter::ClearActiveExtensionAndNotify(
const ExtensionId& id) {
if (!granted_extensions_.Contains(id)) {
return;
}
ExtensionSet granted_to_remove{};
granted_to_remove.Insert(granted_extensions_.GetByID(id));
ClearGrantedExtensionsAndNotify(granted_to_remove);
}
void ActiveTabPermissionGranter::ClearGrantedExtensionsAndNotify(
const ExtensionSet& granted_extensions_to_remove) {
if (granted_extensions_to_remove.empty()) {
return;
}
std::set<content::RenderProcessHost*> extension_processes;
std::vector<ExtensionId> extension_ids;
content::BrowserContext* browser_context =
web_contents()->GetBrowserContext();
ProcessManager* process_manager = ProcessManager::Get(browser_context);
for (const scoped_refptr<const Extension>& extension :
granted_extensions_to_remove) {
extension->permissions_data()->ClearTabSpecificPermissions(tab_id_);
SetCorsOriginAccessList(browser_context, *extension, base::DoNothing());
extension_ids.push_back(extension->id());
for (content::RenderFrameHost* extension_frame_host :
process_manager->GetRenderFrameHostsForExtension(extension->id())) {
extension_processes.insert(extension_frame_host->GetProcess());
}
}
for (content::RenderProcessHost::iterator host_iterator(
content::RenderProcessHost::AllHostsIterator());
!host_iterator.IsAtEnd(); host_iterator.Advance()) {
content::RenderProcessHost* process = host_iterator.GetCurrentValue();
if (!process->IsInitializedAndNotDead()) {
continue;
}
if (process->GetBrowserContext() != browser_context) {
continue;
}
bool update_origin_allowlist = extension_processes.contains(process);
ClearTabSpecificPermissions(extension_ids, tab_id_, update_origin_allowlist,
process);
}
for (const auto& id : extension_ids) {
granted_extensions_.Remove(id);
}
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(ActiveTabPermissionGranter);
}