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

Unified Diff: content/browser/frame_host/data_url_navigation_browsertest.cc

Issue 2702503002: Block renderer-initiated main frame navigations to data URLs (Closed)
Patch Set: Cleanup Created 3 years, 9 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 side-by-side diff with in-line comments
Download patch
Index: content/browser/frame_host/data_url_navigation_browsertest.cc
diff --git a/content/browser/frame_host/data_url_navigation_browsertest.cc b/content/browser/frame_host/data_url_navigation_browsertest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..70cce0d5fabdf5fe2d5ad4a6245b8656707f05ec
--- /dev/null
+++ b/content/browser/frame_host/data_url_navigation_browsertest.cc
@@ -0,0 +1,407 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/strings/pattern.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/webplugininfo.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/download_test_observer.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/shell/browser/shell.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace content {
+
+namespace {
+const char kDataUrlBlockedPattern[] =
+ "Not allowed to top-level navigate to resource:*";
+
+const char kHtmlUrl[] = "data:text/html, <html>test</html>";
+
+// Octet streams are always downloaded.
+const char kOctetStreamUrl[] = "data:application/octet-stream,test";
+
+// Unknown mime types that aren't handled by plugins are always downloaded.
+const char kUnknownMimeTypeUrl[] = "data:unknown/some-mime,test";
+
+// A "Hello World" PDF encoded as a data URL. Source of this PDF:
+// -------------------------
+// %PDF-1.7
+// 1 0 obj << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 2 0 R >>
+// endobj
+// 2 0 obj << /Length 51 >>
+// stream BT
+// /F1 12 Tf
+// 1 0 0 1 100 20 Tm
+// (Hello World)Tj
+// ET
+// endstream
+// endobj
+// 3 0 obj << /Type /Pages /Kids [ 1 0 R ] /Count 1 /MediaBox [ 0 0 300 50] >>
+// endobj
+// 4 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont/Arial >>
+// endobj
+// 5 0 obj << /ProcSet[/PDF/Text] /Font <</F1 4 0 R >> >>
+// endobj
+// 6 0 obj << /Type /Catalog /Pages 3 0 R >>
+// endobj
+// trailer << /Root 6 0 R >>
+// -------------------------
+const char kPdfUrl[] =
+ "data:application/pdf;base64,JVBERi0xLjcKMSAwIG9iaiA8PCAvVHlwZSAvUGFnZSAvUG"
+ "FyZW50IDMgMCBSIC9SZXNvdXJjZXMgNSAwIFIgL0NvbnRlbnRzIDIgMCBSID4+CmVuZG9iagoy"
+ "IDAgb2JqIDw8IC9MZW5ndGggNTEgPj4KIHN0cmVhbSBCVAogL0YxIDEyIFRmCiAxIDAgMCAxID"
+ "EwMCAyMCBUbQogKEhlbGxvIFdvcmxkKVRqCiBFVAogZW5kc3RyZWFtCmVuZG9iagozIDAgb2Jq"
+ "IDw8IC9UeXBlIC9QYWdlcyAvS2lkcyBbIDEgMCBSIF0gL0NvdW50IDEgL01lZGlhQm94IFsgMC"
+ "AwIDMwMCA1MF0gPj4KZW5kb2JqCjQgMCBvYmogPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5"
+ "cGUxIC9OYW1lIC9GMSAvQmFzZUZvbnQvQXJpYWwgPj4KZW5kb2JqCjUgMCBvYmogPDwgL1Byb2"
+ "NTZXRbL1BERi9UZXh0XSAvRm9udCA8PC9GMSA0IDAgUiA+PiA+PgplbmRvYmoKNiAwIG9iaiA8"
+ "PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCnRyYWlsZXIgPDwgL1Jvb3"
+ "QgNiAwIFIgPj4K";
+
+// Helper class for testing data URL navigations with and without browser side
+// navigation enabled.
+class DataUrlNavigationTester {
+ public:
+ DataUrlNavigationTester(Shell* shell) : shell_(shell) {}
+
+ // Tests that a direct navigation to a data URL doesn't show a console warning
+ // and is not blocked.
+ void TestBrowserInitiatedAllowed() {
+ ConsoleObserverDelegate console_delegate(shell_->web_contents(), "FINISH");
+ shell_->web_contents()->SetDelegate(&console_delegate);
+
+ NavigateToURL(
+ shell_,
+ GURL("data:text/html,<html><script>console.log('FINISH');</script>"));
+ console_delegate.Wait();
+ EXPECT_TRUE(shell_->web_contents()->GetURL().SchemeIs(url::kDataScheme));
+ EXPECT_TRUE(shell_->web_contents()
+ ->GetController()
+ .GetLastCommittedEntry()
+ ->GetURL()
+ .SchemeIs(url::kDataScheme));
+ }
+
+ // Tests that a direct navigation to a data URL with mime type PDF isn't
+ // blocked.
+ void TestBrowserInitiatedToPDFAllowed() {
+ TestNavigationObserver observer(shell_->web_contents());
+ NavigateToURL(shell_, GURL(kPdfUrl));
+ EXPECT_EQ(GURL(kPdfUrl), observer.last_navigation_url());
+ EXPECT_TRUE(observer.last_navigation_succeeded());
+ EXPECT_TRUE(shell_->web_contents()->GetURL().SchemeIs(url::kDataScheme));
+ EXPECT_TRUE(shell_->web_contents()
+ ->GetController()
+ .GetLastCommittedEntry()
+ ->GetURL()
+ .SchemeIs(url::kDataScheme));
+ }
+
+ // Test that a window.open on ||original_url| to |data_url| URL is blocked
nasko 2017/03/28 19:58:01 nit: Only one |.
meacer 2017/03/30 20:43:55 Done.
+ // and prints a console message.
+ void TestWindowOpenBlocked(const GURL& original_url, const GURL& data_url) {
+ NavigateToURL(shell_, original_url);
+
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(ExecuteScript(
+ shell_->web_contents(),
+ base::StringPrintf("window.open('%s');", data_url.spec().c_str())));
+ Shell* new_shell = new_shell_observer.GetShell();
+ WaitForLoadStop(new_shell->web_contents());
+ EXPECT_TRUE(new_shell->web_contents()->GetURL().spec().empty());
+ // No navigation should commit.
+ EXPECT_FALSE(
+ new_shell->web_contents()->GetController().GetLastCommittedEntry());
+ }
+
+ // Tests that a redirect from |original_url| to |data_url| is blocked and
+ // prints a console message.
+ void TestRedirectBlocked(const GURL& original_url, const GURL& data_url) {
nasko 2017/03/28 19:58:00 I don't see any redirects in this test method. How
meacer 2017/03/30 20:43:55 As we discussed, my terminology was wrong here. I
+ ASSERT_TRUE(data_url.SchemeIs("data"));
+ NavigateToURL(shell_, original_url);
+ ConsoleObserverDelegate console_delegate(shell_->web_contents(),
+ kDataUrlBlockedPattern);
+ shell_->web_contents()->SetDelegate(&console_delegate);
+ EXPECT_TRUE(ExecuteScript(shell_->web_contents(),
+ base::StringPrintf("window.location.href = '%s';",
+ data_url.spec().c_str())));
+ console_delegate.Wait();
+ // Original page shouldn't navigate away.
+ EXPECT_EQ(original_url, shell_->web_contents()->GetURL());
+ EXPECT_EQ(original_url, shell_->web_contents()
+ ->GetController()
+ .GetLastCommittedEntry()
+ ->GetURL());
+ }
+
+ // Tests that window.open to a data URL is not blocked if the mime type
+ // indicates that the URL will be downloaded.
+ void TestWindowOpenDownloadAllowed(const GURL& original_url,
+ const GURL& data_url) {
+ ASSERT_TRUE(data_url.SchemeIs("data"));
+ NavigateToURL(shell_, original_url);
+
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(ExecuteScript(
+ shell_->web_contents(),
+ base::StringPrintf("window.open('%s');", data_url.spec().c_str())));
+ Shell* new_shell = new_shell_observer.GetShell();
+
+ DownloadManager* download_manager = BrowserContext::GetDownloadManager(
+ new_shell->web_contents()->GetBrowserContext());
+ DownloadTestObserverTerminal download_observer(
+ download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
+
+ WaitForLoadStop(new_shell->web_contents());
+ download_observer.WaitForFinished();
nasko 2017/03/28 19:58:01 Would this observer time out if no download occurr
meacer 2017/03/30 20:43:55 Yes, added a comment.
+
+ EXPECT_TRUE(new_shell->web_contents()->GetURL().spec().empty());
+ // No navigation should commit.
+ EXPECT_FALSE(
+ new_shell->web_contents()->GetController().GetLastCommittedEntry());
+ // Original page shouldn't navigate away.
+ EXPECT_EQ(original_url, shell_->web_contents()->GetURL());
+ EXPECT_EQ(original_url, shell_->web_contents()
+ ->GetController()
+ .GetLastCommittedEntry()
+ ->GetURL());
+ }
+
+ // Tests that a redirect to a data URL is not blocked if the mime type
+ // indicates that the URL will be downloaded.
+ void TestRedirectDownloadAllowed(const GURL& original_url,
+ const GURL& data_url) {
+ ASSERT_TRUE(data_url.SchemeIs("data"));
+ NavigateToURL(shell_, original_url);
+
+ DownloadManager* download_manager = BrowserContext::GetDownloadManager(
+ shell_->web_contents()->GetBrowserContext());
+ DownloadTestObserverTerminal download_observer(
+ download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
+
+ EXPECT_TRUE(ExecuteScript(shell_->web_contents(),
+ base::StringPrintf("window.location.href = '%s';",
+ data_url.spec().c_str())));
+ download_observer.WaitForFinished();
+
+ // Original page shouldn't navigate away.
+ EXPECT_EQ(original_url, shell_->web_contents()->GetURL());
+ EXPECT_EQ(original_url, shell_->web_contents()
+ ->GetController()
+ .GetLastCommittedEntry()
+ ->GetURL());
+ }
+
+ private:
+ Shell* const shell_;
+};
+
+class ScopedPluginRegister {
+ public:
+ ScopedPluginRegister(content::PluginService* plugin_service)
+ : plugin_service_(plugin_service) {
+ const char kPluginName[] = "PDF";
+ const char kPdfMimeType[] = "application/pdf";
+ const char kPdfFileType[] = "pdf";
+ WebPluginInfo plugin_info;
+ plugin_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;
+ plugin_info.name = base::ASCIIToUTF16(kPluginName);
+ plugin_info.mime_types.push_back(
+ WebPluginMimeType(kPdfMimeType, kPdfFileType, std::string()));
+ plugin_service_->RegisterInternalPlugin(plugin_info, false);
+ plugin_service_->RefreshPlugins();
+ }
+
+ ~ScopedPluginRegister() {
+ std::vector<WebPluginInfo> plugins;
+ plugin_service_->GetInternalPlugins(&plugins);
+ EXPECT_EQ(1u, plugins.size());
+ plugin_service_->UnregisterInternalPlugin(plugins[0].path);
+ plugin_service_->RefreshPlugins();
+
+ plugins.clear();
+ plugin_service_->GetInternalPlugins(&plugins);
+ EXPECT_TRUE(plugins.empty());
+ }
+
+ private:
+ content::PluginService* plugin_service_;
+};
+
+} // namespace
+
+class DataUrlNavigationBrowserTest : public ContentBrowserTest {
+ public:
+ DataUrlNavigationBrowserTest()
+ : scoped_plugin_register_(PluginService::GetInstance()) {}
+
+ protected:
+ void SetUpOnMainThread() override {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
+ private:
+ ScopedPluginRegister scoped_plugin_register_;
+};
+
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, BrowserInitiated) {
+ DataUrlNavigationTester(shell()).TestBrowserInitiatedAllowed();
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, RendererInitiated) {
+ DataUrlNavigationTester(shell()).TestRedirectBlocked(
+ embedded_test_server()->GetURL("/simple_links.html"), GURL(kHtmlUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, WindowOpen_Block) {
+ DataUrlNavigationTester(shell()).TestWindowOpenBlocked(
+ embedded_test_server()->GetURL("/simple_page.html"), GURL(kHtmlUrl));
+}
+
+// Test that window.open to a data URL is not blocked if the mime type indicates
+// that the URL will be downloaded.
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
+ WindowOpen_OctetStream_Download) {
+ DataUrlNavigationTester(shell()).TestWindowOpenDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kOctetStreamUrl));
+}
+
+// Test that a content initiated navigation to a data URL is not blocked if the
+// mime type indicates that the URL will be downloaded.
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
+ Redirect_OctetStream_Download) {
+ DataUrlNavigationTester(shell()).TestRedirectDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kOctetStreamUrl));
+}
+
+// Test that window.open to a data URL results in an allowed download if the URL
+// has an unknown mime type.
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
+ WindowOpen_UnknownMimeType_Download) {
+ DataUrlNavigationTester(shell()).TestWindowOpenDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kUnknownMimeTypeUrl));
+}
+
+// Test that redirect to a data URL results in an allowed download if the URL
+// has an unknown mime type.
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
+ Redirect_UnknownMimeType_Download) {
+ DataUrlNavigationTester(shell()).TestRedirectDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kUnknownMimeTypeUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest,
+ DirectNavigation_PDF_Allow) {
+ DataUrlNavigationTester(shell()).TestBrowserInitiatedToPDFAllowed();
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, WindowOpen_PDF_Block) {
+ DataUrlNavigationTester(shell()).TestWindowOpenBlocked(
+ embedded_test_server()->GetURL("/simple_page.html"), GURL(kPdfUrl));
+}
+
+// Test that a content initiated navigation to a data URL should be blocked if
+// the data URL has a mime type that will be handled by a plugin (PDF in this
+// case).
+IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, Redirect_PDF_Block) {
+ DataUrlNavigationTester(shell()).TestRedirectBlocked(
+ embedded_test_server()->GetURL("/simple_page.html"), GURL(kPdfUrl));
+}
+
+// Same as DataUrlNavigationBrowserTest tests, except these tests have browser
+// side navigation enabled.
+class DataUrlBrowserSideNavigationBrowserTest : public ContentBrowserTest {
+ public:
+ DataUrlBrowserSideNavigationBrowserTest()
+ : scoped_plugin_register_(PluginService::GetInstance()) {}
+
+ protected:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kEnableBrowserSideNavigation);
+ }
+
+ void SetUpOnMainThread() override {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
+ private:
+ ScopedPluginRegister scoped_plugin_register_;
+};
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ BrowserInitiated) {
+ DataUrlNavigationTester(shell()).TestBrowserInitiatedAllowed();
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ RendererInitiated) {
+ DataUrlNavigationTester(shell()).TestRedirectBlocked(
+ embedded_test_server()->GetURL("/simple_links.html"), GURL(kHtmlUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ WindowOpen_Block) {
+ DataUrlNavigationTester(shell()).TestWindowOpenBlocked(
+ embedded_test_server()->GetURL("/simple_page.html"), GURL(kHtmlUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ WindowOpen_OctetStream_Download) {
+ DataUrlNavigationTester(shell()).TestWindowOpenDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kOctetStreamUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ Redirect_OctetStream_Download) {
+ DataUrlNavigationTester(shell()).TestRedirectDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kOctetStreamUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ WindowOpen_UnknownMimeType_Download) {
+ DataUrlNavigationTester(shell()).TestWindowOpenDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kUnknownMimeTypeUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ Redirect_UnknownMimeType_Download) {
+ DataUrlNavigationTester(shell()).TestRedirectDownloadAllowed(
+ embedded_test_server()->GetURL("/simple_page.html"),
+ GURL(kUnknownMimeTypeUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ DirectNavigation_PDF_Allow) {
+ DataUrlNavigationTester(shell()).TestBrowserInitiatedToPDFAllowed();
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ WindowOpen_PDF_Block) {
+ DataUrlNavigationTester(shell()).TestWindowOpenBlocked(
+ embedded_test_server()->GetURL("/simple_page.html"), GURL(kPdfUrl));
+}
+
+IN_PROC_BROWSER_TEST_F(DataUrlBrowserSideNavigationBrowserTest,
+ Redirect_PDF_Block) {
+ DataUrlNavigationTester(shell()).TestRedirectBlocked(
+ embedded_test_server()->GetURL("/simple_page.html"), GURL(kPdfUrl));
+}
+
+} // content

Powered by Google App Engine
This is Rietveld 408576698