summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-10-13 11:50:13 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2016-10-13 12:01:23 +0000
commit45d4f70ca6ad97e4b860fb97de44e7329cf02633 (patch)
tree3dd32935d81d1458ba42a4b621e09dace19e9cce
parent7a27704b9b66b68d74f5fb42af921325bc758f8f (diff)
[Backport] Merges six security fixes to M54, related to blobs.
Merge patch created pair programming style with creis@ and nick@. Several manual fixups were required to get the tests passing on M54. BUG=644966,646278,652784 TEST=Manual testing included: - Verifying exploit steps w/ chrome w/ --isolate-extensions - content_browsertests and content_unittests - The following browser_tests subsets, both w/ and w/o --isolate-extensions: *ProcessManager* *Grants* *Exploit* *TouchFocuses* NOPRESUBMIT=true NOTRY=true TBR=nick@chromium.org The following six fixes are included in this diff: 1. https://codereview.chromium.org/2322673005: > Fix process transfers for blob urls of sites requiring dedicated processes > > RenderFrameHostManager::IsRendererTransferNeededForNavigation had a bug > where it passed an effective url, instead of an effective SITE url, to > a function that was expecting the latter. > > Add a test that exercises this case. Add a CHECK to content shell browser > client to verify that we're actually getting site urls all the time. > > Committed: https://crrev.com/db193a1b105de523fd0bb089c9769a71ed287d9e > Cr-Commit-Position: refs/heads/master@{#417752} 2. https://codereview.chromium.org/2331063002: > Fix IsolateIcelandFrameTreeBrowserTest.ProcessSwitchForIsolatedBlob so > that it's not flaky under --site-per-process. > > Committed: https://crrev.com/07fd7e19e0095aeb30bd2c99109d083bb67732cb > Cr-Commit-Position: refs/heads/master@{#417987} 3. https://codereview.chromium.org/2365433002: > (re-land) Disallow navigations to blob URLs with non-canonical origins. > > Re-landing this with a fix for xhr-to-blob-in-isolated-world.html > > Review-Url: https://codereview.chromium.org/2365433002 > Cr-Commit-Position: refs/heads/master@{#420436} 4. https://codereview.chromium.org/2332263002 [partial merge, just for the helper function it added, used by later CLs] > Updated suborigin serialization to latest spec proposal > > This modifiest the serialization format of suborigins so they are now > represented in the form https-so://suboriginname.host.name (or, > alternatively, with the scheme http-so). This change removes collisions > with potentially valid URLs that were being deserialized as suborigins. > > Additionally, this adds suborigins back as an experimental web platform > feature rather than a testing feature. > > Review-Url: https://codereview.chromium.org/2332263002 > Cr-Commit-Position: refs/heads/master@{#420828} > CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_site_isolation 5. https://codereview.chromium.org/2364633004: > Update ChildProcessSecurityPolicy so that the chrome-extension:// scheme > is considered "web safe" to be requestable from any process, but only > "web safe" to commit in extension processes. > > In ChildProcessSecurityPolicy::CanRequestURL and CanCommitURL, when > seeing blob and filesystem urls, make a security decision based > on the inner origin rather than the scheme. > > When the extensions ProcessManager (via ExtensionWebContentsObserver) > notices a RenderFrame being created in an extension SiteInstance, > grant that process permission to commit chrome-extension:// URLs. > > In BlobDispatcherHost, only allow creation of blob URLs from processes > that would be able to commit them. > > Add a security exploit browsertest that verifies the above mechanisms > working together. > > Committed: https://crrev.com/a411fd062bc68fc2b5fc3aca7e4cbb8e4a3e074e > Committed: https://crrev.com/2a8ba8c4c186e5ea0a2ed938cc5d41441af64228 > Cr-Original-Commit-Position: refs/heads/master@{#421964} > Cr-Commit-Position: refs/heads/master@{#422474} 6. https://codereview.chromium.org/2396533003: > Allow <webview> to access URLs in the origin of the app embedding it. > > With r422474 creation of blob: URLs with origin of a chrome-extension:// > was locked down. However, the case of a <webview> loading an > accessible_resource from its embedder and creating a blob: is disallowed. > This CL adds permission for <webview> to create such URLs in the origin > of its embedder. > > This CL is based on work by nick@chromium.org. > > Committed: https://crrev.com/5edda59b0b1cb8fff058b47567ac32e58be5168a > Cr-Commit-Position: refs/heads/master@{#422976} CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_site_isolation Change-Id: I89461b125fff72e19a05a5e676eba520e2a7938b Review-Url: https://codereview.chromium.org/2399853003 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
-rw-r--r--chromium/content/browser/bad_message.h1
-rw-r--r--chromium/content/browser/blob_storage/blob_url_browsertest.cc188
-rw-r--r--chromium/content/browser/child_process_security_policy_impl.cc131
-rw-r--r--chromium/content/browser/child_process_security_policy_impl.h26
-rw-r--r--chromium/content/browser/child_process_security_policy_unittest.cc116
-rw-r--r--chromium/content/browser/frame_host/frame_tree_browsertest.cc76
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.cc4
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_impl.cc2
-rw-r--r--chromium/content/browser/site_instance_impl.cc9
-rw-r--r--chromium/content/browser/site_instance_impl.h12
-rw-r--r--chromium/content/content_tests.gypi1
-rw-r--r--chromium/content/public/browser/child_process_security_policy.h39
-rw-r--r--chromium/content/public/browser/content_browser_client.cc2
-rw-r--r--chromium/content/public/browser/content_browser_client.h12
14 files changed, 567 insertions, 52 deletions
diff --git a/chromium/content/browser/bad_message.h b/chromium/content/browser/bad_message.h
index 1a8c80abb4e..f6568280466 100644
--- a/chromium/content/browser/bad_message.h
+++ b/chromium/content/browser/bad_message.h
@@ -127,6 +127,7 @@ enum BadMessageReason {
DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103,
DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE = 104,
BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN = 105,
+ BDH_DISALLOWED_ORIGIN = 139,
// Please add new elements here. The naming convention is abbreviated class
// name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/chromium/content/browser/blob_storage/blob_url_browsertest.cc b/chromium/content/browser/blob_storage/blob_url_browsertest.cc
new file mode 100644
index 00000000000..f25ee5b258f
--- /dev/null
+++ b/chromium/content/browser/blob_storage/blob_url_browsertest.cc
@@ -0,0 +1,188 @@
+// Copyright 2016 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/macros.h"
+#include "base/strings/pattern.h"
+#include "build/build_config.h"
+#include "content/browser/web_contents/web_contents_impl.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/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/content_browser_test_utils_internal.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace content {
+
+// Tests of the blob: URL scheme.
+class BlobUrlBrowserTest : public ContentBrowserTest {
+ public:
+ BlobUrlBrowserTest() {}
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(embedded_test_server()->Start());
+ SetupCrossSiteRedirector(embedded_test_server());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BlobUrlBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, LinkToUniqueOriginBlob) {
+ // Use a data URL to obtain a test page in a unique origin. The page
+ // contains a link to a "blob:null/SOME-GUID-STRING" URL.
+ NavigateToURL(
+ shell(),
+ GURL("data:text/html,<body><script>"
+ "var link = document.body.appendChild(document.createElement('a'));"
+ "link.innerText = 'Click Me!';"
+ "link.href = URL.createObjectURL(new Blob(['potato']));"
+ "link.target = '_blank';"
+ "link.id = 'click_me';"
+ "</script></body>"));
+
+ // Click the link.
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(
+ ExecuteScript(shell(), "document.getElementById('click_me').click()"));
+
+ // The link should create a new tab.
+ Shell* new_shell = new_shell_observer.GetShell();
+ WebContents* new_contents = new_shell->web_contents();
+ WaitForLoadStop(new_contents);
+
+ EXPECT_TRUE(
+ base::MatchPattern(new_contents->GetVisibleURL().spec(), "blob:null/*"));
+ std::string page_content;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ new_contents,
+ "domAutomationController.send("
+ " document.origin + ' ' + document.body.innerText);",
+ &page_content));
+ EXPECT_EQ("null potato", page_content);
+}
+
+IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, LinkToSameOriginBlob) {
+ // Using an http page, click a link that opens a popup to a same-origin blob.
+ GURL url = embedded_test_server()->GetURL("chromium.org", "/title1.html");
+ url::Origin origin(url);
+ NavigateToURL(shell(), url);
+
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(ExecuteScript(
+ shell(),
+ "var link = document.body.appendChild(document.createElement('a'));"
+ "link.innerText = 'Click Me!';"
+ "link.href = URL.createObjectURL(new Blob(['potato']));"
+ "link.target = '_blank';"
+ "link.click()"));
+
+ // The link should create a new tab.
+ Shell* new_shell = new_shell_observer.GetShell();
+ WebContents* new_contents = new_shell->web_contents();
+ WaitForLoadStop(new_contents);
+
+ EXPECT_TRUE(base::MatchPattern(new_contents->GetVisibleURL().spec(),
+ "blob:" + origin.Serialize() + "/*"));
+ std::string page_content;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ new_contents,
+ "domAutomationController.send("
+ " document.origin + ' ' + document.body.innerText);",
+ &page_content));
+ EXPECT_EQ(origin.Serialize() + " potato", page_content);
+}
+
+// Regression test for https://crbug.com/646278
+IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, LinkToSameOriginBlobWithAuthority) {
+ // Using an http page, click a link that opens a popup to a same-origin blob
+ // that has a spoofy authority section applied. This should be blocked.
+ GURL url = embedded_test_server()->GetURL("chromium.org", "/title1.html");
+ url::Origin origin(url);
+ NavigateToURL(shell(), url);
+
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(ExecuteScript(
+ shell(),
+ "var link = document.body.appendChild(document.createElement('a'));"
+ "link.innerText = 'Click Me!';"
+ "link.href = 'blob:http://spoof.com@' + "
+ " URL.createObjectURL(new Blob(['potato'])).split('://')[1];"
+ "link.target = '_blank';"
+ "link.click()"));
+
+ // The link should create a new tab.
+ Shell* new_shell = new_shell_observer.GetShell();
+ WebContents* new_contents = new_shell->web_contents();
+ WaitForLoadStop(new_contents);
+
+ // The spoofy URL should not be shown to the user.
+ EXPECT_FALSE(
+ base::MatchPattern(new_contents->GetVisibleURL().spec(), "*spoof*"));
+ // The currently implemented behavior is that the URL gets rewritten to
+ // about:blank.
+ EXPECT_EQ("about:blank", new_contents->GetVisibleURL().spec());
+ std::string page_content;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ new_contents,
+ "domAutomationController.send("
+ " document.origin + ' ' + document.body.innerText);",
+ &page_content));
+ EXPECT_EQ(origin.Serialize() + " ", page_content); // no potato
+}
+
+// Regression test for https://crbug.com/646278
+IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, ReplaceStateToAddAuthorityToBlob) {
+ // history.replaceState from a validly loaded blob URL shouldn't allow adding
+ // an authority to the inner URL, which would be spoofy.
+ GURL url = embedded_test_server()->GetURL("chromium.org", "/title1.html");
+ url::Origin origin(url);
+ NavigateToURL(shell(), url);
+
+ ShellAddedObserver new_shell_observer;
+ EXPECT_TRUE(ExecuteScript(
+ shell(),
+ "var spoof_fn = function () {\n"
+ " host_port = document.origin.split('://')[1];\n"
+ " spoof_url = 'blob:http://spoof.com@' + host_port + '/abcd';\n"
+ " window.history.replaceState({}, '', spoof_url);\n"
+ "};\n"
+ "args = ['<body>potato<scr', 'ipt>(', spoof_fn, ')();</scri', 'pt>'];\n"
+ "b = new Blob(args, {type: 'text/html'});"
+ "window.open(URL.createObjectURL(b));"));
+
+ Shell* new_shell = new_shell_observer.GetShell();
+ WebContents* new_contents = new_shell->web_contents();
+ WaitForLoadStop(new_contents);
+
+ // The spoofy URL should not be shown to the user.
+ EXPECT_FALSE(
+ base::MatchPattern(new_contents->GetVisibleURL().spec(), "*spoof*"));
+
+ // The currently implemented behavior is that the URL gets rewritten to
+ // about:blank. The content of the page stays the same.
+ EXPECT_EQ("about:blank", new_contents->GetVisibleURL().spec());
+ std::string page_content;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ new_contents,
+ "domAutomationController.send("
+ " document.origin + ' ' + document.body.innerText);",
+ &page_content));
+ EXPECT_EQ(origin.Serialize() + " potato", page_content);
+
+ // TODO(nick): Currently, window.location still reflects the spoof URL.
+ // This seems unfortunate -- can we fix it?
+ std::string window_location;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ new_contents, "domAutomationController.send(window.location.href);",
+ &window_location));
+ EXPECT_TRUE(base::MatchPattern(window_location, "*spoof*"));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/child_process_security_policy_impl.cc b/chromium/content/browser/child_process_security_policy_impl.cc
index 639401fc731..9a0c26d5bd4 100644
--- a/chromium/content/browser/child_process_security_policy_impl.cc
+++ b/chromium/content/browser/child_process_security_policy_impl.cc
@@ -67,6 +67,31 @@ enum ChildProcessSecurityGrants {
DELETE_FILE_GRANT = DELETE_FILE_PERMISSION,
};
+// https://crbug.com/646278 Valid blob URLs should contain canonically
+// serialized origins.
+bool IsMalformedBlobUrl(const GURL& url) {
+ if (!url.SchemeIsBlob())
+ return false;
+
+ // If the part after blob: survives a roundtrip through url::Origin, then
+ // it's a normal blob URL.
+ std::string canonical_origin = url::Origin(url).Serialize();
+ canonical_origin.append(1, '/');
+ if (base::StartsWith(url.GetContent(), canonical_origin,
+ base::CompareCase::INSENSITIVE_ASCII))
+ return false;
+
+ // blob:blobinternal:// is used by blink for stream URLs. This doesn't survive
+ // url::Origin canonicalization -- blobinternal is a fake scheme -- but allow
+ // it anyway. TODO(nick): Added speculatively, might be unnecessary.
+ if (base::StartsWith(url.GetContent(), "blobinternal://",
+ base::CompareCase::INSENSITIVE_ASCII))
+ return false;
+
+ // This is a malformed blob URL.
+ return true;
+}
+
} // namespace
// The SecurityState class is used to maintain per-child process security state
@@ -177,6 +202,8 @@ class ChildProcessSecurityPolicyImpl::SecurityState {
// Determine whether permission has been granted to commit |url|.
bool CanCommitURL(const GURL& url) {
+ DCHECK(!url.SchemeIsBlob() && !url.SchemeIsFileSystem())
+ << "inner_url extraction should be done already.";
// Having permission to a scheme implies permission to all of its URLs.
SchemeMap::const_iterator scheme_judgment(
scheme_policy_.find(url.scheme()));
@@ -301,6 +328,10 @@ ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() {
RegisterWebSafeScheme(url::kFtpScheme);
RegisterWebSafeScheme(url::kDataScheme);
RegisterWebSafeScheme("feed");
+
+ // TODO(nick): https://crbug.com/651534 blob: and filesystem: schemes embed
+ // other origins, so we should not treat them as web safe. Remove callers of
+ // IsWebSafeScheme(), and then eliminate the next two lines.
RegisterWebSafeScheme(url::kBlobScheme);
RegisterWebSafeScheme(url::kFileSystemScheme);
@@ -311,11 +342,6 @@ ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() {
}
ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() {
- web_safe_schemes_.clear();
- pseudo_schemes_.clear();
- STLDeleteContainerPairSecondPointers(security_state_.begin(),
- security_state_.end());
- security_state_.clear();
}
// static
@@ -353,25 +379,43 @@ void ChildProcessSecurityPolicyImpl::Remove(int child_id) {
void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme(
const std::string& scheme) {
base::AutoLock lock(lock_);
- DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) << "Add schemes at most once.";
+ DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme))
+ << "Add schemes at most once.";
DCHECK_EQ(0U, pseudo_schemes_.count(scheme))
<< "Web-safe implies not pseudo.";
- web_safe_schemes_.insert(scheme);
+ schemes_okay_to_request_in_any_process_.insert(scheme);
+ schemes_okay_to_commit_in_any_process_.insert(scheme);
+}
+
+void ChildProcessSecurityPolicyImpl::RegisterWebSafeIsolatedScheme(
+ const std::string& scheme,
+ bool always_allow_in_origin_headers) {
+ base::AutoLock lock(lock_);
+ DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme))
+ << "Add schemes at most once.";
+ DCHECK_EQ(0U, pseudo_schemes_.count(scheme))
+ << "Web-safe implies not pseudo.";
+
+ schemes_okay_to_request_in_any_process_.insert(scheme);
+ if (always_allow_in_origin_headers)
+ schemes_okay_to_appear_as_origin_headers_.insert(scheme);
}
bool ChildProcessSecurityPolicyImpl::IsWebSafeScheme(
const std::string& scheme) {
base::AutoLock lock(lock_);
- return ContainsKey(web_safe_schemes_, scheme);
+ return ContainsKey(schemes_okay_to_request_in_any_process_, scheme);
}
void ChildProcessSecurityPolicyImpl::RegisterPseudoScheme(
const std::string& scheme) {
base::AutoLock lock(lock_);
DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Add schemes at most once.";
- DCHECK_EQ(0U, web_safe_schemes_.count(scheme))
+ DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme))
+ << "Pseudo implies not web-safe.";
+ DCHECK_EQ(0U, schemes_okay_to_commit_in_any_process_.count(scheme))
<< "Pseudo implies not web-safe.";
pseudo_schemes_.insert(scheme);
@@ -407,6 +451,10 @@ void ChildProcessSecurityPolicyImpl::GrantRequestURL(
return; // Can't grant the capability to request pseudo schemes.
}
+ if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) {
+ return; // Don't grant blanket access to blob: or filesystem: schemes.
+ }
+
{
base::AutoLock lock(lock_);
SecurityStateMap::iterator state = security_state_.find(child_id);
@@ -608,6 +656,20 @@ bool ChildProcessSecurityPolicyImpl::CanRequestURL(
return false;
}
+ // Blob and filesystem URLs require special treatment, since they embed an
+ // inner origin.
+ if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) {
+ if (IsMalformedBlobUrl(url))
+ return false;
+
+ url::Origin origin(url);
+ return origin.unique() || IsWebSafeScheme(origin.scheme()) ||
+ CanCommitURL(child_id, GURL(origin.Serialize()));
+ }
+
+ if (IsWebSafeScheme(url.scheme()))
+ return true;
+
// If the process can commit the URL, it can request it.
if (CanCommitURL(child_id, url))
return true;
@@ -626,16 +688,33 @@ bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id,
if (IsPseudoScheme(url.scheme()))
return base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL);
- // TODO(creis): Tighten this for Site Isolation, so that a URL from a site
- // that is isolated can only be committed in a process dedicated to that site.
- // CanRequestURL should still allow all web-safe schemes. See
- // https://crbug.com/515309.
- if (IsWebSafeScheme(url.scheme()))
- return true; // The scheme has been white-listed for every child process.
+ // Blob and filesystem URLs require special treatment; validate the inner
+ // origin they embed.
+ if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) {
+ if (IsMalformedBlobUrl(url))
+ return false;
+
+ url::Origin origin(url);
+ return origin.unique() || CanCommitURL(child_id, GURL(origin.Serialize()));
+ }
{
base::AutoLock lock(lock_);
+ // Most schemes can commit in any process. Note that we check
+ // schemes_okay_to_commit_in_any_process_ here, which is stricter than
+ // IsWebSafeScheme().
+ //
+ // TODO(creis, nick): https://crbug.com/515309: in generalized Site
+ // Isolation and/or --site-per-process, there will be no such thing as a
+ // scheme that is okay to commit in any process. Instead, an URL from a site
+ // that is isolated may only be committed in a process dedicated to that
+ // site, so CanCommitURL will need to rely on explicit, per-process grants.
+ // Note how today, even with extension isolation, the line below does not
+ // enforce that http pages cannot commit in an extension process.
+ if (ContainsKey(schemes_okay_to_commit_in_any_process_, url.scheme()))
+ return true;
+
SecurityStateMap::iterator state = security_state_.find(child_id);
if (state == security_state_.end())
return false;
@@ -646,6 +725,28 @@ bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id,
}
}
+bool ChildProcessSecurityPolicyImpl::CanSetAsOriginHeader(int child_id,
+ const GURL& url) {
+ if (!url.is_valid())
+ return false; // Can't set invalid URLs as origin headers.
+
+ // If this process can commit |url|, it can use |url| as an origin for
+ // outbound requests.
+ if (CanCommitURL(child_id, url))
+ return true;
+
+ // Allow schemes which may come from scripts executing in isolated worlds;
+ // XHRs issued by such scripts reflect the script origin rather than the
+ // document origin.
+ {
+ base::AutoLock lock(lock_);
+ if (ContainsKey(schemes_okay_to_appear_as_origin_headers_,
+ url.scheme()))
+ return true;
+ }
+ return false;
+}
+
bool ChildProcessSecurityPolicyImpl::CanReadFile(int child_id,
const base::FilePath& file) {
return HasPermissionsForFile(child_id, file, READ_FILE_GRANT);
diff --git a/chromium/content/browser/child_process_security_policy_impl.h b/chromium/content/browser/child_process_security_policy_impl.h
index 46d2d7a6e96..b0aebc259cd 100644
--- a/chromium/content/browser/child_process_security_policy_impl.h
+++ b/chromium/content/browser/child_process_security_policy_impl.h
@@ -42,6 +42,9 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
// ChildProcessSecurityPolicy implementation.
void RegisterWebSafeScheme(const std::string& scheme) override;
+ void RegisterWebSafeIsolatedScheme(
+ const std::string& scheme,
+ bool always_allow_in_origin_headers) override;
bool IsWebSafeScheme(const std::string& scheme) override;
void GrantReadFile(int child_id, const base::FilePath& file) override;
void GrantCreateReadWriteFile(int child_id,
@@ -63,6 +66,8 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
const std::string& filesystem_id) override;
void GrantOrigin(int child_id, const url::Origin& origin) override;
void GrantScheme(int child_id, const std::string& scheme) override;
+ bool CanRequestURL(int child_id, const GURL& url) override;
+ bool CanCommitURL(int child_id, const GURL& url) override;
bool CanReadFile(int child_id, const base::FilePath& file) override;
bool CanCreateReadWriteFile(int child_id,
const base::FilePath& file) override;
@@ -123,16 +128,9 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
// Revoke read raw cookies permission.
void RevokeReadRawCookies(int child_id);
- // Before servicing a child process's request for a URL, the browser should
- // call this method to determine whether the process has the capability to
- // request the URL.
- bool CanRequestURL(int child_id, const GURL& url);
-
- // Whether the process is allowed to commit a document from the given URL.
- // This is more restrictive than CanRequestURL, since CanRequestURL allows
- // requests that might lead to cross-process navigations or external protocol
- // handlers.
- bool CanCommitURL(int child_id, const GURL& url);
+ // Whether the given origin is valid for an origin header. Valid origin
+ // headers are commitable URLs.
+ bool CanSetAsOriginHeader(int child_id, const GURL& url);
// Explicit permissions checks for FileSystemURL specified files.
bool CanReadFileSystemFile(int child_id, const storage::FileSystemURL& url);
@@ -227,9 +225,11 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl
// class. You must not block while holding this lock.
base::Lock lock_;
- // These schemes are white-listed for all child processes. This set is
- // protected by |lock_|.
- SchemeSet web_safe_schemes_;
+ // These schemes are white-listed for all child processes in various contexts.
+ // These sets are protected by |lock_|.
+ SchemeSet schemes_okay_to_commit_in_any_process_;
+ SchemeSet schemes_okay_to_request_in_any_process_;
+ SchemeSet schemes_okay_to_appear_as_origin_headers_;
// These schemes do not actually represent retrievable URLs. For example,
// the the URLs in the "about" scheme are aliases to other URLs. This set is
diff --git a/chromium/content/browser/child_process_security_policy_unittest.cc b/chromium/content/browser/child_process_security_policy_unittest.cc
index 23604683ad3..2ff4ed1dfbb 100644
--- a/chromium/content/browser/child_process_security_policy_unittest.cc
+++ b/chromium/content/browser/child_process_security_policy_unittest.cc
@@ -174,8 +174,17 @@ TEST_F(ChildProcessSecurityPolicyTest, StandardSchemesTest) {
GURL("view-source:http://www.google.com/")));
EXPECT_FALSE(p->CanCommitURL(kRendererID,
GURL("view-source:http://www.google.com/")));
+ EXPECT_TRUE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("http://www.google.com/")));
+ EXPECT_TRUE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("https://www.paypal.com/")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("ftp://ftp.gnu.org/")));
+ EXPECT_TRUE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("data:text/html,<b>Hi</b>")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(
+ kRendererID, GURL("filesystem:http://localhost/temporary/a.gif")));
- // Dangerous to request or commit.
+ // Dangerous to request, commit, or set as origin header.
EXPECT_FALSE(p->CanRequestURL(kRendererID,
GURL("file:///etc/passwd")));
EXPECT_FALSE(p->CanRequestURL(kRendererID,
@@ -184,6 +193,63 @@ TEST_F(ChildProcessSecurityPolicyTest, StandardSchemesTest) {
GURL("file:///etc/passwd")));
EXPECT_FALSE(p->CanCommitURL(kRendererID,
GURL("chrome://foo/bar")));
+ EXPECT_FALSE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("chrome://foo/bar")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(
+ kRendererID, GURL("view-source:http://www.google.com/")));
+
+ p->Remove(kRendererID);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, BlobSchemeTest) {
+ ChildProcessSecurityPolicyImpl* p =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+
+ p->Add(kRendererID);
+
+ EXPECT_TRUE(
+ p->CanRequestURL(kRendererID, GURL("blob:http://localhost/some-guid")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("blob:null/some-guid")));
+ EXPECT_TRUE(
+ p->CanRequestURL(kRendererID, GURL("blob:http://localhost/some-guid")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("blob:NulL/some-guid")));
+ EXPECT_TRUE(
+ p->CanRequestURL(kRendererID, GURL("blob:NulL/some-guid#fragment")));
+ EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("blob:NulL/some-guid?query")));
+ EXPECT_TRUE(
+ p->CanRequestURL(kRendererID, GURL("blob:blobinternal://some-guid")));
+ EXPECT_FALSE(p->CanRequestURL(
+ kRendererID, GURL("blob:http://username@localhost/some-guid")));
+ EXPECT_FALSE(p->CanRequestURL(
+ kRendererID, GURL("blob:http://username @localhost/some-guid")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("blob:blob:some-guid")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("blob:some-guid")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID,
+ GURL("blob:filesystem:http://localhost/path")));
+ EXPECT_FALSE(p->CanRequestURL(kRendererID,
+ GURL("filesystem:blob:http://localhost/guid")));
+
+ EXPECT_TRUE(
+ p->CanCommitURL(kRendererID, GURL("blob:http://localhost/some-guid")));
+ EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("blob:null/some-guid")));
+ EXPECT_TRUE(
+ p->CanCommitURL(kRendererID, GURL("blob:http://localhost/some-guid")));
+ EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("blob:NulL/some-guid")));
+ EXPECT_TRUE(
+ p->CanCommitURL(kRendererID, GURL("blob:NulL/some-guid#fragment")));
+ EXPECT_TRUE(
+ p->CanCommitURL(kRendererID, GURL("blob:blobinternal://some-guid")));
+ EXPECT_FALSE(p->CanCommitURL(
+ kRendererID, GURL("blob:http://username@localhost/some-guid")));
+ EXPECT_FALSE(p->CanCommitURL(
+ kRendererID, GURL("blob:http://username @localhost/some-guid")));
+ EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("blob:blob:some-guid")));
+ EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("blob:some-guid")));
+ EXPECT_FALSE(p->CanCommitURL(kRendererID,
+ GURL("blob:filesystem:http://localhost/path")));
+ EXPECT_FALSE(p->CanCommitURL(kRendererID,
+ GURL("filesystem:blob:http://localhost/guid")));
p->Remove(kRendererID);
}
@@ -202,6 +268,10 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) {
EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("about:BlAnK")));
EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("aBouT:BlAnK")));
EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("aBouT:blank")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("about:blank")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("about:BlAnK")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("aBouT:BlAnK")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("aBouT:blank")));
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:memory")));
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash")));
@@ -211,6 +281,9 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) {
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:crash")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:cache")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:hang")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:crash")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:cache")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:hang")));
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("aBoUt:memory")));
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:CrASh")));
@@ -218,17 +291,21 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) {
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("aBoUt:memory")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:CrASh")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("abOuT:cAChe")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:CrASh")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("abOuT:cAChe")));
// Requests for about: pages should be denied.
p->GrantRequestURL(kRendererID, GURL("about:crash"));
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:crash")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:crash")));
// These requests for chrome:// pages should be granted.
GURL chrome_url("chrome://foo");
p->GrantRequestURL(kRendererID, chrome_url);
EXPECT_TRUE(p->CanRequestURL(kRendererID, chrome_url));
EXPECT_TRUE(p->CanCommitURL(kRendererID, chrome_url));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, chrome_url));
p->Remove(kRendererID);
}
@@ -241,9 +318,13 @@ TEST_F(ChildProcessSecurityPolicyTest, JavaScriptTest) {
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("javascript:alert('xss')")));
+ EXPECT_FALSE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("javascript:alert('xss')")));
p->GrantRequestURL(kRendererID, GURL("javascript:alert('xss')"));
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("javascript:alert('xss')")));
+ EXPECT_FALSE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("javascript:alert('xss')")));
p->Remove(kRendererID);
}
@@ -258,16 +339,19 @@ TEST_F(ChildProcessSecurityPolicyTest, RegisterWebSafeSchemeTest) {
// requested but not committed.
EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers")));
// Once we register "asdf", we default to deny.
RegisterTestScheme("asdf");
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers")));
// We can allow new schemes by adding them to the whitelist.
p->RegisterWebSafeScheme("asdf");
EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers")));
// Cleanup.
p->Remove(kRendererID);
@@ -281,15 +365,20 @@ TEST_F(ChildProcessSecurityPolicyTest, CanServiceCommandsTest) {
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd")));
p->GrantRequestURL(kRendererID, GURL("file:///etc/passwd"));
EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd")));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd")));
// We should forget our state if we repeat a renderer id.
p->Remove(kRendererID);
p->Add(kRendererID);
EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd")));
p->Remove(kRendererID);
}
@@ -319,6 +408,16 @@ TEST_F(ChildProcessSecurityPolicyTest, ViewSource) {
kRendererID, GURL("view-source:view-source:http://www.google.com/")));
+ // View source URLs should not be setable as origin headers
+ EXPECT_FALSE(p->CanSetAsOriginHeader(
+ kRendererID, GURL("view-source:http://www.google.com/")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID,
+ GURL("view-source:file:///etc/passwd")));
+ EXPECT_FALSE(
+ p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(
+ kRendererID, GURL("view-source:view-source:http://www.google.com/")));
+
p->GrantRequestURL(kRendererID, GURL("view-source:file:///etc/passwd"));
// View source needs to be able to request the embedded scheme.
EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
@@ -343,18 +442,24 @@ TEST_F(ChildProcessSecurityPolicyTest, SpecificFile) {
EXPECT_FALSE(p->CanRequestURL(kRendererID, sensitive_url));
EXPECT_FALSE(p->CanCommitURL(kRendererID, icon_url));
EXPECT_FALSE(p->CanCommitURL(kRendererID, sensitive_url));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, icon_url));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, sensitive_url));
p->GrantRequestSpecificFileURL(kRendererID, icon_url);
EXPECT_TRUE(p->CanRequestURL(kRendererID, icon_url));
EXPECT_FALSE(p->CanRequestURL(kRendererID, sensitive_url));
EXPECT_TRUE(p->CanCommitURL(kRendererID, icon_url));
EXPECT_FALSE(p->CanCommitURL(kRendererID, sensitive_url));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, icon_url));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, sensitive_url));
p->GrantRequestURL(kRendererID, icon_url);
EXPECT_TRUE(p->CanRequestURL(kRendererID, icon_url));
EXPECT_TRUE(p->CanRequestURL(kRendererID, sensitive_url));
EXPECT_TRUE(p->CanCommitURL(kRendererID, icon_url));
EXPECT_TRUE(p->CanCommitURL(kRendererID, sensitive_url));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, icon_url));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, sensitive_url));
p->Remove(kRendererID);
}
@@ -715,6 +820,9 @@ TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) {
EXPECT_FALSE(p->CanCommitURL(kRendererID, url_foo1));
EXPECT_FALSE(p->CanCommitURL(kRendererID, url_foo2));
EXPECT_FALSE(p->CanCommitURL(kRendererID, url_bar));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_foo1));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_foo2));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_bar));
p->GrantOrigin(kRendererID, url::Origin(url_foo1));
@@ -724,6 +832,9 @@ TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) {
EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo1));
EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo2));
EXPECT_FALSE(p->CanCommitURL(kRendererID, url_bar));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo1));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo2));
+ EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_bar));
p->GrantScheme(kRendererID, kChromeUIScheme);
@@ -733,6 +844,9 @@ TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) {
EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo1));
EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo2));
EXPECT_TRUE(p->CanCommitURL(kRendererID, url_bar));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo1));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo2));
+ EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_bar));
p->Remove(kRendererID);
}
diff --git a/chromium/content/browser/frame_host/frame_tree_browsertest.cc b/chromium/content/browser/frame_host/frame_tree_browsertest.cc
index 1ffdd0a21d2..5964f73395f 100644
--- a/chromium/content/browser/frame_host/frame_tree_browsertest.cc
+++ b/chromium/content/browser/frame_host/frame_tree_browsertest.cc
@@ -10,6 +10,7 @@
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
+#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
@@ -17,6 +18,7 @@
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
+#include "content/shell/common/shell_switches.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/test_frame_navigation_observer.h"
#include "net/dns/mock_host_resolver.h"
@@ -31,6 +33,17 @@
namespace content {
+namespace {
+
+std::string GetOriginFromRenderer(FrameTreeNode* node) {
+ std::string origin;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ node, "window.domAutomationController.send(document.origin);", &origin));
+ return origin;
+}
+
+} // namespace
+
class FrameTreeBrowserTest : public ContentBrowserTest {
public:
FrameTreeBrowserTest() {}
@@ -635,4 +648,67 @@ IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null");
}
+// FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top
+// level domain. This is an analogue to --isolate-extensions that we use inside
+// of content_browsertests, where extensions don't exist. Iceland, like an
+// extension process, is a special place with magical powers; we want to protect
+// it from outsiders.
+class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest {
+ public:
+ IsolateIcelandFrameTreeBrowserTest() {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitchASCII(switches::kIsolateSitesForTesting, "*.is");
+ }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(embedded_test_server()->Start());
+ SetupCrossSiteRedirector(embedded_test_server());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IsolateIcelandFrameTreeBrowserTest);
+};
+
+// Regression test for https://crbug.com/644966
+IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest,
+ ProcessSwitchForIsolatedBlob) {
+ // blink suppresses navigations to blob URLs of origins different from the
+ // frame initiating the navigation. We disable those checks for this test, to
+ // test what happens in a compromise scenario.
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kDisableWebSecurity);
+
+ // Set up an iframe.
+ WebContents* contents = shell()->web_contents();
+ FrameTreeNode* root =
+ static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root();
+ GURL main_url(embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(a)"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ // The navigation targets an invalid blob url; that's intentional to trigger
+ // an error response. The response should commit in a process dedicated to
+ // http://b.is.
+ std::string result;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ root,
+ "var iframe_element = document.getElementsByTagName('iframe')[0];"
+ "iframe_element.onload = () => {"
+ " domAutomationController.send('done');"
+ "};"
+ "iframe_element.src = 'blob:http://b.is:2932/';",
+ &result));
+ WaitForLoadStop(contents);
+
+ // Make sure we did a process transfer back to "b.is".
+ EXPECT_EQ(
+ " Site A ------------ proxies for B\n"
+ " +--Site B ------- proxies for A\n"
+ "Where A = http://a.com/\n"
+ " B = http://b.is/",
+ FrameTreeVisualizer().DepictFrameTree(root));
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.cc b/chromium/content/browser/frame_host/render_frame_host_manager.cc
index 093dd4a5fa8..4c2f4b604e2 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.cc
@@ -1419,8 +1419,6 @@ bool RenderFrameHostManager::IsRendererTransferNeededForNavigation(
}
BrowserContext* context = rfh->GetSiteInstance()->GetBrowserContext();
- GURL effective_url = SiteInstanceImpl::GetEffectiveURL(context, dest_url);
-
// TODO(nasko, nick): These following --site-per-process checks are
// overly simplistic. Update them to match all the cases
// considered by DetermineSiteInstanceForURL.
@@ -1434,7 +1432,7 @@ bool RenderFrameHostManager::IsRendererTransferNeededForNavigation(
// then a transfer is needed.
return rfh->GetSiteInstance()->RequiresDedicatedProcess() ||
SiteInstanceImpl::DoesSiteRequireDedicatedProcess(context,
- effective_url);
+ dest_url);
}
SiteInstance* RenderFrameHostManager::ConvertToSiteInstance(
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
index d6010572ca2..7c74682b056 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -307,7 +307,7 @@ bool ShouldServiceRequest(int process_type,
origin_string != "null";
if (has_origin) {
GURL origin(origin_string);
- if (!policy->CanCommitURL(child_id, origin) ||
+ if (!policy->CanSetAsOriginHeader(child_id, origin) ||
GetContentClient()->browser()->IsIllegalOrigin(resource_context,
child_id, origin)) {
VLOG(1) << "Killed renderer for illegal origin: " << origin_string;
diff --git a/chromium/content/browser/site_instance_impl.cc b/chromium/content/browser/site_instance_impl.cc
index 9f88bafc19e..2ae47bf7cb9 100644
--- a/chromium/content/browser/site_instance_impl.cc
+++ b/chromium/content/browser/site_instance_impl.cc
@@ -361,15 +361,18 @@ GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
// static
bool SiteInstanceImpl::DoesSiteRequireDedicatedProcess(
BrowserContext* browser_context,
- const GURL& effective_url) {
+ const GURL& url) {
// If --site-per-process is enabled, site isolation is enabled everywhere.
if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites())
return true;
- // Let the content embedder enable site isolation for specific URLs.
+ // Let the content embedder enable site isolation for specific URLs. Use the
+ // canonical site url for this check, so that schemes with nested origins
+ // (blob and filesystem) work properly.
+ GURL site_url = GetSiteForURL(browser_context, url);
if (GetContentClient()->IsSupplementarySiteIsolationModeEnabled() &&
GetContentClient()->browser()->DoesSiteRequireDedicatedProcess(
- browser_context, effective_url)) {
+ browser_context, site_url)) {
return true;
}
diff --git a/chromium/content/browser/site_instance_impl.h b/chromium/content/browser/site_instance_impl.h
index b54de7f5886..35f07d52733 100644
--- a/chromium/content/browser/site_instance_impl.h
+++ b/chromium/content/browser/site_instance_impl.h
@@ -104,14 +104,12 @@ class CONTENT_EXPORT SiteInstanceImpl : public SiteInstance,
static GURL GetEffectiveURL(BrowserContext* browser_context,
const GURL& url);
- // Returns true if pages loaded from |effective_url| ought to be handled only
- // by a renderer process isolated from other sites. If --site-per-process is
- // on the command line, this is true for all sites. In other site isolation
- // modes, only a subset of sites will require dedicated processes.
- //
- // |effective_url| must be an effective URL.
+ // Returns true if pages loaded from |url| ought to be handled only by a
+ // renderer process isolated from other sites. If --site-per-process is on the
+ // command line, this is true for all sites. In other site isolation modes,
+ // only a subset of sites will require dedicated processes.
static bool DoesSiteRequireDedicatedProcess(BrowserContext* browser_context,
- const GURL& effective_url);
+ const GURL& url);
protected:
friend class BrowsingInstance;
diff --git a/chromium/content/content_tests.gypi b/chromium/content/content_tests.gypi
index 2792d423b8e..b83997a0821 100644
--- a/chromium/content/content_tests.gypi
+++ b/chromium/content/content_tests.gypi
@@ -204,6 +204,7 @@
'browser/battery_status/battery_monitor_impl_browsertest.cc',
'browser/battery_status/battery_monitor_integration_browsertest.cc',
'browser/bluetooth/bluetooth_allowed_devices_map_unittest.cc',
+ 'browser/blob_storage/blob_url_browsertest.cc',
'browser/bookmarklet_browsertest.cc',
'browser/browser_side_navigation_browsertest.cc',
'browser/child_process_launcher_browsertest.cc',
diff --git a/chromium/content/public/browser/child_process_security_policy.h b/chromium/content/public/browser/child_process_security_policy.h
index 9a2becf0c82..f2dea9ed31d 100644
--- a/chromium/content/public/browser/child_process_security_policy.h
+++ b/chromium/content/public/browser/child_process_security_policy.h
@@ -34,11 +34,35 @@ class ChildProcessSecurityPolicy {
static CONTENT_EXPORT ChildProcessSecurityPolicy* GetInstance();
// Web-safe schemes can be requested by any child process. Once a web-safe
- // scheme has been registered, any child process can request URLs with
- // that scheme. There is no mechanism for revoking web-safe schemes.
+ // scheme has been registered, any child process can request URLs whose
+ // origins use that scheme. There is no mechanism for revoking web-safe
+ // schemes.
+ //
+ // Only call this function if URLs of this scheme are okay to host in
+ // any ordinary renderer process.
+ //
+ // Registering 'your-scheme' as web-safe also causes 'blob:your-scheme://'
+ // and 'filesystem:your-scheme://' URLs to be considered web-safe.
virtual void RegisterWebSafeScheme(const std::string& scheme) = 0;
+ // More restrictive variant of RegisterWebSafeScheme; URLs with this scheme
+ // may be requested by any child process, but navigations to this scheme may
+ // only commit in child processes that have been explicitly granted
+ // permission to do so.
+ //
+ // |always_allow_in_origin_headers| controls whether this scheme is allowed to
+ // appear as the Origin HTTP header in outbound requests, even if the
+ // originating process does not have permission to commit this scheme. This
+ // may be necessary if the scheme is used in conjunction with blink's
+ // IsolatedWorldSecurityOrigin mechanism, as for extension content scripts.
+ virtual void RegisterWebSafeIsolatedScheme(
+ const std::string& scheme,
+ bool always_allow_in_origin_headers) = 0;
+
// Returns true iff |scheme| has been registered as a web-safe scheme.
+ // TODO(nick): https://crbug.com/651534 This function does not have enough
+ // information to render an appropriate judgment for blob and filesystem URLs;
+ // change it to accept an URL instead.
virtual bool IsWebSafeScheme(const std::string& scheme) = 0;
// This permission grants only read access to a file.
@@ -58,6 +82,17 @@ class ChildProcessSecurityPolicy {
// This permission grants delete permission for |dir|.
virtual void GrantDeleteFrom(int child_id, const base::FilePath& dir) = 0;
+ // Determine whether the process has the capability to request the URL.
+ // Before servicing a child process's request for a URL, the content layer
+ // calls this method to determine whether it is safe.
+ virtual bool CanRequestURL(int child_id, const GURL& url) = 0;
+
+ // Whether the process is allowed to commit a document from the given URL.
+ // This is more restrictive than CanRequestURL, since CanRequestURL allows
+ // requests that might lead to cross-process navigations or external protocol
+ // handlers.
+ virtual bool CanCommitURL(int child_id, const GURL& url) = 0;
+
// These methods verify whether or not the child process has been granted
// permissions perform these functions on |file|.
diff --git a/chromium/content/public/browser/content_browser_client.cc b/chromium/content/public/browser/content_browser_client.cc
index 3f6d970c9f3..e248038da22 100644
--- a/chromium/content/public/browser/content_browser_client.cc
+++ b/chromium/content/public/browser/content_browser_client.cc
@@ -44,7 +44,7 @@ bool ContentBrowserClient::ShouldUseProcessPerSite(
bool ContentBrowserClient::DoesSiteRequireDedicatedProcess(
BrowserContext* browser_context,
- const GURL& effective_url) {
+ const GURL& effective_site_url) {
return false;
}
diff --git a/chromium/content/public/browser/content_browser_client.h b/chromium/content/public/browser/content_browser_client.h
index aa5ac0ebc88..38b67f620c7 100644
--- a/chromium/content/public/browser/content_browser_client.h
+++ b/chromium/content/public/browser/content_browser_client.h
@@ -189,17 +189,17 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual bool ShouldUseProcessPerSite(BrowserContext* browser_context,
const GURL& effective_url);
- // Returns true if site isolation should be enabled for |effective_url|. This
- // call allows the embedder to supplement the site isolation policy enforced
- // by the content layer.
+ // Returns true if site isolation should be enabled for |effective_site_url|.
+ // This call allows the embedder to supplement the site isolation policy
+ // enforced by the content layer.
//
// Will only be called if both of the following happen:
// 1. The embedder asked to be consulted, by returning true from
// ContentClient::IsSupplementarySiteIsolationModeEnabled().
- // 2. The content layer didn't decide to isolate |effective_url| according
- // to its internal policy (e.g. because of --site-per-process).
+ // 2. The content layer didn't decide to isolate |effective_site_url|
+ // according to its internal policy (e.g. because of --site-per-process).
virtual bool DoesSiteRequireDedicatedProcess(BrowserContext* browser_context,
- const GURL& effective_url);
+ const GURL& effective_site_url);
// Returns true unless the effective URL is part of a site that cannot live in
// a process restricted to just that site. This is only called if site