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
Note that this PROCEED case is new vs. the previou
Devlin
2017/05/01 22:04:28
Hmm... is there no way we could surface a useful e
ncarter (slow)
2017/05/02 20:14:15
Note that the PROCEED logic just matches the origi
ncarter (slow)
2017/05/02 23:53:15
My prev comment was incorrect (since the PROCEED i
| |
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 variant of this logic applies to PlzNavigate top-level |
72 const Extension* extension = | 94 // navigations. It is performed for subresources, and for non-PlzNavigate |
73 registry->enabled_extensions().GetExtensionOrAppByURL(url); | 95 // top navigations, in url_request_util::AllowCrossRendererResourceLoad. |
74 guest_view::GuestViewBase* guest = | 96 const std::string& owner_extension_id = guest->owner_host(); |
75 guest_view::GuestViewBase::FromWebContents(web_contents); | 97 const Extension* owner_extension = |
76 if (guest) { | 98 registry->enabled_extensions().GetByID(owner_extension_id); |
77 std::string owner_extension_id = guest->owner_host(); | |
78 const Extension* owner_extension = | |
79 registry->enabled_extensions().GetByID(owner_extension_id); | |
80 | 99 |
81 std::string partition_domain, partition_id; | 100 std::string partition_domain; |
82 bool in_memory; | 101 std::string partition_id; |
83 std::string resource_path = url.path(); | 102 bool in_memory = false; |
84 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 103 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( |
85 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 104 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), |
86 &partition_domain, &partition_id, &in_memory); | 105 &partition_domain, &partition_id, &in_memory); |
87 | 106 |
88 bool allowed = true; | 107 bool allowed = true; |
89 url_request_util::AllowCrossRendererResourceLoadHelper( | 108 url_request_util::AllowCrossRendererResourceLoadHelper( |
90 is_guest, extension, owner_extension, partition_id, resource_path, | 109 is_guest, target_extension, owner_extension, partition_id, url.path(), |
91 navigation_handle()->GetPageTransition(), &allowed); | 110 navigation_handle()->GetPageTransition(), &allowed); |
92 if (!allowed) | 111 if (!allowed) |
93 return content::NavigationThrottle::BLOCK_REQUEST; | 112 return content::NavigationThrottle::BLOCK_REQUEST; |
94 } | |
95 } | 113 } |
96 | 114 |
97 return content::NavigationThrottle::PROCEED; | 115 return content::NavigationThrottle::PROCEED; |
98 } | 116 } |
99 | 117 |
100 // Now enforce web_accessible_resources for navigations. Top-level navigations | 118 // This is a subframe navigation to a |target_extension| resource. |
101 // should always be allowed. | 119 // Enforce the web_accessible_resources restriction. |
120 content::RenderFrameHost* parent = web_contents->FindFrameByFrameTreeNodeId( | |
121 navigation_handle()->GetParentFrameTreeNodeId()); | |
102 | 122 |
103 // If the navigation is not to a chrome-extension:// URL, no need to perform | 123 // Look to see if all ancestors belong to |target_extension|. If not, |
104 // any more checks. | 124 // then the web_accessible_resource restriction applies. |
105 if (!url.SchemeIs(extensions::kExtensionScheme)) | 125 bool external_ancestor = false; |
106 return content::NavigationThrottle::PROCEED; | 126 for (auto* ancestor = parent; ancestor; ancestor = ancestor->GetParent()) { |
127 // Look for a match on the last committed origin. This handles the | |
128 // common case, and the about:blank case. | |
129 if (ancestor->GetLastCommittedOrigin() == target_origin) | |
130 continue; | |
131 // Look for an origin match with the last committed URL. This handles the | |
132 // case of sandboxed extension resources, which commit with a null origin, | |
133 // but are permitted to load non-webaccessible extension resources in | |
134 // subframes. | |
135 if (url::Origin(ancestor->GetLastCommittedURL()) == target_origin) | |
136 continue; | |
137 // Ignore DevTools, as it is allowed to embed extension pages. | |
138 if (ancestor->GetLastCommittedURL().SchemeIs( | |
139 content::kChromeDevToolsScheme)) | |
140 continue; | |
107 | 141 |
108 // The subframe which is navigated needs to have all of its ancestors be | 142 // Otherwise, we have an external ancestor. |
109 // at the same origin, otherwise the resource needs to be explicitly listed | 143 external_ancestor = true; |
110 // in web_accessible_resources. | 144 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 } | 145 } |
140 | 146 |
141 if (!external_ancestor) | 147 if (external_ancestor) { |
142 return content::NavigationThrottle::PROCEED; | 148 // Cancel navigations to nested URLs, to match the main frame behavior. |
Devlin
2017/05/01 22:04:28
Is this difference (CANCEL vs BLOCK_REQUEST) visib
ncarter (slow)
2017/05/01 22:42:53
Nested URLs (blob and filesystem) are classified a
Devlin
2017/05/02 01:52:09
Okay, I think that makes sense, as long as there's
| |
149 if (!url_has_extension_scheme) | |
150 return content::NavigationThrottle::CANCEL; | |
143 | 151 |
144 // Since there was at least one origin different than the navigation URL, | 152 // |url| must be in the manifest's "web_accessible_resources" section. |
145 // explicitly check for the resource in web_accessible_resources. | 153 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(target_extension, |
146 std::string resource_path = url.path(); | 154 url.path())) |
147 if (!registry) | 155 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 } | 156 } |
159 | 157 |
160 return content::NavigationThrottle::BLOCK_REQUEST; | 158 return content::NavigationThrottle::PROCEED; |
161 } | 159 } |
162 | 160 |
163 } // namespace extensions | 161 } // namespace extensions |
OLD | NEW |