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 // TODO(nick): This yields an unsatisfying error page; use a different error |
| 64 // code once that's supported. https://crbug.com/649869 |
| 65 return content::NavigationThrottle::BLOCK_REQUEST; |
| 66 } |
| 67 |
42 if (navigation_handle()->IsInMainFrame()) { | 68 if (navigation_handle()->IsInMainFrame()) { |
43 // Block top-level navigations to blob: or filesystem: URLs with extension | 69 // Block top-level navigations to blob: or filesystem: URLs with extension |
44 // origin from non-extension processes. See https://crbug.com/645028. | 70 // origin from non-extension processes. See https://crbug.com/645028. |
45 bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob(); | 71 bool current_frame_is_extension_process = |
46 bool is_extension = false; | 72 !!registry->enabled_extensions().GetExtensionOrAppByURL( |
47 if (registry) { | 73 navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); |
48 is_extension = !!registry->enabled_extensions().GetExtensionOrAppByURL( | |
49 navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); | |
50 } | |
51 | 74 |
52 url::Origin origin(url); | 75 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 | 76 // Relax this restriction for apps that use <webview>. See |
56 // https://crbug.com/652077. | 77 // https://crbug.com/652077. |
57 const extensions::Extension* extension = | |
58 registry->enabled_extensions().GetByID(origin.host()); | |
59 bool has_webview_permission = | 78 bool has_webview_permission = |
60 extension && | 79 target_extension->permissions_data()->HasAPIPermission( |
61 extension->permissions_data()->HasAPIPermission( | 80 APIPermission::kWebView); |
62 extensions::APIPermission::kWebView); | |
63 if (!has_webview_permission) | 81 if (!has_webview_permission) |
64 return content::NavigationThrottle::CANCEL; | 82 return content::NavigationThrottle::CANCEL; |
65 } | 83 } |
66 | 84 |
67 if (content::IsBrowserSideNavigationEnabled() && | 85 guest_view::GuestViewBase* guest = |
68 url.scheme() == extensions::kExtensionScheme) { | 86 guest_view::GuestViewBase::FromWebContents(web_contents); |
69 // This logic is performed for PlzNavigate sub-resources and for | 87 if (content::IsBrowserSideNavigationEnabled() && url_has_extension_scheme && |
70 // non-PlzNavigate in | 88 guest) { |
71 // extensions::url_request_util::AllowCrossRendererResourceLoad. | 89 // This variant of this logic applies to PlzNavigate top-level |
72 const Extension* extension = | 90 // navigations. It is performed for subresources, and for non-PlzNavigate |
73 registry->enabled_extensions().GetExtensionOrAppByURL(url); | 91 // top navigations, in url_request_util::AllowCrossRendererResourceLoad. |
74 guest_view::GuestViewBase* guest = | 92 const std::string& owner_extension_id = guest->owner_host(); |
75 guest_view::GuestViewBase::FromWebContents(web_contents); | 93 const Extension* owner_extension = |
76 if (guest) { | 94 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 | 95 |
81 std::string partition_domain, partition_id; | 96 std::string partition_domain; |
82 bool in_memory; | 97 std::string partition_id; |
83 std::string resource_path = url.path(); | 98 bool in_memory = false; |
84 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 99 bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( |
85 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 100 navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), |
86 &partition_domain, &partition_id, &in_memory); | 101 &partition_domain, &partition_id, &in_memory); |
87 | 102 |
88 bool allowed = true; | 103 bool allowed = true; |
89 url_request_util::AllowCrossRendererResourceLoadHelper( | 104 url_request_util::AllowCrossRendererResourceLoadHelper( |
90 is_guest, extension, owner_extension, partition_id, resource_path, | 105 is_guest, target_extension, owner_extension, partition_id, url.path(), |
91 navigation_handle()->GetPageTransition(), &allowed); | 106 navigation_handle()->GetPageTransition(), &allowed); |
92 if (!allowed) | 107 if (!allowed) |
93 return content::NavigationThrottle::BLOCK_REQUEST; | 108 return content::NavigationThrottle::BLOCK_REQUEST; |
94 } | |
95 } | 109 } |
96 | 110 |
97 return content::NavigationThrottle::PROCEED; | 111 return content::NavigationThrottle::PROCEED; |
98 } | 112 } |
99 | 113 |
100 // Now enforce web_accessible_resources for navigations. Top-level navigations | 114 // This is a subframe navigation to a |target_extension| resource. |
101 // should always be allowed. | 115 // Enforce the web_accessible_resources restriction. |
| 116 content::RenderFrameHost* parent = navigation_handle()->GetParentFrame(); |
102 | 117 |
103 // If the navigation is not to a chrome-extension:// URL, no need to perform | 118 // Look to see if all ancestors belong to |target_extension|. If not, |
104 // any more checks. | 119 // then the web_accessible_resource restriction applies. |
105 if (!url.SchemeIs(extensions::kExtensionScheme)) | 120 bool external_ancestor = false; |
106 return content::NavigationThrottle::PROCEED; | 121 for (auto* ancestor = parent; ancestor; ancestor = ancestor->GetParent()) { |
| 122 // Look for a match on the last committed origin. This handles the |
| 123 // common case, and the about:blank case. |
| 124 if (ancestor->GetLastCommittedOrigin() == target_origin) |
| 125 continue; |
| 126 // Look for an origin match with the last committed URL. This handles the |
| 127 // case of sandboxed extension resources, which commit with a null origin, |
| 128 // but are permitted to load non-webaccessible extension resources in |
| 129 // subframes. |
| 130 if (url::Origin(ancestor->GetLastCommittedURL()) == target_origin) |
| 131 continue; |
| 132 // Ignore DevTools, as it is allowed to embed extension pages. |
| 133 if (ancestor->GetLastCommittedURL().SchemeIs( |
| 134 content::kChromeDevToolsScheme)) |
| 135 continue; |
107 | 136 |
108 // The subframe which is navigated needs to have all of its ancestors be | 137 // Otherwise, we have an external ancestor. |
109 // at the same origin, otherwise the resource needs to be explicitly listed | 138 external_ancestor = true; |
110 // in web_accessible_resources. | 139 break; |
111 content::RenderFrameHost* ancestor = navigation_handle()->GetParentFrame(); | |
112 bool external_ancestor = false; | |
113 while (ancestor) { | |
114 if (ancestor->GetLastCommittedURL().GetOrigin() != url.GetOrigin()) { | |
115 // Ignore DevTools, as it is allowed to embed extension pages. | |
116 if (!ancestor->GetLastCommittedURL().SchemeIs( | |
117 content::kChromeDevToolsScheme)) { | |
118 external_ancestor = true; | |
119 break; | |
120 } | |
121 } | |
122 ancestor = ancestor->GetParent(); | |
123 } | 140 } |
124 | 141 |
125 if (!external_ancestor) | 142 if (external_ancestor) { |
126 return content::NavigationThrottle::PROCEED; | 143 // Cancel navigations to nested URLs, to match the main frame behavior. |
| 144 if (!url_has_extension_scheme) |
| 145 return content::NavigationThrottle::CANCEL; |
127 | 146 |
128 // Since there was at least one origin different than the navigation URL, | 147 // |url| must be in the manifest's "web_accessible_resources" section. |
129 // explicitly check for the resource in web_accessible_resources. | 148 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(target_extension, |
130 std::string resource_path = url.path(); | 149 url.path())) |
131 if (!registry) | 150 return content::NavigationThrottle::BLOCK_REQUEST; |
132 return content::NavigationThrottle::BLOCK_REQUEST; | |
133 | |
134 const extensions::Extension* extension = | |
135 registry->enabled_extensions().GetByID(url.host()); | |
136 if (!extension) | |
137 return content::NavigationThrottle::BLOCK_REQUEST; | |
138 | |
139 if (WebAccessibleResourcesInfo::IsResourceWebAccessible(extension, | |
140 resource_path)) { | |
141 return content::NavigationThrottle::PROCEED; | |
142 } | 151 } |
143 | 152 |
144 return content::NavigationThrottle::BLOCK_REQUEST; | 153 return content::NavigationThrottle::PROCEED; |
145 } | 154 } |
146 | 155 |
147 const char* ExtensionNavigationThrottle::GetNameForLogging() { | 156 const char* ExtensionNavigationThrottle::GetNameForLogging() { |
148 return "ExtensionNavigationThrottle"; | 157 return "ExtensionNavigationThrottle"; |
149 } | 158 } |
150 | 159 |
151 } // namespace extensions | 160 } // namespace extensions |
OLD | NEW |