 Chromium Code Reviews
 Chromium Code Reviews Issue 2830893002:
  Refactor of ExtensionNavigationThrottle  (Closed)
    
  
    Issue 2830893002:
  Refactor of ExtensionNavigationThrottle  (Closed) 
  | OLD | NEW | 
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "extensions/browser/extension_navigation_throttle.h" | 5 #include "extensions/browser/extension_navigation_throttle.h" | 
| 6 | 6 | 
| 7 #include "components/guest_view/browser/guest_view_base.h" | 7 #include "components/guest_view/browser/guest_view_base.h" | 
| 8 #include "content/public/browser/browser_thread.h" | 8 #include "content/public/browser/browser_thread.h" | 
| 9 #include "content/public/browser/navigation_handle.h" | 9 #include "content/public/browser/navigation_handle.h" | 
| 10 #include "content/public/browser/render_frame_host.h" | 10 #include "content/public/browser/render_frame_host.h" | 
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 | 27 | 
| 28 ExtensionNavigationThrottle::ExtensionNavigationThrottle( | 28 ExtensionNavigationThrottle::ExtensionNavigationThrottle( | 
| 29 content::NavigationHandle* navigation_handle) | 29 content::NavigationHandle* navigation_handle) | 
| 30 : content::NavigationThrottle(navigation_handle) {} | 30 : content::NavigationThrottle(navigation_handle) {} | 
| 31 | 31 | 
| 32 ExtensionNavigationThrottle::~ExtensionNavigationThrottle() {} | 32 ExtensionNavigationThrottle::~ExtensionNavigationThrottle() {} | 
| 33 | 33 | 
| 34 content::NavigationThrottle::ThrottleCheckResult | 34 content::NavigationThrottle::ThrottleCheckResult | 
| 35 ExtensionNavigationThrottle::WillStartRequest() { | 35 ExtensionNavigationThrottle::WillStartRequest() { | 
| 36 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 36 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| 37 GURL url(navigation_handle()->GetURL()); | |
| 38 content::WebContents* web_contents = navigation_handle()->GetWebContents(); | 37 content::WebContents* web_contents = navigation_handle()->GetWebContents(); | 
| 39 ExtensionRegistry* registry = | 38 ExtensionRegistry* registry = | 
| 40 ExtensionRegistry::Get(web_contents->GetBrowserContext()); | 39 ExtensionRegistry::Get(web_contents->GetBrowserContext()); | 
| 41 | 40 | 
| 41 // Is this navigation targeting an extension resource? | |
| 42 const GURL& url = navigation_handle()->GetURL(); | |
| 43 bool url_has_extension_scheme = url.SchemeIs(kExtensionScheme); | |
| 44 url::Origin target_origin(url); | |
| 45 const Extension* target_extension = nullptr; | |
| 46 if (url_has_extension_scheme) { | |
| 47 // "chrome-extension://" URL. | |
| 48 target_extension = | |
| 49 registry->enabled_extensions().GetExtensionOrAppByURL(url); | |
| 50 } else if (target_origin.scheme() == kExtensionScheme) { | |
| 51 // "blob:chrome-extension://" or "filesystem:chrome-extension://" URL. | |
| 52 DCHECK(url.SchemeIsFileSystem() || url.SchemeIsBlob()); | |
| 53 target_extension = | |
| 54 registry->enabled_extensions().GetByID(target_origin.host()); | |
| 55 } else { | |
| 56 // If the navigation is not to a chrome-extension resource, no need to | |
| 57 // perform any more checks; it's outside of the purview of this throttle. | |
| 58 return content::NavigationThrottle::PROCEED; | |
| 59 } | |
| 60 | |
| 61 // If the navigation is to an unknown or disabled extension, block it. | |
| 62 if (!target_extension) { | |
| 63 if (url_has_extension_scheme) { | |
| 64 // Proceed in this case; the protocol handler will surface an error. | |
| 65 return content::NavigationThrottle::PROCEED; | |
| 
ncarter (slow)
2017/05/01 21:50:37
Since the previous patch set, I added this PROCEED
 | |
| 66 } | |
| 67 // Explicitly block blob: and filesystem: URLs with unknown extensions. | |
| 68 // Nothing good can come of those. | |
| 69 return content::NavigationThrottle::BLOCK_REQUEST; | |
| 70 } | |
| 71 | |
| 42 if (navigation_handle()->IsInMainFrame()) { | 72 if (navigation_handle()->IsInMainFrame()) { | 
| 43 // Block top-level navigations to blob: or filesystem: URLs with extension | 73 // Block top-level navigations to blob: or filesystem: URLs with extension | 
| 44 // origin from non-extension processes. See https://crbug.com/645028. | 74 // origin from non-extension processes. See https://crbug.com/645028. | 
| 45 bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob(); | 75 bool current_frame_is_extension_process = | 
| 46 bool is_extension = false; | 76 !!registry->enabled_extensions().GetExtensionOrAppByURL( | 
| 47 if (registry) { | 77 navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); | 
| 48 is_extension = !!registry->enabled_extensions().GetExtensionOrAppByURL( | |
| 49 navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); | |
| 50 } | |
| 51 | 78 | 
| 52 url::Origin origin(url); | 79 if (!url_has_extension_scheme && !current_frame_is_extension_process) { | 
| 53 if (is_nested_url && origin.scheme() == extensions::kExtensionScheme && | |
| 54 !is_extension) { | |
| 55 // Relax this restriction for apps that use <webview>. See | 80 // Relax this restriction for apps that use <webview>. See | 
| 56 // https://crbug.com/652077. | 81 // https://crbug.com/652077. | 
| 57 const extensions::Extension* extension = | |
| 58 registry->enabled_extensions().GetByID(origin.host()); | |
| 59 bool has_webview_permission = | 82 bool has_webview_permission = | 
| 60 extension && | 83 target_extension->permissions_data()->HasAPIPermission( | 
| 61 extension->permissions_data()->HasAPIPermission( | 84 APIPermission::kWebView); | 
| 62 extensions::APIPermission::kWebView); | |
| 63 if (!has_webview_permission) | 85 if (!has_webview_permission) | 
| 64 return content::NavigationThrottle::CANCEL; | 86 return content::NavigationThrottle::CANCEL; | 
| 65 } | 87 } | 
| 66 | 88 | 
| 67 if (content::IsBrowserSideNavigationEnabled() && | 89 guest_view::GuestViewBase* guest = | 
| 68 url.scheme() == extensions::kExtensionScheme) { | 90 guest_view::GuestViewBase::FromWebContents(web_contents); | 
| 69 // This logic is performed for PlzNavigate sub-resources and for | 91 if (content::IsBrowserSideNavigationEnabled() && url_has_extension_scheme && | 
| 70 // non-PlzNavigate in | 92 guest) { | 
| 71 // extensions::url_request_util::AllowCrossRendererResourceLoad. | 93 // This logic is performed for PlzNavigate sub-resources, and for non- | 
| 72 const Extension* extension = | 94 // PlzNavigate in url_request_util::AllowCrossRendererResourceLoad. | 
| 73 registry->enabled_extensions().GetExtensionOrAppByURL(url); | 95 const std::string& owner_extension_id = guest->owner_host(); | 
| 74 guest_view::GuestViewBase* guest = | 96 const Extension* owner_extension = | 
| 75 guest_view::GuestViewBase::FromWebContents(web_contents); | 97 registry->enabled_extensions().GetByID(owner_extension_id); | 
| 76 if (guest) { | |
| 77 std::string owner_extension_id = guest->owner_host(); | |
| 78 const Extension* owner_extension = | |
| 79 registry->enabled_extensions().GetByID(owner_extension_id); | |
| 80 | 98 | 
| 81 std::string partition_domain, partition_id; | 99 std::string partition_domain; | 
| 82 bool in_memory; | 100 std::string partition_id; | 
| 83 std::string resource_path = url.path(); | 101 bool in_memory = false; | 
| 84 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 102 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 
| 85 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 103 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 
| 86 &partition_domain, &partition_id, &in_memory); | 104 &partition_domain, &partition_id, &in_memory); | 
| 87 | 105 | 
| 88 bool allowed = true; | 106 bool allowed = true; | 
| 89 url_request_util::AllowCrossRendererResourceLoadHelper( | 107 url_request_util::AllowCrossRendererResourceLoadHelper( | 
| 90 is_guest, extension, owner_extension, partition_id, resource_path, | 108 is_guest, target_extension, owner_extension, partition_id, url.path(), | 
| 91 navigation_handle()->GetPageTransition(), &allowed); | 109 navigation_handle()->GetPageTransition(), &allowed); | 
| 92 if (!allowed) | 110 if (!allowed) | 
| 93 return content::NavigationThrottle::BLOCK_REQUEST; | 111 return content::NavigationThrottle::BLOCK_REQUEST; | 
| 94 } | |
| 95 } | 112 } | 
| 96 | 113 | 
| 97 return content::NavigationThrottle::PROCEED; | 114 return content::NavigationThrottle::PROCEED; | 
| 98 } | 115 } | 
| 99 | 116 | 
| 100 // Now enforce web_accessible_resources for navigations. Top-level navigations | 117 // This is a subframe navigation to a |target_extension| resource. | 
| 101 // should always be allowed. | 118 // Enforce the web_accessible_resources restriction. | 
| 119 content::RenderFrameHost* parent = web_contents->FindFrameByFrameTreeNodeId( | |
| 120 navigation_handle()->GetParentFrameTreeNodeId()); | |
| 102 | 121 | 
| 103 // If the navigation is not to a chrome-extension:// URL, no need to perform | 122 // Look to see if all ancestors belong to |target_extension|. If not, | 
| 104 // any more checks. | 123 // then the web_accessible_resource restriction applies. | 
| 105 if (!url.SchemeIs(extensions::kExtensionScheme)) | 124 bool external_ancestor = false; | 
| 106 return content::NavigationThrottle::PROCEED; | 125 for (auto* ancestor = parent; ancestor; ancestor = ancestor->GetParent()) { | 
| 126 // Look for a match on the last committed origin. This handles the | |
| 127 // common case, and the about:blank case. | |
| 128 if (ancestor->GetLastCommittedOrigin() == target_origin) | |
| 129 continue; | |
| 130 // Look for an origin match with the last committed URL. This handles the | |
| 131 // case of sandboxed extension resources, which commit with a null origin, | |
| 132 // but are permitted to load non-webaccessible extension resources in | |
| 133 // subframes. | |
| 134 if (url::Origin(ancestor->GetLastCommittedURL()) == target_origin) | |
| 135 continue; | |
| 136 // Ignore DevTools, as it is allowed to embed extension pages. | |
| 137 if (ancestor->GetLastCommittedURL().SchemeIs( | |
| 138 content::kChromeDevToolsScheme)) | |
| 139 continue; | |
| 107 | 140 | 
| 108 // The subframe which is navigated needs to have all of its ancestors be | 141 // Otherwise, we have an external ancestor. | 
| 109 // at the same origin, otherwise the resource needs to be explicitly listed | 142 external_ancestor = true; | 
| 110 // in web_accessible_resources. | 143 break; | 
| 111 // Since the RenderFrameHost is not known until navigation has committed, | |
| 112 // we can't get it from NavigationHandle. However, this code only cares about | |
| 113 // the ancestor chain, so find the current RenderFrameHost and use it to | |
| 114 // traverse up to the main frame. | |
| 115 content::RenderFrameHost* navigating_frame = nullptr; | |
| 116 for (auto* frame : web_contents->GetAllFrames()) { | |
| 117 if (frame->GetFrameTreeNodeId() == | |
| 118 navigation_handle()->GetFrameTreeNodeId()) { | |
| 119 navigating_frame = frame; | |
| 120 break; | |
| 121 } | |
| 122 } | |
| 123 DCHECK(navigating_frame); | |
| 124 | |
| 125 // Traverse the chain of parent frames, checking if they are the same origin | |
| 126 // as the URL of this navigation. | |
| 127 content::RenderFrameHost* ancestor = navigating_frame->GetParent(); | |
| 128 bool external_ancestor = false; | |
| 129 while (ancestor) { | |
| 130 if (ancestor->GetLastCommittedURL().GetOrigin() != url.GetOrigin()) { | |
| 131 // Ignore DevTools, as it is allowed to embed extension pages. | |
| 132 if (!ancestor->GetLastCommittedURL().SchemeIs( | |
| 133 content::kChromeDevToolsScheme)) { | |
| 134 external_ancestor = true; | |
| 135 break; | |
| 136 } | |
| 137 } | |
| 138 ancestor = ancestor->GetParent(); | |
| 139 } | 144 } | 
| 140 | 145 | 
| 141 if (!external_ancestor) | 146 if (external_ancestor) { | 
| 142 return content::NavigationThrottle::PROCEED; | 147 // Cancel navigations to nested URLs, to match the main frame behavior. | 
| 148 if (!url_has_extension_scheme) | |
| 149 return content::NavigationThrottle::CANCEL; | |
| 143 | 150 | 
| 144 // Since there was at least one origin different than the navigation URL, | 151 // |url| must be in the manifest's "web_accessible_resources" section. | 
| 145 // explicitly check for the resource in web_accessible_resources. | 152 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(target_extension, | 
| 146 std::string resource_path = url.path(); | 153 url.path())) | 
| 147 if (!registry) | 154 return content::NavigationThrottle::BLOCK_REQUEST; | 
| 148 return content::NavigationThrottle::BLOCK_REQUEST; | |
| 149 | |
| 150 const extensions::Extension* extension = | |
| 151 registry->enabled_extensions().GetByID(url.host()); | |
| 152 if (!extension) | |
| 153 return content::NavigationThrottle::BLOCK_REQUEST; | |
| 154 | |
| 155 if (WebAccessibleResourcesInfo::IsResourceWebAccessible(extension, | |
| 156 resource_path)) { | |
| 157 return content::NavigationThrottle::PROCEED; | |
| 158 } | 155 } | 
| 159 | 156 | 
| 160 return content::NavigationThrottle::BLOCK_REQUEST; | 157 return content::NavigationThrottle::PROCEED; | 
| 161 } | 158 } | 
| 162 | 159 | 
| 163 } // namespace extensions | 160 } // namespace extensions | 
| OLD | NEW |