Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(271)

Side by Side Diff: chrome/browser/extensions/process_manager_browsertest.cc

Issue 2366973002: Block top-level navigations to nested URLs with extension origins from non-extension processes. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/browser/net/chrome_extensions_network_delegate.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 <stddef.h> 5 #include <stddef.h>
6 6
7 #include <memory> 7 #include <memory>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/macros.h" 11 #include "base/macros.h"
12 #include "base/run_loop.h" 12 #include "base/run_loop.h"
13 #include "chrome/browser/extensions/browser_action_test_util.h" 13 #include "chrome/browser/extensions/browser_action_test_util.h"
14 #include "chrome/browser/extensions/extension_browsertest.h" 14 #include "chrome/browser/extensions/extension_browsertest.h"
15 #include "chrome/browser/extensions/extension_service.h" 15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/test_extension_dir.h" 16 #include "chrome/browser/extensions/test_extension_dir.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h" 17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/common/extensions/extension_process_policy.h" 18 #include "chrome/common/extensions/extension_process_policy.h"
19 #include "chrome/common/pref_names.h"
19 #include "chrome/test/base/in_process_browser_test.h" 20 #include "chrome/test/base/in_process_browser_test.h"
20 #include "chrome/test/base/ui_test_utils.h" 21 #include "chrome/test/base/ui_test_utils.h"
21 #include "content/public/browser/notification_service.h" 22 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/render_frame_host.h" 23 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/render_process_host.h" 24 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/web_contents.h" 25 #include "content/public/browser/web_contents.h"
25 #include "content/public/test/browser_test_utils.h" 26 #include "content/public/test/browser_test_utils.h"
27 #include "content/public/test/test_navigation_observer.h"
26 #include "content/public/test/test_utils.h" 28 #include "content/public/test/test_utils.h"
27 #include "extensions/browser/process_manager.h" 29 #include "extensions/browser/process_manager.h"
28 #include "extensions/common/value_builder.h" 30 #include "extensions/common/value_builder.h"
29 #include "extensions/test/background_page_watcher.h" 31 #include "extensions/test/background_page_watcher.h"
30 #include "net/dns/mock_host_resolver.h" 32 #include "net/dns/mock_host_resolver.h"
31 #include "net/test/embedded_test_server/embedded_test_server.h" 33 #include "net/test/embedded_test_server/embedded_test_server.h"
32 34
33 namespace extensions { 35 namespace extensions {
34 36
35 namespace { 37 namespace {
36 38
37 void AddFrameToSet(std::set<content::RenderFrameHost*>* frames, 39 void AddFrameToSet(std::set<content::RenderFrameHost*>* frames,
38 content::RenderFrameHost* rfh) { 40 content::RenderFrameHost* rfh) {
39 if (rfh->IsRenderFrameLive()) 41 if (rfh->IsRenderFrameLive())
40 frames->insert(rfh); 42 frames->insert(rfh);
41 } 43 }
42 44
45 GURL CreateBlobURL(content::RenderFrameHost* frame,
46 const std::string& content) {
47 std::string blob_url_string;
48 EXPECT_TRUE(ExecuteScriptAndExtractString(
49 frame,
50 "var blob = new Blob(['<html><body>" + content + "</body></html>'],\n"
51 " {type: 'text/html'});\n"
52 "domAutomationController.send(URL.createObjectURL(blob));\n",
53 &blob_url_string));
54 GURL blob_url(blob_url_string);
55 EXPECT_TRUE(blob_url.is_valid());
56 EXPECT_TRUE(blob_url.SchemeIsBlob());
57 return blob_url;
58 }
59
60 GURL CreateFileSystemURL(content::RenderFrameHost* frame,
61 const std::string& content) {
62 std::string filesystem_url_string;
63 EXPECT_TRUE(ExecuteScriptAndExtractString(
64 frame,
65 "var blob = new Blob(['<html><body>" + content + "</body></html>'],\n"
66 " {type: 'text/html'});\n"
67 "window.webkitRequestFileSystem(TEMPORARY, blob.size, fs => {\n"
68 " fs.root.getFile('foo.html', {create: true}, file => {\n"
69 " file.createWriter(writer => {\n"
70 " writer.write(blob);\n"
71 " writer.onwriteend = () => {\n"
72 " domAutomationController.send(file.toURL());\n"
73 " }\n"
74 " });\n"
75 " });\n"
76 "});\n",
77 &filesystem_url_string));
78 GURL filesystem_url(filesystem_url_string);
79 EXPECT_TRUE(filesystem_url.is_valid());
80 EXPECT_TRUE(filesystem_url.SchemeIsFileSystem());
81 return filesystem_url;
82 }
83
84 std::string GetTextContent(content::RenderFrameHost* frame) {
85 std::string result;
86 EXPECT_TRUE(ExecuteScriptAndExtractString(
87 frame, "domAutomationController.send(document.body.innerText)", &result));
88 return result;
89 }
90
43 } // namespace 91 } // namespace
44 92
45 // Takes a snapshot of all frames upon construction. When Wait() is called, a 93 // Takes a snapshot of all frames upon construction. When Wait() is called, a
46 // MessageLoop is created and Quit when all previously recorded frames are 94 // MessageLoop is created and Quit when all previously recorded frames are
47 // either present in the tab, or deleted. If a navigation happens between the 95 // either present in the tab, or deleted. If a navigation happens between the
48 // construction and the Wait() call, then this logic ensures that all obsolete 96 // construction and the Wait() call, then this logic ensures that all obsolete
49 // RenderFrameHosts have been destructed when Wait() returns. 97 // RenderFrameHosts have been destructed when Wait() returns.
50 // See also the comment at ProcessManagerBrowserTest::NavigateToURL. 98 // See also the comment at ProcessManagerBrowserTest::NavigateToURL.
51 class NavigationCompletedObserver : public content::WebContentsObserver { 99 class NavigationCompletedObserver : public content::WebContentsObserver {
52 public: 100 public:
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 observer.Wait(); 205 observer.Wait();
158 } 206 }
159 207
160 size_t IfExtensionsIsolated(size_t if_enabled, size_t if_disabled) { 208 size_t IfExtensionsIsolated(size_t if_enabled, size_t if_disabled) {
161 return content::AreAllSitesIsolatedForTesting() || 209 return content::AreAllSitesIsolatedForTesting() ||
162 IsIsolateExtensionsEnabled() 210 IsIsolateExtensionsEnabled()
163 ? if_enabled 211 ? if_enabled
164 : if_disabled; 212 : if_disabled;
165 } 213 }
166 214
215 content::WebContents* OpenPopup(content::RenderFrameHost* opener,
216 const GURL& url) {
217 content::WindowedNotificationObserver popup_observer(
218 chrome::NOTIFICATION_TAB_ADDED,
219 content::NotificationService::AllSources());
220 EXPECT_TRUE(ExecuteScript(opener, "window.open('" + url.spec() + "')"));
221 popup_observer.Wait();
222 content::WebContents* popup =
223 browser()->tab_strip_model()->GetActiveWebContents();
224 WaitForLoadStop(popup);
225 EXPECT_EQ(url, popup->GetMainFrame()->GetLastCommittedURL());
226 return popup;
227 }
228
167 private: 229 private:
168 std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_; 230 std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_;
169 }; 231 };
170 232
171 // Test that basic extension loading creates the appropriate ExtensionHosts 233 // Test that basic extension loading creates the appropriate ExtensionHosts
172 // and background pages. 234 // and background pages.
173 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, 235 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
174 ExtensionHostCreation) { 236 ExtensionHostCreation) {
175 ProcessManager* pm = ProcessManager::Get(profile()); 237 ProcessManager* pm = ProcessManager::Get(profile());
176 238
(...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 ExecuteScriptInBackgroundPageNoWait( 632 ExecuteScriptInBackgroundPageNoWait(
571 extension->id(), 633 extension->id(),
572 "document.cookie = 'extension_cookie';" 634 "document.cookie = 'extension_cookie';"
573 "window.domAutomationController.send(document.cookie);"); 635 "window.domAutomationController.send(document.cookie);");
574 std::string message; 636 std::string message;
575 ASSERT_TRUE(queue.WaitForMessage(&message)); 637 ASSERT_TRUE(queue.WaitForMessage(&message));
576 EXPECT_EQ(message, "\"extension_cookie\""); 638 EXPECT_EQ(message, "\"extension_cookie\"");
577 } 639 }
578 } 640 }
579 641
642 // Test that navigations to blob: and filesystem: URLs with extension origins
643 // are disallowed when initiated from non-extension processes. See
644 // https://crbug.com/645028 and https://crbug.com/644426.
645 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
646 NestedURLNavigationsToExtensionBlocked) {
647 // Disabling web security is necessary to test the browser enforcement;
648 // without it, the loads in this test would be blocked by
649 // SecurityOrigin::canDisplay() as invalid local resource loads.
650 PrefService* prefs = browser()->profile()->GetPrefs();
651 prefs->SetBoolean(prefs::kWebKitWebSecurityEnabled, false);
652
653 // Create a simple extension without a background page.
654 const Extension* extension = CreateExtension("Extension", false);
655 embedded_test_server()->ServeFilesFromDirectory(extension->path());
656 ASSERT_TRUE(embedded_test_server()->Start());
657
658 // Navigate main tab to a web page with two web iframes. There should be no
659 // extension frames yet.
660 NavigateToURL(embedded_test_server()->GetURL("/two_iframes.html"));
661 ProcessManager* pm = ProcessManager::Get(profile());
662 EXPECT_EQ(0u, pm->GetAllFrames().size());
663 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
664
665 content::WebContents* tab =
666 browser()->tab_strip_model()->GetActiveWebContents();
667
668 // Navigate first subframe to an extension URL. With --isolate-extensions,
669 // this will go into a new extension process.
670 const GURL extension_url(extension->url().Resolve("empty.html"));
671 EXPECT_TRUE(content::NavigateIframeToURL(tab, "frame1", extension_url));
672 EXPECT_EQ(IfExtensionsIsolated(1, 0),
673 pm->GetRenderFrameHostsForExtension(extension->id()).size());
674 EXPECT_EQ(IfExtensionsIsolated(1, 0), pm->GetAllFrames().size());
675
676 content::RenderFrameHost* main_frame = tab->GetMainFrame();
677 content::RenderFrameHost* extension_frame = ChildFrameAt(main_frame, 0);
678
679 // Open a new about:blank popup from main frame. This should stay in the web
680 // process.
681 content::WebContents* popup =
682 OpenPopup(main_frame, GURL(url::kAboutBlankURL));
683 EXPECT_NE(popup, tab);
684 ASSERT_EQ(2, browser()->tab_strip_model()->count());
685 EXPECT_EQ(IfExtensionsIsolated(1, 0),
686 pm->GetRenderFrameHostsForExtension(extension->id()).size());
687 EXPECT_EQ(IfExtensionsIsolated(1, 0), pm->GetAllFrames().size());
688
689 // Create valid blob and filesystem URLs in the extension's origin.
690 url::Origin extension_origin(extension_frame->GetLastCommittedOrigin());
691 GURL blob_url(CreateBlobURL(extension_frame, "foo"));
692 EXPECT_EQ(extension_origin, url::Origin(blob_url));
693 GURL filesystem_url(CreateFileSystemURL(extension_frame, "foo"));
694 EXPECT_EQ(extension_origin, url::Origin(filesystem_url));
695
696 // Navigate the popup to each nested URL with extension origin.
697 GURL nested_urls[] = {blob_url, filesystem_url};
698 for (size_t i = 0; i < arraysize(nested_urls); i++) {
699 content::TestNavigationObserver observer(popup);
700 EXPECT_TRUE(ExecuteScript(
701 popup, "location.href = '" + nested_urls[i].spec() + "';"));
702 observer.Wait();
703
704 // This is a top-level navigation that should be blocked since it
705 // originates from a non-extension process. Ensure that the error page
706 // doesn't commit an extension URL or origin.
707 EXPECT_NE(nested_urls[i], popup->GetLastCommittedURL());
708 EXPECT_FALSE(extension_origin.IsSameOriginWith(
709 popup->GetMainFrame()->GetLastCommittedOrigin()));
710 EXPECT_NE("foo", GetTextContent(popup->GetMainFrame()));
711
712 EXPECT_EQ(IfExtensionsIsolated(1, 0),
713 pm->GetRenderFrameHostsForExtension(extension->id()).size());
714 EXPECT_EQ(IfExtensionsIsolated(1, 0), pm->GetAllFrames().size());
715 }
716
717 // Navigate second subframe to each nested URL from the main frame (i.e.,
718 // from non-extension process).
719 //
720 // TODO(alexmos): Currently, this is still allowed due to unblessed extension
721 // contexts, but in the future such subframe navigations from non-extension
722 // processes should be blocked when unblessed contexts go away with
723 // --isolate-extensions.
724 for (size_t i = 0; i < arraysize(nested_urls); i++) {
725 EXPECT_TRUE(content::NavigateIframeToURL(tab, "frame2", nested_urls[i]));
726 content::RenderFrameHost* second_frame = ChildFrameAt(main_frame, 1);
727 EXPECT_EQ(nested_urls[i], second_frame->GetLastCommittedURL());
728 EXPECT_EQ(extension_origin, second_frame->GetLastCommittedOrigin());
729 EXPECT_EQ("foo", GetTextContent(second_frame));
730 EXPECT_EQ(IfExtensionsIsolated(2, 0),
731 pm->GetRenderFrameHostsForExtension(extension->id()).size());
732 EXPECT_EQ(IfExtensionsIsolated(2, 0), pm->GetAllFrames().size());
733 }
734 }
735
736 // Test that navigations to blob: and filesystem: URLs with extension origins
737 // are allowed when initiated from extension processes. See
738 // https://crbug.com/645028 and https://crbug.com/644426.
739 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
740 NestedURLNavigationsToExtensionAllowed) {
741 // Create a simple extension without a background page.
742 const Extension* extension = CreateExtension("Extension", false);
743 embedded_test_server()->ServeFilesFromDirectory(extension->path());
744 ASSERT_TRUE(embedded_test_server()->Start());
745
746 // Navigate main tab to an extension URL with a blank subframe.
747 const GURL extension_url(extension->url().Resolve("blank_iframe.html"));
748 NavigateToURL(extension_url);
749 ProcessManager* pm = ProcessManager::Get(profile());
750 EXPECT_EQ(2u, pm->GetAllFrames().size());
751 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
752
753 content::WebContents* tab =
754 browser()->tab_strip_model()->GetActiveWebContents();
755 content::RenderFrameHost* main_frame = tab->GetMainFrame();
756
757 // Create blob and filesystem URLs in the extension's origin.
758 url::Origin extension_origin(main_frame->GetLastCommittedOrigin());
759 GURL blob_url(CreateBlobURL(main_frame, "foo"));
760 EXPECT_EQ(extension_origin, url::Origin(blob_url));
761 GURL filesystem_url(CreateFileSystemURL(main_frame, "foo"));
762 EXPECT_EQ(extension_origin, url::Origin(filesystem_url));
763
764 // From the main frame, navigate its subframe to each nested URL. This
765 // should be allowed and should stay in the extension process.
766 GURL nested_urls[] = {blob_url, filesystem_url};
767 for (size_t i = 0; i < arraysize(nested_urls); i++) {
768 EXPECT_TRUE(content::NavigateIframeToURL(tab, "frame0", nested_urls[i]));
769 content::RenderFrameHost* child = ChildFrameAt(main_frame, 0);
770 EXPECT_EQ(nested_urls[i], child->GetLastCommittedURL());
771 EXPECT_EQ(extension_origin, child->GetLastCommittedOrigin());
772 EXPECT_EQ("foo", GetTextContent(child));
773 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
774 EXPECT_EQ(2u, pm->GetAllFrames().size());
775 }
776
777 // From the main frame, create a blank popup and navigate it to each nested
778 // URL. This should also be allowed, since the navigation originated from an
779 // extension process.
780 for (size_t i = 0; i < arraysize(nested_urls); i++) {
781 content::WebContents* popup =
782 OpenPopup(main_frame, GURL(url::kAboutBlankURL));
783 EXPECT_NE(popup, tab);
784
785 content::TestNavigationObserver observer(popup);
786 EXPECT_TRUE(ExecuteScript(
787 popup, "location.href = '" + nested_urls[i].spec() + "';"));
788 observer.Wait();
789
790 EXPECT_EQ(nested_urls[i], popup->GetLastCommittedURL());
791 EXPECT_EQ(extension_origin,
792 popup->GetMainFrame()->GetLastCommittedOrigin());
793 EXPECT_EQ("foo", GetTextContent(popup->GetMainFrame()));
794
795 EXPECT_EQ(3 + i,
796 pm->GetRenderFrameHostsForExtension(extension->id()).size());
797 EXPECT_EQ(3 + i, pm->GetAllFrames().size());
798 }
799 }
800
580 } // namespace extensions 801 } // namespace extensions
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/net/chrome_extensions_network_delegate.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698