blob: 3a5119b3baf7bacd8520aa66129320e36a85c4a7 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string_view>
#include <tuple>
#include "base/base64.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ref.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_base.h"
#include "net/base/features.h"
#include "net/base/filename_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "third_party/blink/public/common/features.h"
namespace content {
using ContentSecurityPolicyBrowserTest = ContentBrowserTestBase;
// Test that the console error message for a Content Security Policy violation
// triggered by web assembly compilation does not mention the keyword
// 'wasm-eval' (which is currently only supported for extensions). This is a
// regression test for https://crbug.com/1169592.
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
WasmEvalBlockedConsoleMessage) {
GURL url = embedded_test_server()->GetURL("/csp_wasm_eval.html");
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern(
"[Report Only] Refused to compile or instantiate WebAssembly module "
"because 'unsafe-eval' is not an allowed source of script in the "
"following Content Security Policy directive: \"script-src "
"'unsafe-inline'\".\n");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
// Test that creating a duplicate Trusted Types policy will yield a console
// message containing "already exists".
//
// This & the following test together ensure that different error causes get
// appropriate messages.
//
// Note: The bulk of Trusted Types related tests are found in the WPT suite
// under trusted-types/*. These two are here, because they need to access
// console messages.
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
TrustedTypesCreatePolicyDupeMessage) {
const char* page = R"(
data:text/html,
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script';trusted-types a;">
<script>
trustedTypes.createPolicy("a", {});
trustedTypes.createPolicy("a", {});
</script>)";
GURL url(page);
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern("*already exists*");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
// Test that creating a Trusted Types policy with a disallowed name will yield
// a console message indicating a directive has been violated.
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
TrustedTypesCreatePolicyForbiddenMessage) {
const char* page = R"(
data:text/html,
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script';trusted-types a;">
<script>
trustedTypes.createPolicy("b", {});
</script>)";
GURL url(page);
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern("*violates*the following*directive*");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
WildcardNotMatchingNonNetworkSchemeBrowserSide) {
const char* page = R"(
data:text/html,
<meta http-equiv="Content-Security-Policy" content="frame-src *">
<iframe src="mailto:[email protected]"></iframe>
)";
GURL url(page);
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern(
"Refused to frame '' because it violates the following Content Security "
"Policy directive: \"frame-src *\". Note that '*' matches only URLs with "
"network schemes ('http', 'https', 'ws', 'wss'), or URLs whose scheme "
"matches `self`'s scheme. The scheme 'mailto:' must be added "
"explicitly.\n");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
WildcardNotMatchingNonNetworkSchemeRendererSide) {
const char* page = R"(
data:text/html,
<meta http-equiv="Content-Security-Policy" content="script-src *">
<script src="mailto:[email protected]"></script>
)";
GURL url(page);
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern(
"Refused to load the script 'mailto:[email protected]' because "
"it violates the following Content Security Policy directive: "
"\"script-src *\". Note that 'script-src-elem' was not explicitly set, "
"so 'script-src' is used as a fallback. Note that '*' matches only URLs "
"with network schemes ('http', 'https', 'ws', 'wss'), or URLs whose "
"scheme matches `self`'s scheme. The scheme 'mailto:' must be added "
"explicitly.\n");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
namespace {
base::FilePath TestFilePath(const char* filename) {
base::ScopedAllowBlockingForTesting allow_blocking;
return GetTestFilePath("", filename);
}
} // namespace
// We test that we correctly match the file: scheme against file: URLs.
// Unfortunately, we cannot write this as Web Platform Test since Web Platform
// Tests don't support file: urls.
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest, FileURLs) {
GURL::Replacements add_localhost;
add_localhost.SetHostStr("localhost");
GURL::Replacements none;
struct {
const char* csp;
std::string element_name;
const raw_ref<const GURL::Replacements> document_host;
const raw_ref<const GURL::Replacements> element_host;
bool expect_allowed;
} test_cases[] = {
{"img-src 'none'", "img", raw_ref(none), raw_ref(none), false},
{"img-src file:", "img", raw_ref(none), raw_ref(none), true},
{"img-src 'self'", "img", raw_ref(none), raw_ref(none), true},
{"img-src 'none'", "img", raw_ref(none), raw_ref(add_localhost), false},
{"img-src file:", "img", raw_ref(none), raw_ref(add_localhost), true},
{"img-src 'self'", "img", raw_ref(none), raw_ref(add_localhost), true},
{"img-src 'none'", "img", raw_ref(add_localhost), raw_ref(none), false},
{"img-src file:", "img", raw_ref(add_localhost), raw_ref(none), true},
{"img-src 'self'", "img", raw_ref(add_localhost), raw_ref(none), true},
{"img-src 'none'", "img", raw_ref(add_localhost), raw_ref(add_localhost),
false},
{"img-src file:", "img", raw_ref(add_localhost), raw_ref(add_localhost),
true},
{"img-src 'self'", "img", raw_ref(add_localhost), raw_ref(add_localhost),
true},
{"frame-src 'none'", "iframe", raw_ref(none), raw_ref(none), false},
{"frame-src file:", "iframe", raw_ref(none), raw_ref(none), true},
{"frame-src 'self'", "iframe", raw_ref(none), raw_ref(none), true},
{"frame-src 'none'", "iframe", raw_ref(none), raw_ref(add_localhost),
false},
{"frame-src file:", "iframe", raw_ref(none), raw_ref(add_localhost),
true},
// TODO(antoniosartori): The following one behaves differently than
// img-src.
{"frame-src 'self'", "iframe", raw_ref(none), raw_ref(add_localhost),
true},
{"frame-src 'none'", "iframe", raw_ref(add_localhost), raw_ref(none),
false},
{"frame-src file:", "iframe", raw_ref(add_localhost), raw_ref(none),
true},
// TODO(antoniosartori): The following one behaves differently than
// img-src.
{"frame-src 'self'", "iframe", raw_ref(add_localhost), raw_ref(none),
true},
{"frame-src 'none'", "iframe", raw_ref(add_localhost),
raw_ref(add_localhost), false},
{"frame-src file:", "iframe", raw_ref(add_localhost),
raw_ref(add_localhost), true},
{"frame-src 'self'", "iframe", raw_ref(add_localhost),
raw_ref(add_localhost), true},
};
for (const auto& test_case : test_cases) {
GURL document_url = net::FilePathToFileURL(TestFilePath("hello.html"))
.ReplaceComponents(*test_case.document_host);
// On windows, if `document_url` contains the host part "localhost", the
// actual committed URL does not. So we omit EXPECT_TRUE and ignore the
// result value here.
std::ignore = NavigateToURL(shell(), document_url);
GURL element_url = net::FilePathToFileURL(TestFilePath(
test_case.element_name == "iframe" ? "empty.html" : "blank.jpg"));
element_url = element_url.ReplaceComponents(*test_case.element_host);
TestNavigationObserver load_observer(web_contents());
EXPECT_TRUE(
ExecJs(main_frame_host(),
JsReplace(R"(
var violation = new Promise(resolve => {
document.addEventListener("securitypolicyviolation", (e) => {
resolve("got violation");
});
});
let meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = $1;
document.head.appendChild(meta);
let element = document.createElement($2);
element.src = $3;
var promise = new Promise(resolve => {
element.onload = () => { resolve("allowed"); };
element.onerror = () => { resolve("blocked"); };
});
document.body.appendChild(element);
)",
test_case.csp, test_case.element_name, element_url)));
if (test_case.element_name == "iframe") {
// Since iframes always trigger the onload event, we need to be more
// careful checking whether the iframe was blocked or not.
load_observer.Wait();
const url::Origin child_origin = main_frame_host()
->child_at(0)
->current_frame_host()
->GetLastCommittedOrigin();
if (test_case.expect_allowed) {
EXPECT_TRUE(load_observer.last_navigation_succeeded())
<< element_url << " in " << document_url << " with CSPs \""
<< test_case.csp << "\" should be allowed";
EXPECT_FALSE(child_origin.opaque());
} else {
EXPECT_FALSE(load_observer.last_navigation_succeeded());
EXPECT_EQ(net::ERR_BLOCKED_BY_CSP, load_observer.last_net_error_code());
// The blocked frame's origin should become unique.
EXPECT_TRUE(child_origin.opaque())
<< element_url << " in " << document_url << " with CSPs \""
<< test_case.csp << "\" should be blocked";
}
} else {
std::string expect_message =
test_case.expect_allowed ? "allowed" : "blocked";
EXPECT_EQ(expect_message, EvalJs(main_frame_host(), "promise"))
<< element_url << " in " << document_url << " with CSPs \""
<< test_case.csp << "\" should be " << expect_message;
}
if (!test_case.expect_allowed) {
EXPECT_EQ("got violation", EvalJs(main_frame_host(), "violation"));
}
}
}
// Test that a 'csp' attribute longer than 4096 bytes is ignored.
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest, CSPAttributeTooLong) {
std::string long_csp_attribute = "script-src 'none' ";
long_csp_attribute.resize(4097, 'a');
std::string page = "data:text/html,<body><iframe csp=\"" +
long_csp_attribute + "\"></iframe></body>";
GURL url(page);
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern("'csp' attribute too long*");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
EXPECT_EQ(main_frame_host()->child_count(), 1u);
EXPECT_FALSE(main_frame_host()->child_at(0)->csp_attribute());
}
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
TransparentPlaceholderImage_ImgSrcPolicyEnforced) {
const char* page = R"(
data:text/html,
<meta http-equiv="Content-Security-Policy" content="img-src 'none';">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
)";
GURL url(page);
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern(
"Refused to load the image "
"'data:image/gif;base64,R0lGODlhAQABAIAAAP///////"
"yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' because it violates the following "
"Content Security Policy directive: \"img-src 'none'\".\n");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
IN_PROC_BROWSER_TEST_F(ContentSecurityPolicyBrowserTest,
TransparentPlaceholderImage_ImgSrcPolicyReported) {
GURL url = embedded_test_server()->GetURL("/csp_report_only_data_url.html");
WebContentsConsoleObserver console_observer(web_contents());
console_observer.SetPattern(
"[Report Only] Refused to load the image "
"'data:image/gif;base64,R0lGODlhAQABAIAAAP///////"
"yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' because it violates the following "
"Content Security Policy directive: \"img-src 'none'\".\n");
EXPECT_TRUE(NavigateToURL(shell(), url));
ASSERT_TRUE(console_observer.Wait());
}
namespace {
constexpr char kWebmPath[] = "/csp_video.webm";
std::unique_ptr<net::test_server::HttpResponse> ServeCSPMedia(
const net::test_server::HttpRequest& request) {
if (request.relative_url != kWebmPath) {
return nullptr;
}
auto cookie_header = request.headers.find("cookie");
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
if (cookie_header == request.headers.end()) {
response->set_code(net::HTTP_UNAUTHORIZED);
return std::move(response);
}
response->set_code(net::HTTP_OK);
const std::string kOneFrameOnePixelWebm =
"GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQN8VSalmQCgq17FA"
"Aw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyB"
"ACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBlrqBlh9DtnVAdOeBAKNAboEAAIDy"
"CACdASqWAJYAPk0ci0WD+IBAAJiWlu4XdQTSq2H4MW0+sMO0gz8HMRe+"
"0jRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0aNGjRo0VAAD+/729RWRzH4mOZ9/"
"O8Dl319afX4gsgAAA";
std::string content;
base::Base64Decode(kOneFrameOnePixelWebm, &content);
response->AddCustomHeader("Content-Security-Policy", "sandbox allow-scripts");
response->AddCustomHeader("Content-Type", "video/webm");
response->AddCustomHeader("Access-Control-Allow-Origin", "null");
response->AddCustomHeader("Access-Control-Allow-Credentials", "true");
response->set_content(content);
return std::move(response);
}
} // namespace
class ThirdPartyCookiesContentSecurityPolicyBrowserTest
: public ContentSecurityPolicyBrowserTest {
public:
ThirdPartyCookiesContentSecurityPolicyBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
feature_list_.InitAndEnableFeature(
net::features::kForceThirdPartyCookieBlocking);
}
void SetUpOnMainThread() override {
ContentSecurityPolicyBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
https_server()->RegisterRequestHandler(base::BindRepeating(&ServeCSPMedia));
ASSERT_TRUE(https_server()->Start());
}
void SetUpCommandLine(base::CommandLine* command_line) override {
ContentSecurityPolicyBrowserTest::SetUpCommandLine(command_line);
mock_cert_verifier_.SetUpCommandLine(command_line);
}
void SetUpInProcessBrowserTestFixture() override {
ContentSecurityPolicyBrowserTest::SetUpInProcessBrowserTestFixture();
mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
}
void TearDownInProcessBrowserTestFixture() override {
mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
ContentSecurityPolicyBrowserTest::TearDownInProcessBrowserTestFixture();
}
protected:
net::EmbeddedTestServer* https_server() { return &https_server_; }
private:
net::EmbeddedTestServer https_server_;
ContentMockCertVerifier mock_cert_verifier_;
base::test::ScopedFeatureList feature_list_;
};
// Test that CSP does not break rendering access-controlled media due to
// third-party cookie blocking.
IN_PROC_BROWSER_TEST_F(ThirdPartyCookiesContentSecurityPolicyBrowserTest,
CSPMediaThirdPartyCookieBlocking) {
ASSERT_TRUE(content::SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL("/"),
"foo=bar; SameSite=None; Secure;"));
ASSERT_TRUE(NavigateToURL(shell(), https_server()->GetURL(kWebmPath)));
EXPECT_TRUE(EvalJs(shell(),
"fetch('/csp_video.webm', {credentials: "
"'include'}).then(res => res.status == 200)")
.ExtractBool());
}
IN_PROC_BROWSER_TEST_F(ThirdPartyCookiesContentSecurityPolicyBrowserTest,
CSPMediaThirdPartyCookieBlocking_IFrame) {
ASSERT_TRUE(content::SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL("/"),
"foo=bar; SameSite=None; Secure;"));
std::string page = "data:text/html,<iframe src=\"" +
https_server()->GetURL(kWebmPath).spec() + "\"></iframe>";
ASSERT_TRUE(NavigateToURL(shell(), GURL(page)));
content::RenderFrameHost* nested_iframe = content::ChildFrameAt(shell(), 0);
EXPECT_FALSE(EvalJs(nested_iframe,
"fetch('/csp_video.webm', {credentials: "
"'include'}).then(res => res.status == 200)")
.ExtractBool());
}
namespace {
constexpr std::string_view kHostA = "a.test";
constexpr std::string_view kHostB = "b.test";
constexpr std::string_view kTopLevelPath = "/top-level.html";
constexpr std::string_view kDefaultSandboxPath = "/sandbox.html";
constexpr std::string_view kAllowSameSiteNoneCookiesSandboxPath =
"/allow-same-site-none-cookies.html";
constexpr std::string_view kIframePath = "/iframe.html";
constexpr std::string_view kCrossSiteIframePath = "/cross-site-iframe.html";
constexpr std::string_view kHandledPaths[] = {
kTopLevelPath, kDefaultSandboxPath, kAllowSameSiteNoneCookiesSandboxPath,
kIframePath, kCrossSiteIframePath};
std::unique_ptr<net::test_server::HttpResponse>
ServeCSPSandboxedWithAllowSameSiteNoneCookies(
const net::test_server::HttpRequest& request) {
if (!base::Contains(kHandledPaths, request.relative_url)) {
return nullptr;
}
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->AddCustomHeader("Access-Control-Allow-Credentials", "true");
auto origin = request.headers.find(net::HttpRequestHeaders::kOrigin);
if (origin != request.headers.end()) {
http_response->AddCustomHeader("Access-Control-Allow-Origin",
origin->second);
}
// Decide whether to include a sandboxing header
if (request.relative_url == kDefaultSandboxPath) {
http_response->AddCustomHeader("Content-Security-Policy",
"sandbox allow-scripts");
} else if (request.relative_url == kAllowSameSiteNoneCookiesSandboxPath) {
http_response->AddCustomHeader(
"Content-Security-Policy",
"sandbox allow-scripts allow-same-site-none-cookies");
}
// Check if cookie was included
auto it = request.headers.find(net::HttpRequestHeaders::kCookie);
if (it == request.headers.end()) {
CHECK_NE(request.relative_url, kIframePath);
return http_response;
}
http_response->AddCustomHeader("Content-Type", "text/plain");
http_response->set_content(it->second);
return http_response;
}
} // namespace
class AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest
: public ContentBrowserTest,
public testing::WithParamInterface<bool> {
public:
AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
feature_list_.InitWithFeatures(
{net::features::kForceThirdPartyCookieBlocking,
net::features::kAllowSameSiteNoneCookiesInSandbox},
/*disabled_features=*/{});
if (include_allow_same_site_none_cookies()) {
sandbox_iframe_policy_ = "allow-scripts allow-same-site-none-cookies";
sandbox_csp_path_ = kAllowSameSiteNoneCookiesSandboxPath;
} else {
sandbox_iframe_policy_ = "allow-scripts";
sandbox_csp_path_ = kDefaultSandboxPath;
}
}
~AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest() override =
default;
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server()->AddDefaultHandlers(GetTestDataFilePath());
https_server()->RegisterRequestHandler(
base::BindRepeating(&ServeCSPSandboxedWithAllowSameSiteNoneCookies));
ASSERT_TRUE(https_server()->Start());
}
WebContents* web_contents() const { return shell()->web_contents(); }
net::EmbeddedTestServer* https_server() { return &https_server_; }
bool include_allow_same_site_none_cookies() const { return GetParam(); }
std::string_view sandbox_csp_path() const { return sandbox_csp_path_; }
std::string_view sandbox_iframe_policy() const {
return sandbox_iframe_policy_;
}
EvalJsResult FetchWithCredentials(RenderFrameHost* frame, const GURL& url) {
constexpr char script[] = R"JS(
fetch($1, {credentials: 'include'}).then(result => result.text());
)JS";
return EvalJs(frame, JsReplace(script, url));
}
private:
base::test::ScopedFeatureList feature_list_;
net::test_server::EmbeddedTestServer https_server_;
std::string sandbox_iframe_policy_;
std::string sandbox_csp_path_;
};
INSTANTIATE_TEST_SUITE_P(
All,
AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
testing::Bool(),
[](const testing::TestParamInfo<bool>& info) {
return base::StringPrintf("_%s", (info.param)
? "AllowsSameSiteNoneCookies"
: "BlocksCookiesWithDefaultSandbox");
});
IN_PROC_BROWSER_TEST_P(AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
FrameWithSandboxCspHeader) {
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostA, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(
web_contents(), https_server()->GetURL(kHostA, sandbox_csp_path())));
// Validate that the frame attributes have been set accurately
EXPECT_NE(web_contents()->GetPrimaryMainFrame()->IsSandboxed(
network::mojom::WebSandboxFlags::kAllowSameSiteNoneCookies),
include_allow_same_site_none_cookies());
EXPECT_EQ(
web_contents()->GetPrimaryMainFrame()->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox),
include_allow_same_site_none_cookies());
// The cookie should be included in the fetch if and only if
// the `allow-same-site-none-cookies` sandbox value was provided.
EXPECT_EQ(FetchWithCredentials(web_contents()->GetPrimaryMainFrame(),
https_server()->GetURL(kHostA, kTopLevelPath)),
include_allow_same_site_none_cookies() ? "foo=bar" : "");
}
IN_PROC_BROWSER_TEST_P(AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
CrossSiteCookiesNotAllowedBySandboxValue) {
GURL url =
https_server()->GetURL(kHostA, kAllowSameSiteNoneCookiesSandboxPath);
GURL cross_site_url = https_server()->GetURL(kHostB, kDefaultSandboxPath);
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostB, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(web_contents(), url));
EXPECT_TRUE(
web_contents()->GetPrimaryMainFrame()->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox));
// Expect cross-site cookie to be blocked for cross-site url fetch.
EXPECT_EQ(FetchWithCredentials(web_contents()->GetPrimaryMainFrame(),
cross_site_url),
"");
}
IN_PROC_BROWSER_TEST_P(AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
FrameWithReloadAllowsSameSiteNoneCookies) {
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostA, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(
web_contents(), https_server()->GetURL(kHostA, sandbox_csp_path())));
// Initiate a frame reload, the origin for this reload is not opaque.
ASSERT_TRUE(content::ExecJs(web_contents()->GetPrimaryMainFrame(),
"window.location.reload();"));
WaitForLoadStop(web_contents());
// Validate that the frame attributes have been set accurately
EXPECT_NE(web_contents()->GetPrimaryMainFrame()->IsSandboxed(
network::mojom::WebSandboxFlags::kAllowSameSiteNoneCookies),
include_allow_same_site_none_cookies());
EXPECT_EQ(
web_contents()->GetPrimaryMainFrame()->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox),
include_allow_same_site_none_cookies());
// The override is used in future subresource requests from the frame if the
// `allow-same-site-none-cookies` sandbox value was provided.
EXPECT_EQ(FetchWithCredentials(web_contents()->GetPrimaryMainFrame(),
https_server()->GetURL(kHostA, kTopLevelPath)),
include_allow_same_site_none_cookies() ? "foo=bar" : "");
}
IN_PROC_BROWSER_TEST_P(AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
IframeWithSandboxAttribute) {
GURL top_level = https_server()->GetURL(kHostA, kTopLevelPath);
GURL iframe_url = https_server()->GetURL(kHostA, kIframePath);
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostA, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(shell(), top_level));
std::string iframe_body = JsReplace(
R"(document.body.innerHTML =
'<iframe id="test" src=$1 sandbox=$2></iframe>';)",
iframe_url.spec(), sandbox_iframe_policy());
ASSERT_TRUE(
content::ExecJs(web_contents()->GetPrimaryMainFrame(), iframe_body));
WaitForLoadStop(web_contents());
// Validate the frame attributes were set accurately
content::RenderFrameHost* child_frame = content::ChildFrameAt(shell(), 0);
EXPECT_NE(child_frame->IsSandboxed(
network::mojom::WebSandboxFlags::kAllowSameSiteNoneCookies),
include_allow_same_site_none_cookies());
EXPECT_EQ(child_frame->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox),
include_allow_same_site_none_cookies());
// The cookie should be included in the fetch if and only if
// the `allow-same-site-none-cookies` sandbox value was provided.
EXPECT_EQ(FetchWithCredentials(child_frame, top_level),
include_allow_same_site_none_cookies() ? "foo=bar" : "");
}
IN_PROC_BROWSER_TEST_P(
AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
CrossSiteIframeWithSandboxAttributeBlocksSameSiteNoneCookies) {
GURL top_level = https_server()->GetURL(kHostA, kTopLevelPath);
GURL iframe_url = https_server()->GetURL(kHostB, kCrossSiteIframePath);
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostB, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(shell(), top_level));
std::string iframe_body = base::StrCat(
{R"(document.body.innerHTML = '<iframe id="test" src=")",
iframe_url.spec(),
R"(" sandbox="allow-scripts allow-same-site-none-cookies ")",
R"("></iframe>';)"});
ASSERT_TRUE(
content::ExecJs(web_contents()->GetPrimaryMainFrame(), iframe_body));
WaitForLoadStop(web_contents());
// Verify override is not set on cross-site frame that inherits sandboxing
// policy and cross-site cookie is blocked
content::RenderFrameHost* child_frame = content::ChildFrameAt(shell(), 0);
EXPECT_FALSE(child_frame->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox));
EXPECT_EQ(FetchWithCredentials(child_frame, iframe_url), "");
}
IN_PROC_BROWSER_TEST_P(AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
IframeInheritsSandboxAllowsSameSiteNoneCookies) {
GURL top_level =
https_server()->GetURL(kHostA, kAllowSameSiteNoneCookiesSandboxPath);
GURL iframe_url = https_server()->GetURL(kHostA, kIframePath);
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostA, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(shell(), top_level));
std::string iframe_body =
base::StrCat({R"(document.body.innerHTML = '<iframe id="test" src=")",
iframe_url.spec(), R"("></iframe>';)"});
ASSERT_TRUE(
content::ExecJs(web_contents()->GetPrimaryMainFrame(), iframe_body));
WaitForLoadStop(web_contents());
// Check the override is set by the inherited sandbox policy and the
// cookie is included
content::RenderFrameHost* child_frame = content::ChildFrameAt(shell(), 0);
EXPECT_TRUE(child_frame->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox));
EXPECT_EQ(FetchWithCredentials(child_frame, iframe_url), "foo=bar");
}
IN_PROC_BROWSER_TEST_P(
AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
CrossSiteIframeInheritsSandboxBlocksSameSiteNoneCookies) {
GURL top_level =
https_server()->GetURL(kHostA, kAllowSameSiteNoneCookiesSandboxPath);
GURL iframe_url = https_server()->GetURL(kHostB, kCrossSiteIframePath);
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostA, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(shell(), top_level));
std::string iframe_body =
base::StrCat({R"(document.body.innerHTML = '<iframe id="test" src=")",
iframe_url.spec(), R"("></iframe>';)"});
ASSERT_TRUE(
content::ExecJs(web_contents()->GetPrimaryMainFrame(), iframe_body));
WaitForLoadStop(web_contents());
// Verify override is not applied and cookie is blocked
content::RenderFrameHost* child_frame = content::ChildFrameAt(shell(), 0);
EXPECT_FALSE(child_frame->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox));
EXPECT_EQ(FetchWithCredentials(child_frame, iframe_url), "");
}
IN_PROC_BROWSER_TEST_P(AllowSameSiteNoneCookiesContentSecurityPolicyBrowserTest,
ABAIframeInheritsSandboxBlocksSameSiteNoneCookies) {
GURL top_level =
https_server()->GetURL(kHostA, kAllowSameSiteNoneCookiesSandboxPath);
GURL cross_site_iframe_url =
https_server()->GetURL(kHostB, kCrossSiteIframePath);
GURL grandchild_iframe_url = https_server()->GetURL(kHostA, kTopLevelPath);
ASSERT_TRUE(SetCookie(web_contents()->GetBrowserContext(),
https_server()->GetURL(kHostA, kTopLevelPath),
"foo=bar;SameSite=None;Secure;"));
ASSERT_TRUE(NavigateToURL(shell(), top_level));
std::string cross_site_iframe_body =
base::StrCat({R"(document.body.innerHTML = '<iframe id="test1" src=")",
cross_site_iframe_url.spec(), R"("></iframe>';)"});
std::string grandchild_iframe_body =
base::StrCat({R"(document.body.innerHTML = '<iframe id="test2" src=")",
grandchild_iframe_url.spec(), R"("></iframe>';)"});
// Cookie is not included for a cross-site iframe and override is not set
ASSERT_TRUE(content::ExecJs(web_contents()->GetPrimaryMainFrame(),
cross_site_iframe_body));
WaitForLoadStop(web_contents());
content::RenderFrameHost* cross_site_iframe =
content::ChildFrameAt(shell(), 0);
ASSERT_FALSE(cross_site_iframe->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox));
ASSERT_EQ(FetchWithCredentials(cross_site_iframe, cross_site_iframe_url), "");
// Grandchild frame is same-site with the top-level but override and
// cookie should not be included due to cross-site ancestor
ASSERT_TRUE(content::ExecJs(cross_site_iframe, grandchild_iframe_body));
WaitForLoadStop(web_contents());
content::RenderFrameHost* grandchild_iframe =
content::ChildFrameAt(cross_site_iframe, 0);
EXPECT_FALSE(grandchild_iframe->GetCookieSettingOverrides().Has(
net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox));
EXPECT_EQ(FetchWithCredentials(grandchild_iframe, grandchild_iframe_url), "");
}
} // namespace content