Chromium Code Reviews| 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()); | 37 const GURL& url = navigation_handle()->GetURL(); |
| 38 bool url_has_extension_scheme = url.SchemeIs(kExtensionScheme); | |
|
Devlin
2017/04/28 22:13:02
Is it worth a brief comment explaining the signifi
ncarter (slow)
2017/05/01 21:50:37
Done. I eliminated the is_nested_url variable and
| |
| 38 content::WebContents* web_contents = navigation_handle()->GetWebContents(); | 39 content::WebContents* web_contents = navigation_handle()->GetWebContents(); |
| 39 ExtensionRegistry* registry = | 40 ExtensionRegistry* registry = |
| 40 ExtensionRegistry::Get(web_contents->GetBrowserContext()); | 41 ExtensionRegistry::Get(web_contents->GetBrowserContext()); |
| 41 | 42 |
| 43 // Is this navigation targeting an extension resource? | |
| 44 bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob(); | |
| 45 url::Origin target_origin(url); | |
| 46 const Extension* target_extension = nullptr; | |
| 47 if (url_has_extension_scheme) { | |
| 48 target_extension = | |
| 49 registry->enabled_extensions().GetExtensionOrAppByURL(url); | |
| 50 } else if (is_nested_url && target_origin.scheme() == kExtensionScheme) { | |
| 51 target_extension = | |
| 52 registry->enabled_extensions().GetByID(target_origin.host()); | |
| 53 } else { | |
| 54 // If the navigation is not to a chrome-extension resource, no need to | |
| 55 // perform any more checks; it's outside of the purview of this throttle. | |
| 56 return content::NavigationThrottle::PROCEED; | |
| 57 } | |
| 58 | |
| 59 // If the navigation is to an unknown or disabled extension, block it. | |
| 60 if (!target_extension) | |
| 61 return content::NavigationThrottle::BLOCK_REQUEST; | |
| 62 | |
| 42 if (navigation_handle()->IsInMainFrame()) { | 63 if (navigation_handle()->IsInMainFrame()) { |
| 43 // Block top-level navigations to blob: or filesystem: URLs with extension | 64 // Block top-level navigations to blob: or filesystem: URLs with extension |
| 44 // origin from non-extension processes. See https://crbug.com/645028. | 65 // origin from non-extension processes. See https://crbug.com/645028. |
| 45 bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob(); | 66 bool current_frame_is_extension_process = |
| 46 bool is_extension = false; | 67 !!registry->enabled_extensions().GetExtensionOrAppByURL( |
| 47 if (registry) { | 68 navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); |
| 48 is_extension = !!registry->enabled_extensions().GetExtensionOrAppByURL( | |
| 49 navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); | |
| 50 } | |
| 51 | 69 |
| 52 url::Origin origin(url); | 70 if (is_nested_url && !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 | 71 // Relax this restriction for apps that use <webview>. See |
| 56 // https://crbug.com/652077. | 72 // https://crbug.com/652077. |
| 57 const extensions::Extension* extension = | |
| 58 registry->enabled_extensions().GetByID(origin.host()); | |
| 59 bool has_webview_permission = | 73 bool has_webview_permission = |
| 60 extension && | 74 target_extension->permissions_data()->HasAPIPermission( |
| 61 extension->permissions_data()->HasAPIPermission( | 75 APIPermission::kWebView); |
| 62 extensions::APIPermission::kWebView); | |
| 63 if (!has_webview_permission) | 76 if (!has_webview_permission) |
| 64 return content::NavigationThrottle::CANCEL; | 77 return content::NavigationThrottle::CANCEL; |
| 65 } | 78 } |
| 66 | 79 |
| 67 if (content::IsBrowserSideNavigationEnabled() && | 80 guest_view::GuestViewBase* guest = |
| 68 url.scheme() == extensions::kExtensionScheme) { | 81 guest_view::GuestViewBase::FromWebContents(web_contents); |
| 69 // This logic is performed for PlzNavigate sub-resources and for | 82 if (content::IsBrowserSideNavigationEnabled() && url_has_extension_scheme && |
| 70 // non-PlzNavigate in | 83 guest) { |
| 71 // extensions::url_request_util::AllowCrossRendererResourceLoad. | 84 // This logic is performed for PlzNavigate sub-resources and for non- |
| 72 const Extension* extension = | 85 // PlzNavigate in url_request_util::AllowCrossRendererResourceLoad. |
|
Devlin
2017/04/28 22:13:02
"and for non-PlzNavigate" - non-PlzNavigate what?
ncarter (slow)
2017/05/01 21:50:37
Done.
| |
| 73 registry->enabled_extensions().GetExtensionOrAppByURL(url); | 86 std::string owner_extension_id = guest->owner_host(); |
|
Devlin
2017/04/28 22:13:02
Can this be a const std::string&?
(Or even just i
ncarter (slow)
2017/05/01 21:50:37
Done.
| |
| 74 guest_view::GuestViewBase* guest = | 87 const Extension* owner_extension = |
| 75 guest_view::GuestViewBase::FromWebContents(web_contents); | 88 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 | 89 |
| 81 std::string partition_domain, partition_id; | 90 std::string partition_domain, partition_id; |
|
Devlin
2017/04/28 22:13:02
one variable per line
ncarter (slow)
2017/05/01 21:50:37
Done.
| |
| 82 bool in_memory; | 91 bool in_memory; |
|
Devlin
2017/04/28 22:13:02
initialize
ncarter (slow)
2017/05/01 21:50:37
Done.
| |
| 83 std::string resource_path = url.path(); | 92 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( |
| 84 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 93 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), |
| 85 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 94 &partition_domain, &partition_id, &in_memory); |
| 86 &partition_domain, &partition_id, &in_memory); | |
| 87 | 95 |
| 88 bool allowed = true; | 96 bool allowed = true; |
| 89 url_request_util::AllowCrossRendererResourceLoadHelper( | 97 url_request_util::AllowCrossRendererResourceLoadHelper( |
| 90 is_guest, extension, owner_extension, partition_id, resource_path, | 98 is_guest, target_extension, owner_extension, partition_id, url.path(), |
| 91 navigation_handle()->GetPageTransition(), &allowed); | 99 navigation_handle()->GetPageTransition(), &allowed); |
| 92 if (!allowed) | 100 if (!allowed) |
| 93 return content::NavigationThrottle::BLOCK_REQUEST; | 101 return content::NavigationThrottle::BLOCK_REQUEST; |
| 94 } | |
| 95 } | 102 } |
| 96 | 103 |
| 97 return content::NavigationThrottle::PROCEED; | 104 return content::NavigationThrottle::PROCEED; |
| 98 } | 105 } |
| 99 | 106 |
| 100 // Now enforce web_accessible_resources for navigations. Top-level navigations | 107 // This is a subframe navigation to a |target_extension| resource. |
| 101 // should always be allowed. | 108 // Enforce the web_accessible_resources restriction. |
| 109 content::RenderFrameHost* parent = web_contents->FindFrameByFrameTreeNodeId( | |
| 110 navigation_handle()->GetParentFrameTreeNodeId()); | |
| 102 | 111 |
| 103 // If the navigation is not to a chrome-extension:// URL, no need to perform | 112 // Look to see if all ancestors belong to |target_extension|. If not, |
| 104 // any more checks. | 113 // then the web_accessible_resource restriction applies. |
| 105 if (!url.SchemeIs(extensions::kExtensionScheme)) | 114 bool external_ancestor = false; |
| 106 return content::NavigationThrottle::PROCEED; | 115 for (auto* ancestor = parent; ancestor; ancestor = ancestor->GetParent()) { |
| 116 // Look for a match on the last committed origin. This handles the | |
| 117 // common case, and the about:blank case. | |
| 118 if (ancestor->GetLastCommittedOrigin().IsSameOriginWith(target_origin)) | |
| 119 continue; | |
| 120 // Look for an origin match with the last committed URL. This handles the | |
| 121 // case of sandboxed extension resources. | |
|
Devlin
2017/04/28 22:13:02
Can you expand on this a bit to include briefly wh
ncarter (slow)
2017/05/01 21:50:37
Done.
| |
| 122 if (url::Origin(ancestor->GetLastCommittedURL()) == target_origin) | |
| 123 continue; | |
| 124 // Ignore DevTools, as it is allowed to embed extension pages. | |
| 125 if (ancestor->GetLastCommittedURL().SchemeIs( | |
| 126 content::kChromeDevToolsScheme)) | |
| 127 continue; | |
| 107 | 128 |
| 108 // The subframe which is navigated needs to have all of its ancestors be | 129 // Otherwise, we have an external ancestor. |
| 109 // at the same origin, otherwise the resource needs to be explicitly listed | 130 external_ancestor = true; |
| 110 // in web_accessible_resources. | 131 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 } | 132 } |
| 140 | 133 |
| 141 if (!external_ancestor) | 134 if (external_ancestor) { |
| 142 return content::NavigationThrottle::PROCEED; | 135 // Cancel navigations to nested URLs, to match the main frame behavior. |
| 136 if (!url_has_extension_scheme) | |
| 137 return content::NavigationThrottle::CANCEL; | |
| 143 | 138 |
| 144 // Since there was at least one origin different than the navigation URL, | 139 // |url| must be in the manifest's "web_accessible_resources" section. |
| 145 // explicitly check for the resource in web_accessible_resources. | 140 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(target_extension, |
| 146 std::string resource_path = url.path(); | 141 url.path())) |
| 147 if (!registry) | 142 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 } | 143 } |
| 159 | 144 |
| 160 return content::NavigationThrottle::BLOCK_REQUEST; | 145 return content::NavigationThrottle::PROCEED; |
| 161 } | 146 } |
| 162 | 147 |
| 163 } // namespace extensions | 148 } // namespace extensions |
| OLD | NEW |