blob: 68c0a92e340da9750e1fcff31bd267476f9f0e00 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/extension_navigation_throttle.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/browser/url_request_util.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
#include "extensions/common/manifest_handlers/webview_info.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "ui/base/page_transition_types.h"
namespace extensions {
ExtensionNavigationThrottle::ExtensionNavigationThrottle(
content::NavigationHandle* navigation_handle)
: content::NavigationThrottle(navigation_handle) {}
ExtensionNavigationThrottle::~ExtensionNavigationThrottle() {}
content::NavigationThrottle::ThrottleCheckResult
ExtensionNavigationThrottle::WillStartRequest() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
GURL url(navigation_handle()->GetURL());
content::WebContents* web_contents = navigation_handle()->GetWebContents();
ExtensionRegistry* registry =
ExtensionRegistry::Get(web_contents->GetBrowserContext());
if (navigation_handle()->IsInMainFrame()) {
// Block top-level navigations to blob: or filesystem: URLs with extension
// origin from non-extension processes. See https://crbug.com/645028.
bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob();
bool is_extension = false;
if (registry) {
is_extension = !!registry->enabled_extensions().GetExtensionOrAppByURL(
navigation_handle()->GetStartingSiteInstance()->GetSiteURL());
}
url::Origin origin(url);
if (is_nested_url && origin.scheme() == extensions::kExtensionScheme &&
!is_extension) {
// Relax this restriction for apps that use <webview>. See
// https://crbug.com/652077.
const extensions::Extension* extension =
registry->enabled_extensions().GetByID(origin.host());
bool has_webview_permission =
extension &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kWebView);
if (!has_webview_permission)
return content::NavigationThrottle::CANCEL;
}
if (content::IsBrowserSideNavigationEnabled() &&
url.scheme() == extensions::kExtensionScheme) {
// This logic is performed for PlzNavigate sub-resources and for
// non-PlzNavigate in
// extensions::url_request_util::AllowCrossRendererResourceLoad.
const Extension* extension =
registry->enabled_extensions().GetExtensionOrAppByURL(url);
guest_view::GuestViewBase* guest =
guest_view::GuestViewBase::FromWebContents(web_contents);
if (guest) {
std::string owner_extension_id = guest->owner_host();
const Extension* owner_extension =
registry->enabled_extensions().GetByID(owner_extension_id);
std::string partition_domain, partition_id;
bool in_memory;
std::string resource_path = url.path();
bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite(
navigation_handle()->GetStartingSiteInstance()->GetSiteURL(),
&partition_domain, &partition_id, &in_memory);
bool allowed = true;
url_request_util::AllowCrossRendererResourceLoadHelper(
is_guest, extension, owner_extension, partition_id, resource_path,
navigation_handle()->GetPageTransition(), &allowed);
if (!allowed)
return content::NavigationThrottle::CANCEL;
}
}
return content::NavigationThrottle::PROCEED;
}
// Now enforce web_accessible_resources for navigations. Top-level navigations
// should always be allowed.
// If the navigation is not to a chrome-extension:// URL, no need to perform
// any more checks.
if (!url.SchemeIs(extensions::kExtensionScheme))
return content::NavigationThrottle::PROCEED;
// The subframe which is navigated needs to have all of its ancestors be
// at the same origin, otherwise the resource needs to be explicitly listed
// in web_accessible_resources.
// Since the RenderFrameHost is not known until navigation has committed,
// we can't get it from NavigationHandle. However, this code only cares about
// the ancestor chain, so find the current RenderFrameHost and use it to
// traverse up to the main frame.
content::RenderFrameHost* navigating_frame = nullptr;
for (auto* frame : web_contents->GetAllFrames()) {
if (frame->GetFrameTreeNodeId() ==
navigation_handle()->GetFrameTreeNodeId()) {
navigating_frame = frame;
break;
}
}
DCHECK(navigating_frame);
// Traverse the chain of parent frames, checking if they are the same origin
// as the URL of this navigation.
content::RenderFrameHost* ancestor = navigating_frame->GetParent();
bool external_ancestor = false;
while (ancestor) {
if (ancestor->GetLastCommittedURL().GetOrigin() != url.GetOrigin()) {
// Ignore DevTools, as it is allowed to embed extension pages.
if (!ancestor->GetLastCommittedURL().SchemeIs(
content::kChromeDevToolsScheme)) {
external_ancestor = true;
break;
}
}
ancestor = ancestor->GetParent();
}
if (!external_ancestor)
return content::NavigationThrottle::PROCEED;
// Since there was at least one origin different than the navigation URL,
// explicitly check for the resource in web_accessible_resources.
std::string resource_path = url.path();
if (!registry)
return content::NavigationThrottle::BLOCK_REQUEST;
const extensions::Extension* extension =
registry->enabled_extensions().GetByID(url.host());
if (!extension)
return content::NavigationThrottle::BLOCK_REQUEST;
if (WebAccessibleResourcesInfo::IsResourceWebAccessible(extension,
resource_path)) {
return content::NavigationThrottle::PROCEED;
}
return content::NavigationThrottle::BLOCK_REQUEST;
}
} // namespace extensions