chore: update extensions url handling to match upstream (#40038)

- 4772028
- 4264656
- 4712150
This commit is contained in:
Shelley Vohr 2023-10-02 10:35:00 +02:00 committed by GitHub
parent 371e83a8d2
commit 5643e86956
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 158 additions and 66 deletions

View file

@ -7,20 +7,27 @@
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/strings/pattern.h"
#include "base/types/expected_macros.h"
#include "chrome/common/url_constants.h"
#include "components/url_formatter/url_fixer.h"
#include "content/public/browser/navigation_entry.h"
#include "extensions/browser/extension_api_frame_id_map.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/mojom/host_id.mojom.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/switches.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/native_window.h"
#include "shell/browser/web_contents_zoom_controller.h"
#include "shell/browser/window_list.h"
#include "shell/common/extensions/api/tabs.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "url/gurl.h"
@ -468,17 +475,25 @@ bool IsKillURL(const GURL& url) {
DCHECK(url.IsAboutBlank() || url.IsAboutSrcdoc());
#endif
static const char* const kill_hosts[] = {
chrome::kChromeUICrashHost, chrome::kChromeUIDelayedHangUIHost,
chrome::kChromeUIHangUIHost, chrome::kChromeUIKillHost,
// Disallow common renderer debug URLs.
// Note: this would also disallow JavaScript URLs, but we already explicitly
// check for those before calling into here from PrepareURLForNavigation.
if (blink::IsRendererDebugURL(url)) {
return true;
}
if (!url.SchemeIs(content::kChromeUIScheme)) {
return false;
}
// Also disallow a few more hosts which are not covered by the check above.
static const char* const kKillHosts[] = {
chrome::kChromeUIDelayedHangUIHost, chrome::kChromeUIHangUIHost,
chrome::kChromeUIQuitHost, chrome::kChromeUIRestartHost,
content::kChromeUIBrowserCrashHost, content::kChromeUIMemoryExhaustHost,
};
if (!url.SchemeIs(content::kChromeUIScheme))
return false;
return base::Contains(kill_hosts, url.host_piece());
return base::Contains(kKillHosts, url.host_piece());
}
GURL ResolvePossiblyRelativeURL(const std::string& url_string,
@ -489,10 +504,18 @@ GURL ResolvePossiblyRelativeURL(const std::string& url_string,
return url;
}
bool PrepareURLForNavigation(const std::string& url_string,
const Extension* extension,
GURL* return_url,
std::string* error) {
bool AllowFileAccess(const ExtensionId& extension_id,
content::BrowserContext* context) {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableExtensionsFileAccessCheck) ||
ExtensionPrefs::Get(context)->AllowFileAccess(extension_id);
}
base::expected<GURL, std::string> PrepareURLForNavigation(
const std::string& url_string,
const Extension* extension,
content::BrowserContext* browser_context) {
GURL url = ResolvePossiblyRelativeURL(url_string, extension);
// Ideally, the URL would only be "fixed" for user input (e.g. for URLs
@ -504,34 +527,62 @@ bool PrepareURLForNavigation(const std::string& url_string,
// Reject invalid URLs.
if (!url.is_valid()) {
const char kInvalidUrlError[] = "Invalid url: \"*\".";
*error = ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string);
return false;
return base::unexpected(
ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string));
}
// Don't let the extension use JavaScript URLs in API triggered navigations.
if (url.SchemeIs(url::kJavaScriptScheme)) {
const char kJavaScriptUrlsNotAllowedInExtensionNavigations[] =
"JavaScript URLs are not allowed in API based extension navigations. "
"Use "
"chrome.scripting.executeScript instead.";
return base::unexpected(kJavaScriptUrlsNotAllowedInExtensionNavigations);
}
// Don't let the extension crash the browser or renderers.
if (IsKillURL(url)) {
const char kNoCrashBrowserError[] =
"I'm sorry. I'm afraid I can't do that.";
*error = kNoCrashBrowserError;
return false;
return base::unexpected(kNoCrashBrowserError);
}
// Don't let the extension navigate directly to devtools scheme pages, unless
// they have applicable permissions.
if (url.SchemeIs(content::kChromeDevToolsScheme) &&
!(extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kDevtools) ||
extension->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::kDebugger))) {
const char kCannotNavigateToDevtools[] =
"Cannot navigate to a devtools:// page without either the devtools or "
"debugger permission.";
*error = kCannotNavigateToDevtools;
return false;
if (url.SchemeIs(content::kChromeDevToolsScheme)) {
bool has_permission =
extension && (extension->permissions_data()->HasAPIPermission(
mojom::APIPermissionID::kDevtools) ||
extension->permissions_data()->HasAPIPermission(
mojom::APIPermissionID::kDebugger));
if (!has_permission) {
const char kCannotNavigateToDevtools[] =
"Cannot navigate to a devtools:// page without either the devtools "
"or "
"debugger permission.";
return base::unexpected(kCannotNavigateToDevtools);
}
}
return_url->Swap(&url);
return true;
// Don't let the extension navigate directly to chrome-untrusted scheme pages.
if (url.SchemeIs(content::kChromeUIUntrustedScheme)) {
const char kCannotNavigateToChromeUntrusted[] =
"Cannot navigate to a chrome-untrusted:// page.";
return base::unexpected(kCannotNavigateToChromeUntrusted);
}
// Don't let the extension navigate directly to file scheme pages, unless
// they have file access.
if (url.SchemeIsFile() &&
!AllowFileAccess(extension->id(), browser_context) &&
base::FeatureList::IsEnabled(
extensions_features::kRestrictFileURLNavigation)) {
const char kFileUrlsNotAllowedInExtensionNavigations[] =
"Cannot navigate to a file URL without local file access.";
return base::unexpected(kFileUrlsNotAllowedInExtensionNavigations);
}
return url;
}
TabsUpdateFunction::TabsUpdateFunction() : web_contents_(nullptr) {}
@ -566,22 +617,14 @@ ExtensionFunction::ResponseAction TabsUpdateFunction::Run() {
bool TabsUpdateFunction::UpdateURL(const std::string& url_string,
int tab_id,
std::string* error) {
GURL url;
if (!PrepareURLForNavigation(url_string, extension(), &url, error)) {
auto url =
PrepareURLForNavigation(url_string, extension(), browser_context());
if (!url.has_value()) {
*error = std::move(url.error());
return false;
}
const bool is_javascript_scheme = url.SchemeIs(url::kJavaScriptScheme);
// JavaScript URLs are forbidden in chrome.tabs.update().
if (is_javascript_scheme) {
const char kJavaScriptUrlsNotAllowedInTabsUpdate[] =
"JavaScript URLs are not allowed in chrome.tabs.update. Use "
"chrome.tabs.executeScript instead.";
*error = kJavaScriptUrlsNotAllowedInTabsUpdate;
return false;
}
content::NavigationController::LoadURLParams load_params(url);
content::NavigationController::LoadURLParams load_params(*url);
// Treat extension-initiated navigations as renderer-initiated so that the URL
// does not show in the omnibox until it commits. This avoids URL spoofs