[Extensions] Restrict debugging file:-scheme URLs
Don't allow extensions to debug file:-scheme URLs if the extension does
not have explicit file access (as set in chrome://extensions). Achieve
this by introducing a new virtual method on DevToolsAgentHostClient to
allow the implementor to check if a given host is allowed to be
inspected.
Add regression tests for the same.
Bug: 666299
Change-Id: Icb5ee89bf788643eee166eef83802d10ab825a6c
Reviewed-on: https://chromium-review.googlesource.com/1104954
Commit-Queue: Devlin <[email protected]>
Reviewed-by: Dmitry Gozman <[email protected]>
Cr-Commit-Position: refs/heads/master@{#569828}
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 94097ccd7..c3ce2dd 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -29,6 +29,7 @@
#include "chrome/browser/extensions/api/debugger/extension_dev_tools_infobar.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
@@ -83,6 +84,26 @@
dst->target_id.reset(new std::string(*src.target_id));
}
+// Returns true if the given |Extension| is allowed to attach to the specified
+// |url|.
+bool ExtensionCanAttachToURL(const Extension& extension,
+ const GURL& url,
+ Profile* profile,
+ std::string* error) {
+ // NOTE: The `debugger` permission implies all URLs access (and indicates
+ // such to the user), so we don't check explicit page access. However, we
+ // still need to check if it's an otherwise-restricted URL.
+ if (extension.permissions_data()->IsRestrictedUrl(url, error))
+ return false;
+
+ if (url.SchemeIsFile() && !util::AllowFileAccess(extension.id(), profile)) {
+ *error = debugger_api_constants::kRestrictedError;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
// ExtensionDevToolsClientHost ------------------------------------------------
@@ -97,14 +118,13 @@
public:
ExtensionDevToolsClientHost(Profile* profile,
DevToolsAgentHost* agent_host,
- const std::string& extension_id,
- const std::string& extension_name,
+ scoped_refptr<const Extension> extension,
const Debuggee& debuggee);
~ExtensionDevToolsClientHost() override;
bool Attach();
- const std::string& extension_id() { return extension_id_; }
+ const std::string& extension_id() { return extension_->id(); }
DevToolsAgentHost* agent_host() { return agent_host_.get(); }
void RespondDetachedToPendingRequests();
void Close();
@@ -119,6 +139,8 @@
void AgentHostClosed(DevToolsAgentHost* agent_host) override;
void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) override;
+ bool MayAttachToRenderer(content::RenderFrameHost* render_frame_host,
+ bool is_webui) override;
private:
using PendingRequests =
@@ -138,8 +160,7 @@
Profile* profile_;
scoped_refptr<DevToolsAgentHost> agent_host_;
- std::string extension_id_;
- std::string extension_name_;
+ scoped_refptr<const Extension> extension_;
Debuggee debuggee_;
content::NotificationRegistrar registrar_;
int last_request_id_;
@@ -157,13 +178,11 @@
ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
Profile* profile,
DevToolsAgentHost* agent_host,
- const std::string& extension_id,
- const std::string& extension_name,
+ scoped_refptr<const Extension> extension,
const Debuggee& debuggee)
: profile_(profile),
agent_host_(agent_host),
- extension_id_(extension_id),
- extension_name_(extension_name),
+ extension_(std::move(extension)),
last_request_id_(0),
infobar_(nullptr),
detach_reason_(api::debugger::DETACH_REASON_TARGET_CLOSED),
@@ -195,17 +214,11 @@
// We allow policy-installed extensions to circumvent the normal
// infobar warning. See crbug.com/693621.
- const Extension* extension =
- ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
- extension_id_);
- // TODO(dgozman): null-checking |extension| below is sketchy.
- // We probably should not allow debugging in this case. Or maybe
- // it's never null?
- if (extension && Manifest::IsPolicyLocation(extension->location()))
+ if (Manifest::IsPolicyLocation(extension_->location()))
return true;
infobar_ = ExtensionDevToolsInfoBar::Create(
- extension_id_, extension_name_, this,
+ extension_id(), extension_->name(), this,
base::Bind(&ExtensionDevToolsClientHost::InfoBarDismissed,
base::Unretained(this)));
return true;
@@ -272,15 +285,15 @@
auto event =
std::make_unique<Event>(events::DEBUGGER_ON_DETACH, OnDetach::kEventName,
std::move(args), profile_);
- EventRouter::Get(profile_)
- ->DispatchEventToExtension(extension_id_, std::move(event));
+ EventRouter::Get(profile_)->DispatchEventToExtension(extension_id(),
+ std::move(event));
}
void ExtensionDevToolsClientHost::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) {
- if (extension->id() == extension_id_)
+ if (extension->id() == extension_id())
Close();
}
@@ -320,8 +333,8 @@
auto event =
std::make_unique<Event>(events::DEBUGGER_ON_EVENT, OnEvent::kEventName,
std::move(args), profile_);
- EventRouter::Get(profile_)
- ->DispatchEventToExtension(extension_id_, std::move(event));
+ EventRouter::Get(profile_)->DispatchEventToExtension(extension_id(),
+ std::move(event));
} else {
DebuggerSendCommandFunction* function = pending_requests_[id].get();
if (!function)
@@ -332,6 +345,31 @@
}
}
+bool ExtensionDevToolsClientHost::MayAttachToRenderer(
+ content::RenderFrameHost* render_frame_host,
+ bool is_webui) {
+ if (is_webui)
+ return false;
+
+ if (!render_frame_host)
+ return true;
+
+ std::string error;
+ // We check the site instance URL here (instead of
+ // RenderFrameHost::GetLastCommittedURL()) because it's too early in the
+ // navigation for anything else.
+ const GURL& site_instance_url =
+ render_frame_host->GetSiteInstance()->GetSiteURL();
+
+ if (site_instance_url.is_empty()) {
+ // |site_instance_url| is empty for about:blank. Allow the extension to
+ // attach.
+ return true;
+ }
+
+ return ExtensionCanAttachToURL(*extension_, site_instance_url, profile_,
+ &error);
+}
// DebuggerFunction -----------------------------------------------------------
@@ -366,8 +404,10 @@
if (result && web_contents) {
// TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL().
GURL url = web_contents->GetVisibleURL();
- if (extension()->permissions_data()->IsRestrictedUrl(url, &error_))
+
+ if (!ExtensionCanAttachToURL(*extension(), url, GetProfile(), &error_))
return false;
+
agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
}
} else if (debuggee_.extension_id) {
@@ -463,8 +503,7 @@
}
auto host = std::make_unique<ExtensionDevToolsClientHost>(
- GetProfile(), agent_host_.get(), extension()->id(), extension()->name(),
- debuggee_);
+ GetProfile(), agent_host_.get(), extension(), debuggee_);
if (!host->Attach()) {
FormatErrorMessage(debugger_api_constants::kRestrictedError);