Commit 66e7d0de authored by Takashi Toyoshima's avatar Takashi Toyoshima Committed by Commit Bot

OOR-CORS: Introduce OriginAccessList to manage origin whitelisting

This patches introduces OriginAccessList in the NetworkService
to manage origin whitelisting in the NetworkService. Also the class
is designed to be used even in Blink to unify existing whitelisting
implementation in blink::SecurityPolicy.

Bug: 870172
Cq-Include-Trybots: luci.chromium.try:linux_mojo
Change-Id: If44cfadbbf88f5b55c8bc2b01c1ae87c7a6e0a74
Tbr: mkwst@chromium.org, rdevlin.cronin@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/1183203
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#586623}
parent dce08a8e
......@@ -78,7 +78,7 @@ void ChromeExtensionsDispatcherDelegate::AddOriginAccessPermissions(
// conservative.
if (extensions::Manifest::IsComponentLocation(extension.location()) &&
is_extension_active) {
blink::WebSecurityPolicy::AddOriginAccessWhitelistEntry(
blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
extension.url(), blink::WebString::FromUTF8(content::kChromeUIScheme),
blink::WebString::FromUTF8(chrome::kChromeUIThemeHost), false);
}
......@@ -88,7 +88,7 @@ void ChromeExtensionsDispatcherDelegate::AddOriginAccessPermissions(
// changes.
if (is_extension_active && extension.permissions_data()->HasAPIPermission(
extensions::APIPermission::kManagement)) {
blink::WebSecurityPolicy::AddOriginAccessWhitelistEntry(
blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
extension.url(), blink::WebString::FromUTF8(content::kChromeUIScheme),
blink::WebString::FromUTF8(chrome::kChromeUIExtensionIconHost), false);
}
......
......@@ -156,7 +156,7 @@ class TestRunnerBindings : public gin::Wrappable<TestRunnerBindings> {
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
void AddOriginAccessWhitelistEntry(const std::string& source_origin,
void AddOriginAccessAllowListEntry(const std::string& source_origin,
const std::string& destination_protocol,
const std::string& destination_host,
bool allow_destination_subdomains);
......@@ -401,8 +401,8 @@ gin::ObjectTemplateBuilder TestRunnerBindings::GetObjectTemplateBuilder(
return gin::Wrappable<TestRunnerBindings>::GetObjectTemplateBuilder(isolate)
.SetMethod("abortModal", &TestRunnerBindings::NotImplemented)
.SetMethod("addDisallowedURL", &TestRunnerBindings::NotImplemented)
.SetMethod("addOriginAccessWhitelistEntry",
&TestRunnerBindings::AddOriginAccessWhitelistEntry)
.SetMethod("addOriginAccessAllowListEntry",
&TestRunnerBindings::AddOriginAccessAllowListEntry)
.SetMethod("addWebPageOverlay", &TestRunnerBindings::AddWebPageOverlay)
.SetMethod("callShouldCloseOnWebView",
&TestRunnerBindings::CallShouldCloseOnWebView)
......@@ -841,13 +841,20 @@ void TestRunnerBindings::SetIsolatedWorldContentSecurityPolicy(
view_runner_->SetIsolatedWorldContentSecurityPolicy(world_id, policy);
}
void TestRunnerBindings::AddOriginAccessWhitelistEntry(
void TestRunnerBindings::AddOriginAccessAllowListEntry(
const std::string& source_origin,
const std::string& destination_protocol,
const std::string& destination_host,
bool allow_destination_subdomains) {
if (runner_) {
runner_->AddOriginAccessWhitelistEntry(source_origin, destination_protocol,
// Non-standard schemes should be added to the scheme registeries to use
// for the origin access whitelisting.
GURL url(source_origin);
DCHECK(url.is_valid());
DCHECK(url.has_scheme());
DCHECK(url.has_host());
runner_->AddOriginAccessAllowListEntry(source_origin, destination_protocol,
destination_host,
allow_destination_subdomains);
}
......@@ -1551,7 +1558,7 @@ void TestRunner::Reset() {
mock_screen_orientation_client_->ResetData();
drag_image_.reset();
WebSecurityPolicy::ResetOriginAccessWhitelists();
WebSecurityPolicy::ClearOriginAccessAllowList();
#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
WebFontRenderStyle::SetSubpixelPositioning(false);
#endif
......@@ -2081,7 +2088,7 @@ void TestRunner::ResetTestHelperControllers() {
test_interfaces_->ResetTestHelperControllers();
}
void TestRunner::AddOriginAccessWhitelistEntry(
void TestRunner::AddOriginAccessAllowListEntry(
const std::string& source_origin,
const std::string& destination_protocol,
const std::string& destination_host,
......@@ -2090,7 +2097,7 @@ void TestRunner::AddOriginAccessWhitelistEntry(
if (!url.IsValid())
return;
WebSecurityPolicy::AddOriginAccessWhitelistEntry(
WebSecurityPolicy::AddOriginAccessAllowListEntry(
url, WebString::FromUTF8(destination_protocol),
WebString::FromUTF8(destination_host), allow_destination_subdomains);
}
......
......@@ -237,15 +237,11 @@ class TestRunner : public WebTestRunner {
void SetCloseRemainingWindowsWhenComplete(bool close_remaining_windows);
void ResetTestHelperControllers();
// Allows layout tests to manage origins' whitelisting.
void AddOriginAccessWhitelistEntry(const std::string& source_origin,
// Allows layout tests to manage origins' allow list.
void AddOriginAccessAllowListEntry(const std::string& source_origin,
const std::string& destination_protocol,
const std::string& destination_host,
bool allow_destination_subdomains);
void RemoveOriginAccessWhitelistEntry(const std::string& source_origin,
const std::string& destination_protocol,
const std::string& destination_host,
bool allow_destination_subdomains);
// Add |source_code| as an injected stylesheet to the active document of the
// window of the current V8 context.
......
......@@ -1134,7 +1134,7 @@ void Dispatcher::OnUnloaded(const std::string& id) {
// Update the origin access map so that any content scripts injected are no
// longer allowlisted for extra origins.
WebSecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
WebSecurityPolicy::ClearOriginAccessAllowListForOrigin(
Extension::GetBaseURLFromExtensionId(id));
// We don't do anything with existing platform-app stylesheets. They will
......@@ -1231,7 +1231,7 @@ void Dispatcher::UpdateActiveExtensions() {
void Dispatcher::InitOriginPermissions(const Extension* extension) {
const GURL webstore_launch_url = extension_urls::GetWebstoreLaunchURL();
WebSecurityPolicy::AddOriginAccessBlacklistEntry(
WebSecurityPolicy::AddOriginAccessBlockListEntry(
extension->url(), WebString::FromUTF8(webstore_launch_url.scheme()),
WebString::FromUTF8(webstore_launch_url.host()), true);
......@@ -1255,8 +1255,7 @@ void Dispatcher::UpdateOriginPermissions(const Extension& extension) {
};
// Remove all old patterns associated with this extension.
WebSecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
extension.url());
WebSecurityPolicy::ClearOriginAccessAllowListForOrigin(extension.url());
delegate_->AddOriginAccessPermissions(extension,
IsExtensionActive(extension.id()));
......@@ -1268,7 +1267,7 @@ void Dispatcher::UpdateOriginPermissions(const Extension& extension) {
const char* scheme = kSchemes[i];
for (const auto& pattern : patterns) {
if (pattern.MatchesScheme(scheme)) {
WebSecurityPolicy::AddOriginAccessWhitelistEntry(
WebSecurityPolicy::AddOriginAccessAllowListEntry(
extension.url(), WebString::FromUTF8(scheme),
WebString::FromUTF8(pattern.host()), pattern.match_subdomains());
}
......
......@@ -12,6 +12,8 @@ component("cpp") {
"cors/cors.h",
"cors/origin_access_entry.cc",
"cors/origin_access_entry.h",
"cors/origin_access_list.cc",
"cors/origin_access_list.h",
"cors/preflight_cache.cc",
"cors/preflight_cache.h",
"cors/preflight_result.cc",
......@@ -143,6 +145,7 @@ source_set("tests") {
sources = [
"cors/cors_unittest.cc",
"cors/origin_access_entry_unittest.cc",
"cors/origin_access_list_unittest.cc",
"cors/preflight_cache_unittest.cc",
"cors/preflight_result_unittest.cc",
"cross_thread_shared_url_loader_factory_info_unittest.cc",
......
// Copyright 2018 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 "services/network/public/cpp/cors/origin_access_list.h"
namespace network {
namespace cors {
OriginAccessList::OriginAccessList() = default;
OriginAccessList::~OriginAccessList() = default;
void OriginAccessList::SetAllowListForOrigin(
const url::Origin& source_origin,
const std::vector<mojom::CorsOriginPatternPtr>& patterns) {
SetForOrigin(source_origin, patterns, &allow_list_);
}
void OriginAccessList::AddAllowListEntryForOrigin(
const url::Origin& source_origin,
const std::string& protocol,
const std::string& domain,
bool allow_subdomains) {
AddForOrigin(source_origin, protocol, domain, allow_subdomains, &allow_list_);
}
void OriginAccessList::ClearAllowList() {
allow_list_.clear();
}
void OriginAccessList::SetBlockListForOrigin(
const url::Origin& source_origin,
const std::vector<mojom::CorsOriginPatternPtr>& patterns) {
SetForOrigin(source_origin, patterns, &block_list_);
}
void OriginAccessList::AddBlockListEntryForOrigin(
const url::Origin& source_origin,
const std::string& protocol,
const std::string& domain,
bool allow_subdomains) {
AddForOrigin(source_origin, protocol, domain, allow_subdomains, &block_list_);
}
void OriginAccessList::ClearBlockList() {
block_list_.clear();
}
bool OriginAccessList::IsAllowed(const url::Origin& source_origin,
const GURL& destination) const {
if (source_origin.unique())
return false;
std::string source = source_origin.Serialize();
url::Origin destination_origin = url::Origin::Create(destination);
return IsInMapForOrigin(source, destination_origin, allow_list_) &&
!IsInMapForOrigin(source, destination_origin, block_list_);
}
// static
void OriginAccessList::SetForOrigin(
const url::Origin& source_origin,
const std::vector<mojom::CorsOriginPatternPtr>& patterns,
PatternMap* map) {
DCHECK(map);
DCHECK(!source_origin.unique());
std::string source = source_origin.Serialize();
map->erase(source);
if (patterns.empty())
return;
Patterns& native_patterns = (*map)[source];
for (const auto& pattern : patterns) {
native_patterns.push_back(OriginAccessEntry(
pattern->protocol, pattern->domain,
pattern->allow_subdomains ? OriginAccessEntry::kAllowSubdomains
: OriginAccessEntry::kDisallowSubdomains));
}
}
// static
void OriginAccessList::AddForOrigin(const url::Origin& source_origin,
const std::string& protocol,
const std::string& domain,
bool allow_subdomains,
PatternMap* map) {
DCHECK(map);
DCHECK(!source_origin.unique());
std::string source = source_origin.Serialize();
(*map)[source].push_back(OriginAccessEntry(
protocol, domain,
allow_subdomains ? OriginAccessEntry::kAllowSubdomains
: OriginAccessEntry::kDisallowSubdomains));
}
// static
bool OriginAccessList::IsInMapForOrigin(const std::string& source,
const url::Origin& destination_origin,
const PatternMap& map) {
auto patterns_for_origin_it = map.find(source);
if (patterns_for_origin_it == map.end())
return false;
for (const auto& entry : patterns_for_origin_it->second) {
if (entry.MatchesOrigin(destination_origin) !=
OriginAccessEntry::kDoesNotMatchOrigin) {
return true;
}
}
return false;
}
} // namespace cors
} // namespace network
// Copyright 2018 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.
#ifndef SERVICES_NETWORK_PUBLIC_CPP_CORS_ORIGIN_ACCESS_LIST_H_
#define SERVICES_NETWORK_PUBLIC_CPP_CORS_ORIGIN_ACCESS_LIST_H_
#include <map>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
#include "services/network/public/cpp/cors/origin_access_entry.h"
#include "services/network/public/mojom/cors_origin_pattern.mojom.h"
#include "url/origin.h"
namespace network {
namespace cors {
// A class to manage origin access whitelisting. It manages two lists for
// whitelisting and blacklisting. If these lists conflict, blacklisting will be
// respected. These lists are managed per source-origin basis.
class COMPONENT_EXPORT(NETWORK_CPP) OriginAccessList {
public:
OriginAccessList();
~OriginAccessList();
// Clears the old allow list for |source_origin|, and set |patterns| to the
// allow list.
void SetAllowListForOrigin(
const url::Origin& source_origin,
const std::vector<mojom::CorsOriginPatternPtr>& patterns);
// Adds a matching pattern for |protocol|, |domain|, and |allow_subdomains|
// to the allow list.
void AddAllowListEntryForOrigin(const url::Origin& source_origin,
const std::string& protocol,
const std::string& domain,
bool allow_subdomains);
// Clears the old allow list.
void ClearAllowList();
// Clears the old block list for |source_origin| and set |patterns| to the
// block list.
void SetBlockListForOrigin(
const url::Origin& source_origin,
const std::vector<mojom::CorsOriginPatternPtr>& patterns);
// Adds a matching pattern for |protocol|, |domain|, and |allow_subdomains|
// to the block list.
void AddBlockListEntryForOrigin(const url::Origin& source_origin,
const std::string& protocol,
const std::string& domain,
bool allow_subdomains);
// Clears the old block list.
void ClearBlockList();
// Returns true if |destination| is in the allow list, and not in the block
// list of the |source_origin|.
bool IsAllowed(const url::Origin& source_origin,
const GURL& destination) const;
private:
using Patterns = std::vector<OriginAccessEntry>;
using PatternMap = std::map<std::string, Patterns>;
static void SetForOrigin(
const url::Origin& source_origin,
const std::vector<mojom::CorsOriginPatternPtr>& patterns,
PatternMap* map);
static void AddForOrigin(const url::Origin& source_origin,
const std::string& protocol,
const std::string& domain,
bool allow_subdomains,
PatternMap* map);
static bool IsInMapForOrigin(const std::string& source,
const url::Origin& destination_origin,
const PatternMap& map);
PatternMap allow_list_;
PatternMap block_list_;
DISALLOW_COPY_AND_ASSIGN(OriginAccessList);
};
} // namespace cors
} // namespace network
#endif // SERVICES_NETWORK_PUBLIC_CPP_CORS_ORIGIN_ACCESS_LIST_H_
// Copyright 2018 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 "services/network/public/cpp/cors/origin_access_list.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace network {
namespace cors {
namespace {
// OriginAccessListTest is a out of blink version of blink::SecurityPolicyTest,
// but it contains only tests for the allow/block lists management.
class OriginAccessListTest : public testing::Test {
public:
OriginAccessListTest()
: https_example_origin_(url::Origin::Create(GURL("https://example.com"))),
https_sub_example_origin_(
url::Origin::Create(GURL("https://sub.example.com"))),
http_example_origin_(url::Origin::Create(GURL("http://example.com"))),
https_google_origin_(url::Origin::Create(GURL("https://google.com"))),
source_origin_(url::Origin::Create(GURL("https://chromium.org"))) {}
~OriginAccessListTest() override = default;
protected:
const url::Origin& https_example_origin() const {
return https_example_origin_;
}
const url::Origin& https_sub_example_origin() const {
return https_sub_example_origin_;
}
const url::Origin& http_example_origin() const {
return http_example_origin_;
}
const url::Origin& https_google_origin() const {
return https_google_origin_;
}
bool IsAllowed(const url::Origin& destination_origin) const {
return list_.IsAllowed(source_origin_, destination_origin.GetURL());
}
void SetAllowListEntry(const std::string& protocol,
const std::string& host,
bool allow_subdomains) {
std::vector<mojom::CorsOriginPatternPtr> patterns;
patterns.push_back(
mojom::CorsOriginPattern::New(protocol, host, allow_subdomains));
list_.SetAllowListForOrigin(source_origin_, patterns);
}
void AddAllowListEntry(const std::string& protocol,
const std::string& host,
bool allow_subdomains) {
list_.AddAllowListEntryForOrigin(source_origin_, protocol, host,
allow_subdomains);
}
void SetBlockListEntry(const std::string& protocol,
const std::string& host,
bool allow_subdomains) {
std::vector<mojom::CorsOriginPatternPtr> patterns;
patterns.push_back(
mojom::CorsOriginPattern::New(protocol, host, allow_subdomains));
list_.SetBlockListForOrigin(source_origin_, patterns);
}
void AddBlockListEntry(const std::string& protocol,
const std::string& host,
bool allow_subdomains) {
list_.AddBlockListEntryForOrigin(source_origin_, protocol, host,
allow_subdomains);
}
void ResetLists() {
std::vector<mojom::CorsOriginPatternPtr> patterns;
list_.SetAllowListForOrigin(source_origin_, patterns);
list_.SetBlockListForOrigin(source_origin_, patterns);
}
private:
url::Origin https_example_origin_;
url::Origin https_sub_example_origin_;
url::Origin http_example_origin_;
url::Origin https_google_origin_;
url::Origin source_origin_;
OriginAccessList list_;
DISALLOW_COPY_AND_ASSIGN(OriginAccessListTest);
};
TEST_F(OriginAccessListTest, IsAccessAllowed) {
// By default, no access should be allowed.
EXPECT_FALSE(IsAllowed(https_example_origin()));
EXPECT_FALSE(IsAllowed(https_sub_example_origin()));
EXPECT_FALSE(IsAllowed(http_example_origin()));
// Adding access for https://example.com should work, but should not grant
// access to subdomains or other schemes.
SetAllowListEntry("https", "example.com", false);
EXPECT_TRUE(IsAllowed(https_example_origin()));
EXPECT_FALSE(IsAllowed(https_sub_example_origin()));
EXPECT_FALSE(IsAllowed(http_example_origin()));
// Clearing the map should revoke all special access.
ResetLists();
EXPECT_FALSE(IsAllowed(https_example_origin()));
EXPECT_FALSE(IsAllowed(https_sub_example_origin()));
EXPECT_FALSE(IsAllowed(http_example_origin()));
// Adding an entry that matches subdomains should grant access to any
// subdomains.
AddAllowListEntry("https", "example.com", true);
EXPECT_TRUE(IsAllowed(https_example_origin()));
EXPECT_TRUE(IsAllowed(https_sub_example_origin()));
EXPECT_FALSE(IsAllowed(http_example_origin()));
}
TEST_F(OriginAccessListTest, IsAccessAllowedWildCard) {
// An empty domain that matches subdomains results in matching every domain.
SetAllowListEntry("https", "", true);
EXPECT_TRUE(IsAllowed(https_example_origin()));
EXPECT_TRUE(IsAllowed(https_google_origin()));
EXPECT_FALSE(IsAllowed(http_example_origin()));
}
TEST_F(OriginAccessListTest, IsAccessAllowedWithBlockListEntry) {
// The block list takes priority over the allow list.
SetAllowListEntry("https", "example.com", true);
SetBlockListEntry("https", "example.com", false);
EXPECT_FALSE(IsAllowed(https_example_origin()));
EXPECT_TRUE(IsAllowed(https_sub_example_origin()));
}
TEST_F(OriginAccessListTest, IsAccessAllowedWildcardWithBlockListEntry) {
SetAllowListEntry("https", "", true);
AddBlockListEntry("https", "google.com", false);
EXPECT_TRUE(IsAllowed(https_example_origin()));
EXPECT_FALSE(IsAllowed(https_google_origin()));
}
} // namespace
} // namespace cors
} // namespace network
......@@ -76,6 +76,7 @@ mojom("mojom") {
sources = [
"cookie_manager.mojom",
"cors.mojom",
"cors_origin_pattern.mojom",
"ct_log_info.mojom",
"digitally_signed.mojom",
"fetch_api.mojom",
......
......@@ -4,6 +4,8 @@
module network.mojom;
// TODO(crbug.com/875759): Rename to Cors* rather than CORS*.
// A policy to decide if CORS-preflight fetch should be performed.
enum CORSPreflightPolicy {
kConsiderPreflight,
......
// Copyright 2018 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.
module network.mojom;
// Parameters for representing a access origin whitelist or blacklist for CORS.
struct CorsOriginPattern {
// The protocol part of the destination URL.
string protocol;
// The domain part of the destination URL.
string domain;
// Whether subdomains match this protocol and host pattern.
bool allow_subdomains;
};
......@@ -26,7 +26,7 @@ crbug.com/608015 http/tests/inspector-protocol/access-inspected-object.js [ Fail
# https://crbug.com/771003 - Dump history from all processes in layout tests
crbug.com/771003 http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
# https://crbug.com/585188 - testRunner.addOriginAccessWhitelistEntry is not replicated to OOPIFs.
# https://crbug.com/585188 - testRunner.addOriginAccessAllowListEntry is not replicated to OOPIFs.
crbug.com/585188 http/tests/xmlhttprequest/origin-whitelisting-all.html [ Failure ]
crbug.com/585188 virtual/outofblink-cors/http/tests/xmlhttprequest/origin-whitelisting-all.html [ Failure ]
crbug.com/585188 http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html [ Failure ]
......
......@@ -2,7 +2,7 @@
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry(location.origin, location.protocol, '', false);
testRunner.addOriginAccessAllowListEntry(location.origin, location.protocol, '', false);
}
var blobUrl = URL.createObjectURL(new Blob([
......
......@@ -6,8 +6,8 @@ header("Content-Security-Policy: sandbox allow-scripts");
<script>
var orginURL = document.URL;
test(function () {
testRunner.addOriginAccessWhitelistEntry(location.origin, location.protocol, '', false);
}, 'testRunner.addOriginAccessWhitelistEntry is required for this test');
testRunner.addOriginAccessAllowListEntry(location.origin, location.protocol, '', false);
}, 'testRunner.addOriginAccessAllowListEntry is required for this test');
test(function () {
assert_throws('SecurityError', function () {
......
......@@ -2,8 +2,8 @@
<script src="../resources/testharnessreport.js"></script>
<script>
test(function () {
testRunner.addOriginAccessWhitelistEntry(location.origin, location.protocol, '', false);
}, 'testRunner.addOriginAccessWhitelistEntry is required for this test');
testRunner.addOriginAccessAllowListEntry(location.origin, location.protocol, '', false);
}, 'testRunner.addOriginAccessAllowListEntry is required for this test');
test(function () {
// http://username@password:localhost:8000/... should be blocked.
......
......@@ -2,8 +2,8 @@
<script src="../resources/testharnessreport.js"></script>
<script>
test(function () {
testRunner.addOriginAccessWhitelistEntry(location.origin, location.protocol, '', false);
}, 'testRunner.addOriginAccessWhitelistEntry is required for this test');
testRunner.addOriginAccessAllowListEntry(location.origin, location.protocol, '', false);
}, 'testRunner.addOriginAccessAllowListEntry is required for this test');
test(function () {
// http://username@password:localhost:8000/... should be blocked.
......
......@@ -2,9 +2,9 @@
<script src="../resources/testharnessreport.js"></script>
<script>
test(function () {
testRunner.addOriginAccessWhitelistEntry(location.origin, location.protocol, '', false);
testRunner.addOriginAccessWhitelistEntry(location.origin, 'blob', '', false);
}, 'testRunner.addOriginAccessWhitelistEntry is required for this test');
testRunner.addOriginAccessAllowListEntry(location.origin, location.protocol, '', false);
testRunner.addOriginAccessAllowListEntry(location.origin, 'blob', '', false);
}, 'testRunner.addOriginAccessAllowListEntry is required for this test');
test(function () {
var blobUrl = URL.createObjectURL(new Blob());
......
......@@ -26,8 +26,10 @@ var tests = [
},
function() {
debug('XHR from isolated world with security origin that is whitelisted for XHR target');
testRunner.setIsolatedWorldSecurityOrigin(2, 'chrome-extension://123');
testRunner.addOriginAccessWhitelistEntry('chrome-extension://123', 'http', 'localhost', false);
// The scheme used for whitelisting should be recognized by the GURL.
// GURL manages it via scheme registries interfaces in //url/url_util.
testRunner.setIsolatedWorldSecurityOrigin(2, 'chrome://123');
testRunner.addOriginAccessAllowListEntry('chrome://123', 'http', 'localhost', false);
runTestInWorld(2, 'xhr');
},
function() {
......
This test is to see if a remote file can include a local image when the access has been white listed using addOriginAccessWhitelistEntry.
This test is to see if a remote file can include a local image when the access has been white listed using addOriginAccessAllowListEntry.
Test Passed. Local image loaded remotely.
......@@ -5,7 +5,7 @@
<script>
testRunner.dumpAsText();
testRunner.addOriginAccessWhitelistEntry('http://127.0.0.1:8000', 'file', '', true);
testRunner.addOriginAccessAllowListEntry('http://127.0.0.1:8000', 'file', '', true);
var localImageLocation = testRunner.pathToLocalResource('file:///tmp/LayoutTests/http/tests/security/resources/compass.jpg');
......@@ -25,7 +25,7 @@ window.onload = function() {
</script>
<p>This test is to see if a remote file can include a local image when the
access has been white listed using addOriginAccessWhitelistEntry.
access has been white listed using addOriginAccessAllowListEntry.
<p id=result>Test has not run.
......
......@@ -4,8 +4,8 @@
<script>
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry("http://127.0.0.1:8000", "http", "", true);
testRunner.addOriginAccessWhitelistEntry("http://localhost:8000", "http", "", true);
testRunner.addOriginAccessAllowListEntry("http://127.0.0.1:8000", "http", "", true);
testRunner.addOriginAccessAllowListEntry("http://localhost:8000", "http", "", true);
function log(message)
{
......
......@@ -4,7 +4,7 @@
<script>
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry("http://127.0.0.1:8000", "http", "localhost", false);
testRunner.addOriginAccessAllowListEntry("http://127.0.0.1:8000", "http", "localhost", false);
function log(message)
{
......
......@@ -4,7 +4,7 @@
<script>
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry("http://127.0.0.1:8000", "https", "localhost", false);
testRunner.addOriginAccessAllowListEntry("http://127.0.0.1:8000", "https", "localhost", false);
function log(message)
{
......
......@@ -3,7 +3,7 @@
<script>
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry("http://localhost:8000", "http", "*.0.0.1", true);
testRunner.addOriginAccessAllowListEntry("http://localhost:8000", "http", "*.0.0.1", true);
function log(message)
{
......
......@@ -3,7 +3,7 @@
<script>
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry("http://localhost:8000", "http", "127.0.0.1", false);
testRunner.addOriginAccessAllowListEntry("http://localhost:8000", "http", "127.0.0.1", false);
function log(message)
{
......
......@@ -4,7 +4,7 @@
<script>
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.addOriginAccessWhitelistEntry("http://127.0.0.1:8000", "http", "localhost", true);
testRunner.addOriginAccessAllowListEntry("http://127.0.0.1:8000", "http", "localhost", true);
function log(message)
{
......
......@@ -64,21 +64,17 @@ class WebSecurityPolicy {
BLINK_EXPORT static void RegisterURLSchemeAsFirstPartyWhenTopLevel(
const WebString&);
// Support for whitelisting access to origins beyond the same-origin policy.
BLINK_EXPORT static void AddOriginAccessWhitelistEntry(
// Support for managing allow/block access lists to origins beyond the
// same-origin policy. The block list takes priority over the allow list.
BLINK_EXPORT static void AddOriginAccessAllowListEntry(
const WebURL& source_origin,
const WebString& destination_protocol,
const WebString& destination_host,
bool allow_destination_subdomains);
BLINK_EXPORT static void RemoveAllOriginAccessWhitelistEntriesForOrigin(
BLINK_EXPORT static void ClearOriginAccessAllowListForOrigin(
const WebURL& source_origin);
BLINK_EXPORT static void ResetOriginAccessWhitelists();
// Support for restricting the whitelists, in order to allow for broad
// whitelist access (e.g., "chromium.org") while protecting a subset of hosts
// (e.g., "secure.chromium.org"). If an origin is in both the whitelist and
// the blacklist, it is disallowed access.
BLINK_EXPORT static void AddOriginAccessBlacklistEntry(
BLINK_EXPORT static void ClearOriginAccessAllowList();
BLINK_EXPORT static void AddOriginAccessBlockListEntry(
const WebURL& source_origin,
const WebString& destination_protocol,
const WebString& destination_host,
......
......@@ -65,32 +65,32 @@ void WebSecurityPolicy::RegisterURLSchemeAsFirstPartyWhenTopLevel(
SchemeRegistry::RegisterURLSchemeAsFirstPartyWhenTopLevel(scheme);
}
void WebSecurityPolicy::AddOriginAccessWhitelistEntry(
void WebSecurityPolicy::AddOriginAccessAllowListEntry(
const WebURL& source_origin,
const WebString& destination_protocol,
const WebString& destination_host,
bool allow_destination_subdomains) {
SecurityPolicy::AddOriginAccessWhitelistEntry(
SecurityPolicy::AddOriginAccessAllowListEntry(
*SecurityOrigin::Create(source_origin), destination_protocol,
destination_host, allow_destination_subdomains);
}
void WebSecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
void WebSecurityPolicy::ClearOriginAccessAllowListForOrigin(
const WebURL& source_origin) {
SecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
SecurityPolicy::ClearOriginAccessAllowListForOrigin(
*SecurityOrigin::Create(source_origin));
}
void WebSecurityPolicy::ResetOriginAccessWhitelists() {
SecurityPolicy::ResetOriginAccessWhitelists();
void WebSecurityPolicy::ClearOriginAccessAllowList() {
SecurityPolicy::ClearOriginAccessAllowList();
}
void WebSecurityPolicy::AddOriginAccessBlacklistEntry(
void WebSecurityPolicy::AddOriginAccessBlockListEntry(
const WebURL& source_origin,
const WebString& destination_protocol,
const WebString& destination_host,
bool allow_destination_subdomains) {
SecurityPolicy::AddOriginAccessBlacklistEntry(
SecurityPolicy::AddOriginAccessBlockListEntry(
*SecurityOrigin::Create(source_origin), destination_protocol,
destination_host, allow_destination_subdomains);
}
......
......@@ -10,6 +10,8 @@ include_rules = [
# so we have separate DEPS from platform's one.
"+net/base",
"+services/network/public/cpp/cors/origin_access_entry.h",
"+services/network/public/cpp/cors/origin_access_list.h",
"+services/network/public/mojom/cors_origin_pattern.mojom-shared.h",
"+third_party/blink/renderer/platform/blob/blob_url.h",
"+third_party/blink/renderer/platform/platform_export.h",
"+third_party/blink/renderer/platform/runtime_enabled_features.h",
......
......@@ -313,7 +313,7 @@ bool SecurityOrigin::CanRequest(const KURL& url) const {
if (IsSameSchemeHostPort(target_origin.get()))
return true;
if (SecurityPolicy::IsAccessWhiteListed(this, target_origin.get()))
if (SecurityPolicy::IsOriginAccessAllowed(this, target_origin.get()))
return true;
return false;
......@@ -341,13 +341,15 @@ bool SecurityOrigin::CanDisplay(const KURL& url) const {
if (SchemeRegistry::CanDisplayOnlyIfCanRequest(protocol))
return CanRequest(url);
if (SchemeRegistry::ShouldTreatURLSchemeAsDisplayIsolated(protocol))
if (SchemeRegistry::ShouldTreatURLSchemeAsDisplayIsolated(protocol)) {
return protocol_ == protocol ||
SecurityPolicy::IsAccessToURLWhiteListed(this, url);
SecurityPolicy::IsOriginAccessToURLAllowed(this, url);
}
if (SchemeRegistry::ShouldTreatURLSchemeAsLocal(protocol))
if (SchemeRegistry::ShouldTreatURLSchemeAsLocal(protocol)) {
return CanLoadLocalResources() ||
SecurityPolicy::IsAccessToURLWhiteListed(this, url);
SecurityPolicy::IsOriginAccessToURLAllowed(this, url);
}
return true;
}
......@@ -358,8 +360,9 @@ bool SecurityOrigin::IsPotentiallyTrustworthy() const {
return is_opaque_origin_potentially_trustworthy_;
if (SchemeRegistry::ShouldTreatURLSchemeAsSecure(protocol_) || IsLocal() ||
IsLocalhost())
IsLocalhost()) {
return true;
}
if (SecurityPolicy::IsOriginWhiteListedTrustworthy(*this))
return true;
......
......@@ -274,7 +274,7 @@ TEST_F(SecurityOriginTest, CanRequestWithWhitelistedAccess) {
EXPECT_FALSE(origin->CanRequest(url));
// Adding the url to the access whitelist should allow the request.
SecurityPolicy::AddOriginAccessWhitelistEntry(*origin, "https", "example.com",
SecurityPolicy::AddOriginAccessAllowListEntry(*origin, "https", "example.com",
false);
EXPECT_TRUE(origin->CanRequest(url));
}
......
......@@ -31,92 +31,45 @@
#include <memory>
#include "base/strings/pattern.h"
#include "services/network/public/cpp/cors/origin_access_list.h"
#include "services/network/public/mojom/cors_origin_pattern.mojom-shared.h"
#include "third_party/blink/public/platform/web_referrer_policy.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
#include "third_party/blink/renderer/platform/wtf/threading.h"
#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
#include "url/gurl.h"
namespace blink {
using OriginAccessList = Vector<OriginAccessEntry>;
using OriginAccessMap = HashMap<String, std::unique_ptr<OriginAccessList>>;
using OriginSet = HashSet<String>;
static OriginAccessMap& GetOriginAccessWhitelistMap() {
DEFINE_STATIC_LOCAL(OriginAccessMap, origin_access_whitelist_map, ());
return origin_access_whitelist_map;
static Mutex& GetMutex() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ());
return mutex;
}
static OriginAccessMap& GetOriginAccessBlacklistMap() {
DEFINE_STATIC_LOCAL(OriginAccessMap, origin_access_blacklist_map, ());
return origin_access_blacklist_map;
static network::cors::OriginAccessList& GetOriginAccessList() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(network::cors::OriginAccessList,
origin_access_list, ());
return origin_access_list;
}
using OriginSet = HashSet<String>;
static OriginSet& TrustworthyOriginSet() {
DEFINE_STATIC_LOCAL(OriginSet, trustworthy_origin_set, ());
return trustworthy_origin_set;
}
static void AddOriginAccessEntry(const SecurityOrigin& source_origin,
const String& destination_protocol,
const String& destination_domain,
bool allow_destination_subdomains,
OriginAccessMap& access_map) {
DCHECK(IsMainThread());
DCHECK(!source_origin.IsOpaque());
if (source_origin.IsOpaque())
return;
String source_string = source_origin.ToString();
OriginAccessMap::AddResult result = access_map.insert(source_string, nullptr);
if (result.is_new_entry)
result.stored_value->value = std::make_unique<OriginAccessList>();
OriginAccessList* list = result.stored_value->value.get();
list->push_back(OriginAccessEntry(
destination_protocol, destination_domain,
allow_destination_subdomains
? network::cors::OriginAccessEntry::kAllowSubdomains
: network::cors::OriginAccessEntry::kDisallowSubdomains));
}
static void RemoveAllOriginAccessEntriesForOrigin(
const SecurityOrigin& source_origin,
OriginAccessMap& access_map) {
DCHECK(IsMainThread());
DCHECK(!source_origin.IsOpaque());
access_map.erase(source_origin.ToString());
}
static bool IsOriginPairInAccessMap(const SecurityOrigin* active_origin,
const SecurityOrigin* target_origin,
const OriginAccessMap& access_map) {
if (access_map.IsEmpty())
return false;
if (OriginAccessList* list = access_map.at(active_origin->ToString())) {
for (size_t i = 0; i < list->size(); ++i) {
if (list->at(i).MatchesOrigin(*target_origin) !=
network::cors::OriginAccessEntry::kDoesNotMatchOrigin)
return true;
}
}
return false;
}
void SecurityPolicy::Init() {
GetOriginAccessWhitelistMap();
GetOriginAccessBlacklistMap();
TrustworthyOriginSet();
}
......@@ -267,56 +220,59 @@ bool SecurityPolicy::IsUrlWhiteListedTrustworthy(const KURL& url) {
return IsOriginWhiteListedTrustworthy(*SecurityOrigin::Create(url).get());
}
bool SecurityPolicy::IsAccessWhiteListed(const SecurityOrigin* active_origin,
const SecurityOrigin* target_origin) {
return IsOriginPairInAccessMap(active_origin, target_origin,
GetOriginAccessWhitelistMap()) &&
!IsOriginPairInAccessMap(active_origin, target_origin,
GetOriginAccessBlacklistMap());
bool SecurityPolicy::IsOriginAccessAllowed(
const SecurityOrigin* active_origin,
const SecurityOrigin* target_origin) {
MutexLocker lock(GetMutex());
return GetOriginAccessList().IsAllowed(active_origin->ToUrlOrigin(),
target_origin->ToUrlOrigin().GetURL());
}
bool SecurityPolicy::IsAccessToURLWhiteListed(
bool SecurityPolicy::IsOriginAccessToURLAllowed(
const SecurityOrigin* active_origin,
const KURL& url) {
scoped_refptr<const SecurityOrigin> target_origin =
SecurityOrigin::Create(url);
return IsAccessWhiteListed(active_origin, target_origin.get());
MutexLocker lock(GetMutex());
return GetOriginAccessList().IsAllowed(active_origin->ToUrlOrigin(), url);
}
void SecurityPolicy::AddOriginAccessWhitelistEntry(
void SecurityPolicy::AddOriginAccessAllowListEntry(
const SecurityOrigin& source_origin,
const String& destination_protocol,
const String& destination_domain,
bool allow_destination_subdomains) {
AddOriginAccessEntry(source_origin, destination_protocol, destination_domain,
allow_destination_subdomains,
GetOriginAccessWhitelistMap());
MutexLocker lock(GetMutex());
GetOriginAccessList().AddAllowListEntryForOrigin(
source_origin.ToUrlOrigin(), WebString(destination_protocol).Utf8(),
WebString(destination_domain).Utf8(), allow_destination_subdomains);
}
void SecurityPolicy::RemoveAllOriginAccessWhitelistEntriesForOrigin(
void SecurityPolicy::ClearOriginAccessAllowListForOrigin(
const SecurityOrigin& source_origin) {
RemoveAllOriginAccessEntriesForOrigin(source_origin,
GetOriginAccessWhitelistMap());
MutexLocker lock(GetMutex());
GetOriginAccessList().SetAllowListForOrigin(
source_origin.ToUrlOrigin(),
std::vector<network::mojom::CorsOriginPatternPtr>());
}
void SecurityPolicy::ResetOriginAccessWhitelists() {
DCHECK(IsMainThread());
GetOriginAccessWhitelistMap().clear();
void SecurityPolicy::ClearOriginAccessAllowList() {
MutexLocker lock(GetMutex());
GetOriginAccessList().ClearAllowList();
}
void SecurityPolicy::AddOriginAccessBlacklistEntry(
void SecurityPolicy::AddOriginAccessBlockListEntry(
const SecurityOrigin& source_origin,
const String& destination_protocol,
const String& destination_domain,
bool allow_destination_subdomains) {
AddOriginAccessEntry(source_origin, destination_protocol, destination_domain,
allow_destination_subdomains,
GetOriginAccessBlacklistMap());
MutexLocker lock(GetMutex());
GetOriginAccessList().AddBlockListEntryForOrigin(
source_origin.ToUrlOrigin(), WebString(destination_protocol).Utf8(),
WebString(destination_domain).Utf8(), allow_destination_subdomains);
}
void SecurityPolicy::ResetOriginAccessBlacklists() {
DCHECK(IsMainThread());
GetOriginAccessBlacklistMap().clear();
void SecurityPolicy::ClearOriginAccessBlockList() {
MutexLocker lock(GetMutex());
GetOriginAccessList().ClearBlockList();
}
bool SecurityPolicy::ReferrerPolicyFromString(
......
......@@ -65,24 +65,24 @@ class PLATFORM_EXPORT SecurityPolicy {
const KURL&,
const String& referrer);
static void AddOriginAccessWhitelistEntry(const SecurityOrigin& source_origin,
static void AddOriginAccessAllowListEntry(const SecurityOrigin& source_origin,
const String& destination_protocol,
const String& destination_domain,
bool allow_destination_subdomains);
static void RemoveAllOriginAccessWhitelistEntriesForOrigin(
static void ClearOriginAccessAllowListForOrigin(
const SecurityOrigin& source_origin);
static void ResetOriginAccessWhitelists();
static void ClearOriginAccessAllowList();
static void AddOriginAccessBlacklistEntry(const SecurityOrigin& source_origin,
static void AddOriginAccessBlockListEntry(const SecurityOrigin& source_origin,
const String& destination_protocol,
const String& destination_domain,
bool allow_destination_subdomains);
static void ResetOriginAccessBlacklists();
static void ClearOriginAccessBlockList();
static bool IsAccessWhiteListed(const SecurityOrigin* active_origin,
const SecurityOrigin* target_origin);
static bool IsAccessToURLWhiteListed(const SecurityOrigin* active_origin,
const KURL&);
static bool IsOriginAccessAllowed(const SecurityOrigin* active_origin,
const SecurityOrigin* target_origin);
static bool IsOriginAccessToURLAllowed(const SecurityOrigin* active_origin,
const KURL&);
static void AddOriginTrustworthyWhiteList(const String&);
static bool IsOriginWhiteListedTrustworthy(const SecurityOrigin&);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment