blob: 76ad4dcf9268a4b3629eaac4c4eb19f650339d57 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/platform_thread.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/policy/policy_constants.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "pdf/buildflags.h"
#include "services/network/public/cpp/content_security_policy/content_security_policy.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/cross_origin_opener_policy.mojom.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#if BUILDFLAG(ENABLE_PDF)
#include "base/test/with_feature_override.h"
#include "pdf/pdf_features.h"
#endif
namespace {
const int kWasmPageSize = 1 << 16;
// Path to a response that passes Private Network Access checks.
constexpr char kPnaPath[] =
"/set-header"
"?Access-Control-Allow-Origin: *"
"&Access-Control-Allow-Private-Network: true";
// Web platform security features are implemented by content/ and blink/.
// However, since ContentBrowserClientImpl::LogWebFeatureForCurrentPage() is
// currently left blank in content/, metrics logging can't be tested from
// content/. So it is tested from chrome/ instead.
class ChromeWebPlatformSecurityMetricsBrowserTest : public policy::PolicyTest {
public:
using WebFeature = blink::mojom::WebFeature;
ChromeWebPlatformSecurityMetricsBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
http_server_(net::EmbeddedTestServer::TYPE_HTTP) {
features_.InitWithFeatures(GetEnabledFeatures(), GetDisabledFeatures());
}
content::WebContents* web_contents() const {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void set_monitored_feature(WebFeature feature) {
monitored_feature_ = feature;
}
void LoadIFrame(const GURL& url) {
LoadIFrameInWebContents(web_contents(), url);
}
content::WebContents* OpenPopup(const GURL& url) {
content::WebContentsAddedObserver new_tab_observer;
EXPECT_TRUE(
content::ExecJs(web_contents(), "window.open('" + url.spec() + "')"));
content::WebContents* web_contents = new_tab_observer.GetWebContents();
EXPECT_TRUE(content::WaitForLoadStop(web_contents));
return web_contents;
}
void LoadIFrameInWebContents(content::WebContents* web_contents,
const GURL& url) {
EXPECT_EQ(true, content::EvalJs(web_contents, content::JsReplace(R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = $1;
iframe.onload = () => resolve(true);
document.body.appendChild(iframe);
});
)",
url)));
}
void ExpectHistogramIncreasedBy(int count) {
expected_count_ += count;
histogram_.ExpectBucketCount("Blink.UseCounter.Features",
monitored_feature_, expected_count_);
}
net::EmbeddedTestServer& https_server() { return https_server_; }
net::EmbeddedTestServer& http_server() { return http_server_; }
// Fetch the Blink.UseCounter.Features histogram in every renderer process
// until reaching, but not exceeding, |expected_count|.
void CheckCounter(WebFeature feature, int expected_count) {
CheckHistogramCount("Blink.UseCounter.Features", feature, expected_count);
}
// Fetch the Blink.UseCounter.MainFrame.Features histogram in every renderer
// process until reaching, but not exceeding, |expected_count|.
void CheckCounterMainFrame(WebFeature feature, int expected_count) {
CheckHistogramCount("Blink.UseCounter.MainFrame.Features", feature,
expected_count);
}
// Fetch the |histogram|'s |bucket| in every renderer process until reaching,
// but not exceeding, |expected_count|.
template <typename T>
void CheckHistogramCount(base::StringPiece histogram,
T bucket,
int expected_count) {
while (true) {
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
int count = histogram_.GetBucketCount(histogram, bucket);
CHECK_LE(count, expected_count);
if (count == expected_count)
return;
base::PlatformThread::Sleep(base::Milliseconds(5));
}
}
virtual std::vector<base::test::FeatureRef> GetEnabledFeatures() const {
return {
network::features::kCrossOriginOpenerPolicy,
// SharedArrayBuffer is needed for these tests.
features::kSharedArrayBuffer,
// Some PNA worker feature relies on this.
// TODO(crbug.com/40263073): Remove this once PNA for workers
// metric logging doesn't rely on kPlzDedicatedWorker
blink::features::kPlzDedicatedWorker,
};
}
virtual std::vector<base::test::FeatureRef> GetDisabledFeatures() const {
return {
// Disabled because some subtests set document.domain and this
// feature flag prevents that:
blink::features::kOriginAgentClusterDefaultEnabled,
};
}
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
https_server_.AddDefaultHandlers(GetChromeTestDataDir());
http_server_.AddDefaultHandlers(GetChromeTestDataDir());
// Add content/test/data for cross_site_iframe_factory.html
https_server_.ServeFilesFromSourceDirectory("content/test/data");
http_server_.ServeFilesFromSourceDirectory("content/test/data");
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
ASSERT_TRUE(https_server_.Start());
ASSERT_TRUE(http_server_.Start());
EXPECT_TRUE(content::NavigateToURL(web_contents(), GURL("about:blank")));
}
private:
void SetUpCommandLine(base::CommandLine* command_line) final {
// For https_server()
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
net::EmbeddedTestServer https_server_;
net::EmbeddedTestServer http_server_;
int expected_count_ = 0;
base::HistogramTester histogram_;
WebFeature monitored_feature_;
base::test::ScopedFeatureList features_;
};
class PrivateNetworkAccessWebSocketMetricBrowserTest
: public ChromeWebPlatformSecurityMetricsBrowserTest {
public:
PrivateNetworkAccessWebSocketMetricBrowserTest()
: ws_server_(net::SpawnedTestServer::TYPE_WS,
net::GetWebSocketTestDataDirectory()) {}
net::SpawnedTestServer& ws_server() { return ws_server_; }
std::string WaitAndGetTitle() {
return base::UTF16ToUTF8(watcher_->WaitAndGetTitle());
}
private:
void SetUpOnMainThread() override {
ChromeWebPlatformSecurityMetricsBrowserTest::SetUpOnMainThread();
watcher_ = std::make_unique<content::TitleWatcher>(
browser()->tab_strip_model()->GetActiveWebContents(), u"PASS");
watcher_->AlsoWaitForTitle(u"FAIL");
}
void TearDownOnMainThread() override { watcher_.reset(); }
net::SpawnedTestServer ws_server_;
std::unique_ptr<content::TitleWatcher> watcher_;
};
// Return the child of `parent`.
// Precondition: the number of children must be one.
content::RenderFrameHost* GetChild(content::RenderFrameHost& parent) {
content::RenderFrameHost* child_rfh = nullptr;
parent.ForEachRenderFrameHost([&](content::RenderFrameHost* rfh) {
if (&parent == rfh->GetParent()) {
CHECK(!child_rfh) << "Multiple children found";
child_rfh = rfh;
}
});
CHECK(child_rfh) << "No children found";
return child_rfh;
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. No header => 0
// count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingNoHeader) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = https_server().GetURL("a.com", "/title1.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
ExpectHistogramIncreasedBy(0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessIgnoredCrossSitePreflightError) {
ASSERT_TRUE(content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/no-favicon-treat-as-public-address.html")));
ASSERT_EQ(true, content::EvalJs(
web_contents(),
content::JsReplace(
"fetch($1).then(response => response.ok)",
https_server().GetURL("b.com", "/cors-ok.txt"))));
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightWarning, 1);
CheckCounter(
WebFeature::kPrivateNetworkAccessIgnoredCrossOriginPreflightError, 1);
CheckCounter(WebFeature::kPrivateNetworkAccessIgnoredCrossSitePreflightError,
1);
}
IN_PROC_BROWSER_TEST_F(
ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessIgnoredCrossOriginSameSitePreflightError) {
ASSERT_TRUE(content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/no-favicon-treat-as-public-address.html")));
ASSERT_EQ(true, content::EvalJs(web_contents(),
content::JsReplace(
"fetch($1).then(response => response.ok)",
https_server().GetURL("subdomain.a.com",
"/cors-ok.txt"))));
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightWarning, 1);
CheckCounter(
WebFeature::kPrivateNetworkAccessIgnoredCrossOriginPreflightError, 1);
CheckCounter(WebFeature::kPrivateNetworkAccessIgnoredCrossSitePreflightError,
0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessSameOriginNoIgnoredPreflightError) {
ASSERT_TRUE(content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/no-favicon-treat-as-public-address.html")));
ASSERT_EQ(true, content::EvalJs(
web_contents(),
content::JsReplace(
"fetch($1).then(response => response.ok)",
https_server().GetURL("a.com", "/cors-ok.txt"))));
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightWarning, 0);
CheckCounter(
WebFeature::kPrivateNetworkAccessIgnoredCrossOriginPreflightError, 0);
CheckCounter(WebFeature::kPrivateNetworkAccessIgnoredCrossSitePreflightError,
0);
}
// This test verifies that when a secure context served from the public address
// space loads a resource from the private network, the correct WebFeature is
// use-counted.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessFetchWithPreflight) {
ASSERT_TRUE(content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/no-favicon-treat-as-public-address.html")));
ASSERT_EQ(true,
content::EvalJs(
web_contents(),
content::JsReplace("fetch($1).then(response => response.ok)",
https_server().GetURL("b.com", kPnaPath))));
CheckCounter(WebFeature::kAddressSpacePublicSecureContextEmbeddedLocal, 1);
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightSuccess, 1);
}
// This test verifies that when a preflight request is sent ahead of a private
// network request, the server replies with Access-Control-Allow-Origin but
// without Access-Control-Allow-Private-Network, and enforcement is not enabled,
// the correct WebFeature is use-counted to reflect the suppressed error.
IN_PROC_BROWSER_TEST_F(
ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessFetchWithPreflightRepliedWithoutPNAHeaders) {
ASSERT_EQ(true, content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/"
"no-favicon-treat-as-public-address.html")));
// The server does not reply with valid CORS headers, so the preflight fails.
// The enforcement feature is not enabled however, so the error is suppressed.
// Instead, a warning is shown in DevTools and a WebFeature use-counted.
ASSERT_EQ(true, content::EvalJs(
web_contents(),
content::JsReplace(
"fetch($1).then(response => response.ok)",
https_server().GetURL("b.com", "/cors-ok.txt"))));
CheckCounter(WebFeature::kAddressSpacePublicSecureContextEmbeddedLocal, 1);
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightWarning, 1);
}
IN_PROC_BROWSER_TEST_F(
ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessPolicyEnabledFetchWithPreflightRepliedWithoutPNAHeaders) {
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kPrivateNetworkAccessRestrictionsEnabled,
base::Value(true));
UpdateProviderPolicy(policies);
ASSERT_EQ(true, content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/"
"no-favicon-treat-as-public-address.html")));
// The server does not reply with valid CORS headers, so the preflight fails.
// The enforcement feature is not enabled however, so the error is suppressed.
// Instead, a warning is shown in DevTools and a WebFeature use-counted.
ASSERT_EQ(false,
content::EvalJs(
web_contents(),
content::JsReplace(
"fetch($1).then(response => response.ok, error => false)",
https_server().GetURL("b.com", "/cors-ok.txt"))));
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessPolicyEnabledFetchWithPreflight) {
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kPrivateNetworkAccessRestrictionsEnabled,
base::Value(true));
UpdateProviderPolicy(policies);
ASSERT_EQ(true, content::NavigateToURL(
web_contents(),
https_server().GetURL(
"a.com",
"/private_network_access/"
"no-favicon-treat-as-public-address.html")));
// The server does not reply with valid CORS headers, so the preflight fails.
// The enforcement feature is not enabled however, so the error is suppressed.
// Instead, a warning is shown in DevTools and a WebFeature use-counted.
ASSERT_EQ(true,
content::EvalJs(
web_contents(),
content::JsReplace(
"fetch($1).then(response => response.ok, error => false)",
https_server().GetURL("b.com", kPnaPath))));
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessFetchInWorker) {
ASSERT_EQ(true,
content::NavigateToURL(
web_contents(), https_server().GetURL("a.com",
"/private_network_access/"
"no-favicon.html")));
base::StringPiece kScriptTemplate = R"(
(async () => {
const worker = new Worker("/workers/fetcher_treat_as_public.js");
const messagePromise = new Promise((resolve) => {
const listener = (event) => resolve(event.data);
worker.addEventListener("message", listener, { once: true });
});
worker.postMessage($1);
const { error, ok } = await messagePromise;
if (error !== undefined) {
throw(error);
}
return ok;
})()
)";
ASSERT_EQ(true,
content::EvalJs(web_contents(),
content::JsReplace(kScriptTemplate,
https_server().GetURL(
"b.com", "/cors-ok.txt"))));
CheckCounter(WebFeature::kPrivateNetworkAccessWithinWorker, 1);
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightWarning, 1);
}
// When WebSocket is connected to a more-private ip address space, log a use
// counter.
IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWebSocketMetricBrowserTest,
PrivateNetworkAccessWebSocketConnectedPublicToLocal) {
// Launch a WebSocket server.
ASSERT_TRUE(ws_server().Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), http_server().GetURL(
"a.com",
"/private_network_access/"
"websocket-treat-as-public-address.html"
"?url=" +
ws_server().GetURL("echo-with-no-extension").spec())));
EXPECT_EQ("PASS", WaitAndGetTitle());
CheckCounter(WebFeature::kPrivateNetworkAccessWebSocketConnected, 1);
}
// When WebSocket is connected to the same ip address space, do not log a use
// counter.
// TODO(crbug.com/336429017): Flaky on Win.
#if BUILDFLAG(IS_WIN)
#define MAYBE_PrivateNetworkAccessWebSocketConnectedLocalToLocal \
DISABLED_PrivateNetworkAccessWebSocketConnectedLocalToLocal
#else
#define MAYBE_PrivateNetworkAccessWebSocketConnectedLocalToLocal \
PrivateNetworkAccessWebSocketConnectedLocalToLocal
#endif
IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWebSocketMetricBrowserTest,
MAYBE_PrivateNetworkAccessWebSocketConnectedLocalToLocal) {
// Launch a WebSocket server.
ASSERT_TRUE(ws_server().Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), http_server().GetURL(
"a.com",
"/private_network_access/"
"websocket.html"
"?url=" +
ws_server().GetURL("echo-with-no-extension").spec())));
EXPECT_EQ("PASS", WaitAndGetTitle());
CheckCounter(WebFeature::kPrivateNetworkAccessWebSocketConnected, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
PrivateNetworkAccessFetchInSharedWorker) {
ASSERT_EQ(true,
content::NavigateToURL(
web_contents(), https_server().GetURL("a.com",
"/private_network_access/"
"no-favicon.html")));
base::StringPiece kScriptTemplate = R"(
(async () => {
const worker = await new Promise((resolve, reject) => {
const worker =
new SharedWorker("/workers/shared_fetcher_treat_as_public.js");
worker.port.addEventListener("message", () => resolve(worker));
worker.addEventListener("error", reject);
worker.port.start();
});
const messagePromise = new Promise((resolve) => {
const listener = (event) => resolve(event.data);
worker.port.addEventListener("message", listener, { once: true });
});
worker.port.postMessage($1);
const { error, ok } = await messagePromise;
if (error !== undefined) {
throw(error);
}
return ok;
})()
)";
ASSERT_EQ(true,
content::EvalJs(web_contents(),
content::JsReplace(kScriptTemplate,
https_server().GetURL(
"b.com", "/cors-ok.txt"))));
CheckCounter(WebFeature::kPrivateNetworkAccessWithinWorker, 1);
CheckCounter(WebFeature::kPrivateNetworkAccessPreflightWarning, 1);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. COOP-Report-Only +
// HTTP => 0 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingReportOnlyHTTP) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = http_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin; report-to%3d\"a\"");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
ExpectHistogramIncreasedBy(0);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. COOP-Report-Only +
// HTTPS => 1 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingReportOnlyHTTPS) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin; report-to%3d\"a\"");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
ExpectHistogramIncreasedBy(1);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. COOP + HTPS => 1
// count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingCOOPHTTPS) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin; report-to%3d\"a\"");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
ExpectHistogramIncreasedBy(1);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. COOP + COOP-RO +
// HTTPS => 1 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingCOOPAndReportOnly) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin; report-to%3d\"a\"&"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin; report-to%3d\"a\"");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
ExpectHistogramIncreasedBy(1);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. No report
// endpoints defined => 0 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingNoEndpoint) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = https_server().GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Opener-Policy-Report-Only: same-origin");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
ExpectHistogramIncreasedBy(0);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. Main frame
// (COOP-RO), subframe (COOP-RO) => 1 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingMainFrameAndSubframe) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin; report-to%3d\"a\"");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
LoadIFrame(url);
ExpectHistogramIncreasedBy(1);
}
// Check the kCrossOriginOpenerPolicyReporting feature usage. Main frame
// (no-headers), subframe (COOP-RO) => 0 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginOpenerPolicyReportingUsageSubframeOnly) {
set_monitored_feature(WebFeature::kCrossOriginOpenerPolicyReporting);
GURL main_document_url = https_server().GetURL("a.com", "/title1.html");
GURL sub_document_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy-Report-Only: "
"same-origin; report-to%3d\"a\"");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(sub_document_url);
ExpectHistogramIncreasedBy(0);
}
// Check kCrossOriginSubframeWithoutEmbeddingControl reporting. Same-origin
// iframe (no headers) => 0 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginSubframeWithoutEmbeddingControlSameOrigin) {
set_monitored_feature(
WebFeature::kCrossOriginSubframeWithoutEmbeddingControl);
GURL url = https_server().GetURL("a.com", "/title1.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
LoadIFrame(url);
ExpectHistogramIncreasedBy(0);
}
// Check kCrossOriginSubframeWithoutEmbeddingControl reporting. Cross-origin
// iframe (no headers) => 0 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginSubframeWithoutEmbeddingControlNoHeaders) {
set_monitored_feature(
WebFeature::kCrossOriginSubframeWithoutEmbeddingControl);
GURL main_document_url = https_server().GetURL("a.com", "/title1.html");
GURL sub_document_url = https_server().GetURL("b.com", "/title1.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(sub_document_url);
ExpectHistogramIncreasedBy(1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
LogCSPFrameSrcWildcardMatchFeature) {
struct {
const char* csp_frame_src;
const char* sub_document_url;
int expected_kCspWouldBlockIfWildcardDoesNotMatchWs;
int expected_kCspWouldBlockIfWildcardDoesNotMatchFtp;
} test_cases[] = {
{"*", "http://example.com", 0, 0},
// Feature shouldn't be logged if matches explicitly.
{"ftp:*", "ftp://example.com", 0, 0},
{"ws:*", "ws://example.com", 0, 0},
{"wss:*", "wss://example.com", 0, 0},
// Feature should be logged if matched with wildcard.
{
"*",
"ftp://example.com",
0,
base::FeatureList::IsEnabled(
network::features::kCspStopMatchingWildcardDirectivesToFtp)
? 0
: 1,
},
{"*", "ws://example.com", 1, 0},
{"*", "wss://example.com", 1, 0},
};
int total_kCspWouldBlockIfWildcardDoesNotMatchWs = 0;
int total_kCspWouldBlockIfWildcardDoesNotMatchFtp = 0;
for (const auto& test_case : test_cases) {
GURL main_document_url = https_server().GetURL(
"a.com",
base::StrCat({"/set-header?Content-Security-Policy: frame-src ",
test_case.csp_frame_src, ";"}));
url::Origin main_document_origin = url::Origin::Create(main_document_url);
GURL sub_document_url = GURL(test_case.sub_document_url);
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
content::TestNavigationObserver load_observer(web_contents());
EXPECT_TRUE(
content::ExecJs(web_contents(), content::JsReplace(R"(
let iframe = document.createElement("iframe");
iframe.src = $1;
document.body.appendChild(iframe);
)",
sub_document_url)));
load_observer.Wait();
CheckCounter(WebFeature::kCspWouldBlockIfWildcardDoesNotMatchWs,
total_kCspWouldBlockIfWildcardDoesNotMatchWs +=
test_case.expected_kCspWouldBlockIfWildcardDoesNotMatchWs);
CheckCounter(WebFeature::kCspWouldBlockIfWildcardDoesNotMatchFtp,
total_kCspWouldBlockIfWildcardDoesNotMatchFtp +=
test_case.expected_kCspWouldBlockIfWildcardDoesNotMatchFtp);
}
}
// Check kCrossOriginSubframeWithoutEmbeddingControl reporting. Cross-origin
// iframe (CSP frame-ancestors) => 0 count.
IN_PROC_BROWSER_TEST_F(
ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginSubframeWithoutEmbeddingControlFrameAncestors) {
set_monitored_feature(
WebFeature::kCrossOriginSubframeWithoutEmbeddingControl);
GURL main_document_url = https_server().GetURL("a.com", "/title1.html");
url::Origin main_document_origin = url::Origin::Create(main_document_url);
std::string csp_header = "Content-Security-Policy: frame-ancestors 'self' *;";
GURL sub_document_url =
https_server().GetURL("b.com", "/set-header?" + csp_header);
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(sub_document_url);
ExpectHistogramIncreasedBy(0);
}
// Check kCrossOriginSubframeWithoutEmbeddingControl reporting. Cross-origin
// iframe (blocked by CSP header) => 0 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginSubframeWithoutEmbeddingControlNoEmbedding) {
set_monitored_feature(
WebFeature::kCrossOriginSubframeWithoutEmbeddingControl);
GURL main_document_url = https_server().GetURL("a.com", "/title1.html");
GURL sub_document_url =
https_server().GetURL("b.com",
"/set-header?"
"Content-Security-Policy: frame-ancestors 'self';");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(sub_document_url);
ExpectHistogramIncreasedBy(0);
}
// Check kCrossOriginSubframeWithoutEmbeddingControl reporting. Cross-origin
// iframe (other CSP header) => 1 count.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginSubframeWithoutEmbeddingControlOtherCSP) {
set_monitored_feature(
WebFeature::kCrossOriginSubframeWithoutEmbeddingControl);
GURL main_document_url = https_server().GetURL("a.com", "/title1.html");
GURL sub_document_url =
https_server().GetURL("b.com",
"/set-header?"
"Content-Security-Policy: script-src 'self';");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(sub_document_url);
ExpectHistogramIncreasedBy(1);
}
// Check kEmbeddedCrossOriginFrameWithoutFrameAncestorsOrXFO feature usage.
// This should increment in cases where a cross-origin frame is embedded which
// does not assert either X-Frame-Options or CSP's frame-ancestors.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
EmbeddingOptIn) {
set_monitored_feature(
WebFeature::kEmbeddedCrossOriginFrameWithoutFrameAncestorsOrXFO);
GURL main_document_url = https_server().GetURL("a.com", "/title1.html");
struct TestCase {
const char* name;
const char* host;
const char* header;
bool expect_counter;
} cases[] = {{
"Same-origin, no XFO, no frame-ancestors",
"a.com",
nullptr,
false,
},
{
"Cross-origin, no XFO, no frame-ancestors",
"b.com",
nullptr,
true,
},
{
"Same-origin, yes XFO, no frame-ancestors",
"a.com",
"X-Frame-Options: ALLOWALL",
false,
},
{
"Cross-origin, yes XFO, no frame-ancestors",
"b.com",
"X-Frame-Options: ALLOWALL",
false,
},
{
"Same-origin, no XFO, yes frame-ancestors",
"a.com",
"Content-Security-Policy: frame-ancestors *",
false,
},
{
"Cross-origin, no XFO, yes frame-ancestors",
"b.com",
"Content-Security-Policy: frame-ancestors *",
false,
}};
for (auto test : cases) {
SCOPED_TRACE(test.name);
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
std::string path = "/set-header?";
if (test.header)
path += test.header;
GURL url = https_server().GetURL(test.host, path);
LoadIFrame(url);
ExpectHistogramIncreasedBy(test.expect_counter ? 1 : 0);
}
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
NonCrossOriginIsolatedCheckSabConstructor) {
GURL url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(NavigateToURL(web_contents(), url));
EXPECT_EQ(true, content::EvalJs(web_contents(),
"'SharedArrayBuffer' in globalThis"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
NonCrossOriginIsolatedSabSizeZero) {
GURL url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_EQ(true, content::ExecJs(web_contents(), "new SharedArrayBuffer(0)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
NonCrossOriginIsolatedSab) {
GURL url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_EQ(true,
content::ExecJs(web_contents(), "new SharedArrayBuffer(8192)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginIsolatedSab) {
GURL url =
https_server().GetURL("a.com",
"/set-header"
"?Cross-Origin-Opener-Policy: same-origin"
"&Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_EQ(true,
content::ExecJs(web_contents(), "new SharedArrayBuffer(8192)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmMemorySharingCrossSite) {
GURL main_url = https_server().GetURL("a.com", "/empty.html");
GURL sub_url = https_server().GetURL("b.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
received_memory = undefined;
addEventListener("message", event => {
received_memory = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
const memory = new WebAssembly.Memory({
initial:1,
maximum:1,
shared:true
});
parent.postMessage(memory, "*");
)"));
// It doesn't exist yet a warning or an error being dispatched for failing to
// send a WebAssembly.Memory. This test simply wait.
EXPECT_EQ("Success: Nothing received", content::EvalJs(main_document, R"(
new Promise(async resolve => {
await new Promise(r => setTimeout(r, 1000));
if (received_memory)
resolve("Failure: Received Webassembly Memory");
else
resolve("Success: Nothing received");
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmMemorySharingCrossOrigin) {
GURL main_url = https_server().GetURL("a.a.com", "/empty.html");
GURL sub_url = https_server().GetURL("b.a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
addEventListener("message", event => {
received_memory = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
const memory = new WebAssembly.Memory({
initial:1,
maximum:1,
shared:true
});
parent.postMessage(memory, "*");
)"));
EXPECT_EQ(1 * kWasmPageSize, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_memory)
await new Promise(r => setTimeout(r, 10));
resolve(received_memory.buffer.byteLength);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmMemorySharingSameOrigin) {
GURL main_url = https_server().GetURL("a.com", "/empty.html");
GURL sub_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
received_memory = undefined;
addEventListener("message", event => {
received_memory = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
const memory = new WebAssembly.Memory({
initial:1,
maximum:1,
shared:true
});
parent.postMessage(memory, "*");
)"));
EXPECT_EQ(1 * kWasmPageSize, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_memory)
await new Promise(r => setTimeout(r, 10));
resolve(received_memory.buffer.byteLength);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmMemorySharingCrossOriginBeforeSetDocumentDomain) {
GURL main_url = https_server().GetURL("sub.a.com", "/empty.html");
GURL sub_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
document.domain = "a.com";
received_memory = undefined;
addEventListener("message", event => {
received_memory = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
document.domain = "a.com";
const memory = new WebAssembly.Memory({
initial:1,
maximum:1,
shared:true
});
parent.postMessage(memory, "*");
)"));
EXPECT_EQ(1 * kWasmPageSize, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_memory)
await new Promise(r => setTimeout(r, 10));
resolve(received_memory.buffer.byteLength);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmMemorySharingCrossOriginAfterSetDocumentDomain) {
GURL main_url = https_server().GetURL("sub.a.com", "/empty.html");
GURL sub_url = https_server().GetURL("sub.a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
document.domain = "a.com";
received_memory = undefined;
addEventListener("message", event => {
received_memory = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
document.domain = "sub.a.com";
const memory = new WebAssembly.Memory({
initial:1,
maximum:1,
shared:true
});
parent.postMessage(memory, "*");
)"));
EXPECT_EQ(1 * kWasmPageSize, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_memory)
await new Promise(r => setTimeout(r, 10));
resolve(received_memory.buffer.byteLength);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 1);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmMemorySharingCrossOriginIsolated) {
GURL url =
https_server().GetURL("a.com",
"/set-header"
"?Cross-Origin-Opener-Policy: same-origin"
"&Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
LoadIFrame(url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
addEventListener("message", event => {
received_memory = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
const memory = new WebAssembly.Memory({
initial:1,
maximum:1,
shared:true
});
parent.postMessage(memory, "*");
)"));
EXPECT_EQ(1 * kWasmPageSize, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_memory)
await new Promise(r => setTimeout(r, 10));
resolve(received_memory.buffer.byteLength);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmModuleSharingCrossSite) {
GURL main_url = https_server().GetURL("a.com", "/empty.html");
GURL sub_url = https_server().GetURL("b.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
received_module = undefined;
addEventListener("message", event => {
received_module = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
let module = new WebAssembly.Module(new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
parent.postMessage(module, "*");
)"));
// It doesn't exist yet a warning or an error being dispatched for failing to
// send a WebAssembly.Module. This test simply wait.
EXPECT_EQ("Success: Nothing received", content::EvalJs(main_document, R"(
new Promise(async resolve => {
await new Promise(r => setTimeout(r, 1000));
if (received_module)
resolve("Failure: Received Webassembly module");
else
resolve("Success: Nothing received");
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
// TODO(ahaas): Check the histogram for:
// - kWasmModuleSharing
// - kCrossOriginWasmModuleSharing
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmModuleSharingSameSite) {
GURL main_url = https_server().GetURL("a.a.com", "/empty.html");
GURL sub_url = https_server().GetURL("b.a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
received_module = undefined;
addEventListener("message", event => {
received_module = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
let module = new WebAssembly.Module(new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
parent.postMessage(module, "*");
)"));
// It doesn't exist yet a warning or an error being dispatched for failing to
// send a WebAssembly.Module. This test simply wait.
EXPECT_EQ("Success: Nothing received", content::EvalJs(main_document, R"(
new Promise(async resolve => {
await new Promise(r => setTimeout(r, 1000));
if (received_module)
resolve("Failure: Received Webassembly module");
else
resolve("Success: Nothing received");
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmModuleSharingSameOrigin) {
GURL main_url = https_server().GetURL("a.com", "/empty.html");
GURL sub_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
received_module = undefined;
addEventListener("message", event => {
received_module = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
let module = new WebAssembly.Module(new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
parent.postMessage(module, "*");
)"));
EXPECT_EQ(true, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_module)
await new Promise(r => setTimeout(r, 10));
resolve(true);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
// TODO(ahaas): Check the histogram for:
// - kWasmModuleSharing
// - kCrossOriginWasmModuleSharing
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmModuleSharingSameSiteBeforeSetDocumentDomain) {
GURL main_url = https_server().GetURL("sub.a.com", "/empty.html");
GURL sub_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
document.domain = "a.com";
received_module = undefined;
addEventListener("message", event => {
received_module = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
document.domain = "a.com";
let module = new WebAssembly.Module(new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
parent.postMessage(module, "*");
)"));
// It doesn't exist yet a warning or an error being dispatched for failing to
// send a WebAssembly.Module. This test simply wait.
EXPECT_EQ("Success: Nothing received", content::EvalJs(main_document, R"(
new Promise(async resolve => {
await new Promise(r => setTimeout(r, 1000));
if (received_module)
resolve("Failure: Received Webassembly module");
else
resolve("Success: Nothing received");
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WasmModuleSharingSameSiteAfterSetDocumentDomain) {
GURL main_url = https_server().GetURL("sub.a.com", "/empty.html");
GURL sub_url = https_server().GetURL("sub.a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
LoadIFrame(sub_url);
content::RenderFrameHost* main_document =
web_contents()->GetPrimaryMainFrame();
content::RenderFrameHost* sub_document = ChildFrameAt(main_document, 0);
EXPECT_EQ(true, content::ExecJs(main_document, R"(
document.domain = "a.com";
received_module = undefined;
addEventListener("message", event => {
received_module = event.data;
});
)"));
EXPECT_EQ(true, content::ExecJs(sub_document, R"(
document.domain = "sub.a.com";
let module = new WebAssembly.Module(new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
parent.postMessage(module, "*");
)"));
EXPECT_EQ(true, content::EvalJs(main_document, R"(
new Promise(async resolve => {
while (!received_module)
await new Promise(r => setTimeout(r, 10));
resolve(true);
});
)"));
CheckCounter(WebFeature::kV8SharedArrayBufferConstructedWithoutIsolation, 0);
CheckCounter(WebFeature::kV8SharedArrayBufferConstructed, 0);
// TODO(ahaas): Check the histogram for:
// - kWasmModuleSharing
// - kCrossOriginWasmModuleSharing
}
// Check that two pages with same-origin documents do not get reported when the
// COOP status is the same.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
SameOriginDocumentsWithSameCOOPStatus) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
OpenPopup(main_document_url);
ExpectHistogramIncreasedBy(0);
}
// Check that two pages with same-origin documents do get reported when the
// COOP status is not the same and they are in the same browsing context group.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
SameOriginDocumentsWithDifferentCOOPStatus) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL no_coop_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
OpenPopup(no_coop_url);
ExpectHistogramIncreasedBy(1);
}
// Check that two pages with same-origin documents do not get reported when the
// COOP status is not the same but they are in different browsing context
// groups.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
SameOriginDocumentsWithDifferentCOOPStatusBCGSwitch) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL coop_same_origin_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
OpenPopup(coop_same_origin_url);
ExpectHistogramIncreasedBy(0);
}
// Check that two pages with two different COOP status are not reported when
// their documents are cross-origin.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossOriginDocumentsWithNoCOOPStatus) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL no_coop_url = https_server().GetURL("b.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
OpenPopup(no_coop_url);
ExpectHistogramIncreasedBy(0);
}
// Check that a COOP same-origin-allow-popups page with a cross-origin iframe
// that opens a popup to the same origin document gets reported.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
COOPSameOriginAllowPopupsIframeAndPopup) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL no_coop_url = https_server().GetURL("b.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(no_coop_url);
OpenPopup(no_coop_url);
ExpectHistogramIncreasedBy(1);
}
// Check that an iframe that is same-origin with its opener of a different COOP
// status gets reported.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
SameOriginIframeInCrossOriginPopupWithCOOP) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL no_coop_url = https_server().GetURL("b.com", "/empty.html");
GURL same_origin_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
content::WebContents* popup = OpenPopup(no_coop_url);
LoadIFrameInWebContents(popup, same_origin_url);
ExpectHistogramIncreasedBy(1);
}
// Check that two same-origin iframes in pages with different COOP status gets
// reported.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
IFramesWithDifferentCOOPStatus) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL popup_url = https_server().GetURL("b.com", "/empty.html");
GURL iframe_url = https_server().GetURL("c.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
LoadIFrame(iframe_url);
content::WebContents* popup = OpenPopup(popup_url);
LoadIFrameInWebContents(popup, iframe_url);
ExpectHistogramIncreasedBy(1);
}
// Check that when two pages both have frames that are same-origin with a
// document in the other page and have different COOP status, the metrics is
// only recorded once.
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
SameOriginDifferentCOOPStatusRecordedOnce) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL popup_url = https_server().GetURL("b.com", "/empty.html");
GURL same_origin_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
content::WebContents* popup = OpenPopup(popup_url);
LoadIFrame(popup_url);
LoadIFrameInWebContents(popup, same_origin_url);
ExpectHistogramIncreasedBy(1);
}
// Check that when two pages COOP same-origin-allow-popups have frames that are
// same-origin with a COOP unsafe-none, the metrcis is recorded twice (once per
// COOP same-origin-allow-popups page).
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
SameOriginDifferentCOOPStatusTwoCOOPPages) {
set_monitored_feature(
WebFeature::kSameOriginDocumentsWithDifferentCOOPStatus);
GURL main_document_url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Opener-Policy: "
"same-origin-allow-popups");
GURL same_origin_url = https_server().GetURL("a.com", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_document_url));
OpenPopup(main_document_url);
OpenPopup(same_origin_url);
ExpectHistogramIncreasedBy(2);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepNoneMainFrame) {
GURL url = https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: unsafe-none");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorpReportOnly, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentiallessReportOnly,
0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepCredentiallessMainFrame) {
GURL url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 1);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorpReportOnly, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentiallessReportOnly,
0);
CheckCounterMainFrame(WebFeature::kCrossOriginEmbedderPolicyCredentialless,
1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepRequireCorpMainFrame) {
GURL url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 1);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorpReportOnly, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentiallessReportOnly,
0);
CheckCounterMainFrame(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepReportOnlyCredentiallessMainFrame) {
GURL url = https_server().GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy-Report-Only: credentialless");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorpReportOnly, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentiallessReportOnly,
1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepReportOnlyRequireCorpMainFrame) {
GURL url = https_server().GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy-Report-Only: require-corp");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorpReportOnly, 1);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentiallessReportOnly,
0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoopAndCoepIsolatedMainFrame) {
GURL url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless&"
"Cross-Origin-Opener-Policy: same-origin");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCoopAndCoepIsolated, 1);
CheckCounter(WebFeature::kCoopAndCoepIsolatedReportOnly, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoopAndCoepIsolatedEnforcedReportOnlyMainFrame) {
GURL url = https_server().GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless&"
"Cross-Origin-Embedder-Policy-Report-Only: credentialless&"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Opener-Policy-Report-Only: same-origin");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCoopAndCoepIsolated, 1);
CheckCounter(WebFeature::kCoopAndCoepIsolatedReportOnly, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoopAndCoepIsolatedMainFrameReportOnly) {
GURL url = https_server().GetURL(
"a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless&"
"Cross-Origin-Opener-Policy-Report-Only: same-origin");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
CheckCounter(WebFeature::kCoopAndCoepIsolated, 0);
CheckCounter(WebFeature::kCoopAndCoepIsolatedReportOnly, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoopAndCoepIsolatedIframe) {
GURL main_url = https_server().GetURL("a.com", "/set-header?");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
GURL child_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless&"
"Cross-Origin-Opener-Policy: same-origin");
LoadIFrame(child_url);
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
CheckCounter(WebFeature::kCoopAndCoepIsolated, 0);
CheckCounter(WebFeature::kCoopAndCoepIsolatedReportOnly, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepRequireCorpEmbedsCredentialless) {
GURL main_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 0);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 1);
GURL child_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless");
LoadIFrame(child_url);
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 1);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 1);
CheckCounterMainFrame(WebFeature::kCrossOriginEmbedderPolicyCredentialless,
0);
CheckCounterMainFrame(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepCredentiallessEmbedsRequireCorp) {
GURL main_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless");
EXPECT_TRUE(content::NavigateToURL(web_contents(), main_url));
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 1);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 0);
GURL child_url =
https_server().GetURL("a.com",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp");
LoadIFrame(child_url);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyCredentialless, 1);
CheckCounter(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 1);
CheckCounterMainFrame(WebFeature::kCrossOriginEmbedderPolicyCredentialless,
1);
CheckCounterMainFrame(WebFeature::kCrossOriginEmbedderPolicyRequireCorp, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepNoneSharedWorker) {
GURL main_page_url = https_server().GetURL("a.test", "/empty.html");
GURL worker_url =
https_server().GetURL("a.test",
"/set-header?"
"Cross-Origin-Embedder-Policy: unsafe-none");
ASSERT_TRUE(content::NavigateToURL(web_contents(), main_page_url));
EXPECT_TRUE(content::ExecJs(
web_contents(),
content::JsReplace("worker = new SharedWorker($1)", worker_url)));
CheckCounter(WebFeature::kCoepNoneSharedWorker, 1);
CheckCounter(WebFeature::kCoepCredentiallessSharedWorker, 0);
CheckCounter(WebFeature::kCoepRequireCorpSharedWorker, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepCredentiallessSharedWorker) {
GURL main_page_url = https_server().GetURL("a.test", "/empty.html");
GURL worker_url =
https_server().GetURL("a.test",
"/set-header?"
"Cross-Origin-Embedder-Policy: credentialless");
ASSERT_TRUE(content::NavigateToURL(web_contents(), main_page_url));
EXPECT_TRUE(content::ExecJs(
web_contents(),
content::JsReplace("worker = new SharedWorker($1)", worker_url)));
CheckCounter(WebFeature::kCoepNoneSharedWorker, 0);
CheckCounter(WebFeature::kCoepCredentiallessSharedWorker, 1);
CheckCounter(WebFeature::kCoepRequireCorpSharedWorker, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CoepRequireCorpSharedWorker) {
GURL main_page_url = https_server().GetURL("a.test", "/empty.html");
GURL worker_url =
https_server().GetURL("a.test",
"/set-header?"
"Cross-Origin-Embedder-Policy: require-corp");
ASSERT_TRUE(content::NavigateToURL(web_contents(), main_page_url));
EXPECT_TRUE(content::ExecJs(
web_contents(),
content::JsReplace("worker = new SharedWorker($1)", worker_url)));
CheckCounter(WebFeature::kCoepNoneSharedWorker, 0);
CheckCounter(WebFeature::kCoepCredentiallessSharedWorker, 0);
CheckCounter(WebFeature::kCoepRequireCorpSharedWorker, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccess) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(a,b)");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
content::RenderFrameHost* same_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
content::RenderFrameHost* cross_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 1);
struct TestCase {
const char* name;
const char* property;
WebFeature property_access;
WebFeature property_access_from_other_page;
} cases[] = {
{
"blur",
"window.top.blur()",
WebFeature::kWindowProxyCrossOriginAccessBlur,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageBlur,
},
{
"closed",
"window.top.closed",
WebFeature::kWindowProxyCrossOriginAccessClosed,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClosed,
},
{
"focus",
"window.top.focus()",
WebFeature::kWindowProxyCrossOriginAccessFocus,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageFocus,
},
{
"frames",
"window.top.frames",
WebFeature::kWindowProxyCrossOriginAccessFrames,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageFrames,
},
{
"length",
"window.top.length",
WebFeature::kWindowProxyCrossOriginAccessLength,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLength,
},
{
"location get",
"window.top.location",
WebFeature::kWindowProxyCrossOriginAccessLocation,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
},
{
"opener get",
"window.top.opener",
WebFeature::kWindowProxyCrossOriginAccessOpener,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener,
},
{
"parent",
"window.top.parent",
WebFeature::kWindowProxyCrossOriginAccessParent,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageParent,
},
{
"postMessage",
"window.top.postMessage('','*')",
WebFeature::kWindowProxyCrossOriginAccessPostMessage,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPagePostMessage,
},
{
"self",
"window.top.self",
WebFeature::kWindowProxyCrossOriginAccessSelf,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageSelf,
},
{
"top",
"window.top.top",
WebFeature::kWindowProxyCrossOriginAccessTop,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageTop,
},
{
"window",
"window.top.window",
WebFeature::kWindowProxyCrossOriginAccessWindow,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageWindow,
}};
for (auto test : cases) {
SCOPED_TRACE(test.name);
// Check that a same-origin access does not register use counters.
EXPECT_TRUE(content::ExecJs(same_origin_subframe, test.property));
CheckCounter(test.property_access, 0);
CheckCounter(test.property_access_from_other_page, 0);
// Check that a cross-origin access register use counters.
EXPECT_TRUE(content::ExecJs(cross_origin_subframe, test.property));
CheckCounter(test.property_access, 1);
CheckCounter(test.property_access_from_other_page, 0);
}
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessCloseSameOrigin) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(a)");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::RenderFrameHost* same_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
EXPECT_TRUE(content::ExecJs(same_origin_subframe, "window.top.close()"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessClose, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClose, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessCloseCrossOrigin) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(b)");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a cross-origin access register use counters.
content::RenderFrameHost* cross_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
EXPECT_TRUE(content::ExecJs(cross_origin_subframe, "window.top.close()"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessClose, 1);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClose, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessIndexedGetter) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(a,b)");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::RenderFrameHost* same_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
EXPECT_TRUE(content::ExecJs(same_origin_subframe, "window.top[0]"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessIndexedGetter, 0);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageIndexedGetter, 0);
// Check that a cross-origin access register use counters.
content::RenderFrameHost* cross_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 1);
EXPECT_TRUE(content::ExecJs(cross_origin_subframe, "window.top[0]"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessIndexedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageIndexedGetter, 0);
// A failed access should not register the use counter.
EXPECT_FALSE(content::ExecJs(cross_origin_subframe, "window.top[2]"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessIndexedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageIndexedGetter, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessLocationSetSameOrigin) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(a,b)");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::RenderFrameHost* same_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
EXPECT_TRUE(
content::ExecJs(same_origin_subframe,
content::JsReplace("window.top.location = $1", url)));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessLocation, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessLocationSetCrossOrigin) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(a,b)");
GURL fragment_url = https_server().GetURL(
"a.com", "/cross_site_iframe_factory.html?a(a,b)#foo");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a cross-origin access register use counters.
content::RenderFrameHost* cross_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 1);
EXPECT_TRUE(content::ExecJs(
cross_origin_subframe,
content::JsReplace("window.top.location = $1", fragment_url)));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessLocation, 1);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessNamedGetter) {
GURL url = https_server().GetURL("a.test", "/iframe_about_blank.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
LoadIFrame(cross_origin_url);
// Check that a same-origin access does not register use counters.
content::RenderFrameHost* same_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
EXPECT_TRUE(content::ExecJs(same_origin_subframe,
"window.top['about_blank_iframe']"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessNamedGetter, 0);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageNamedGetter, 0);
// Check that a cross-origin access register use counters.
content::RenderFrameHost* cross_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 1);
EXPECT_TRUE(content::ExecJs(cross_origin_subframe,
"window.top['about_blank_iframe']"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessNamedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageNamedGetter, 0);
// A failed access should not register the use counter.
EXPECT_FALSE(
content::ExecJs(cross_origin_subframe, "window.top['wrongName']"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessNamedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageNamedGetter, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessOpenerSet) {
GURL url =
https_server().GetURL("a.com", "/cross_site_iframe_factory.html?a(a,b)");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::RenderFrameHost* same_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);
EXPECT_TRUE(content::ExecJs(same_origin_subframe, "window.top.opener = ''"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessOpener, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener, 0);
// Check that a cross-origin access doesn't register use counters because it
// is blocked by the same-origin policy.
content::RenderFrameHost* cross_origin_subframe =
ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 1);
EXPECT_FALSE(
content::ExecJs(cross_origin_subframe, "window.top.opener = ''"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessOpener, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPage) {
GURL url = https_server().GetURL("a.com", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
content::WebContents* same_origin_popup = OpenPopup(url);
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
struct TestCase {
const char* name;
const char* property;
WebFeature property_access;
WebFeature property_access_from_other_page;
} cases[] = {
{
"blur",
"window.opener.blur()",
WebFeature::kWindowProxyCrossOriginAccessBlur,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageBlur,
},
{
"closed",
"window.opener.closed",
WebFeature::kWindowProxyCrossOriginAccessClosed,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClosed,
},
{
"focus",
"window.opener.focus()",
WebFeature::kWindowProxyCrossOriginAccessFocus,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageFocus,
},
{
"frames",
"window.opener.frames",
WebFeature::kWindowProxyCrossOriginAccessFrames,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageFrames,
},
{
"length",
"window.opener.length",
WebFeature::kWindowProxyCrossOriginAccessLength,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLength,
},
{
"location get",
"window.opener.location",
WebFeature::kWindowProxyCrossOriginAccessLocation,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
},
{
"opener get",
"window.opener.opener",
WebFeature::kWindowProxyCrossOriginAccessOpener,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener,
},
{
"parent",
"window.opener.parent",
WebFeature::kWindowProxyCrossOriginAccessParent,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageParent,
},
{
"postMessage",
"window.opener.postMessage('','*')",
WebFeature::kWindowProxyCrossOriginAccessPostMessage,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPagePostMessage,
},
{
"self",
"window.opener.self",
WebFeature::kWindowProxyCrossOriginAccessSelf,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageSelf,
},
{
"top",
"window.opener.top",
WebFeature::kWindowProxyCrossOriginAccessTop,
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageTop,
}};
for (auto test : cases) {
SCOPED_TRACE(test.name);
// Check that a same-origin access does not register use counters.
EXPECT_TRUE(content::ExecJs(same_origin_popup, test.property));
CheckCounter(test.property_access, 0);
CheckCounter(test.property_access_from_other_page, 0);
// Check that a cross-origin access register use counters.
EXPECT_TRUE(content::ExecJs(cross_origin_popup, test.property));
CheckCounter(test.property_access, 1);
CheckCounter(test.property_access_from_other_page, 1);
}
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageCloseSameOrigin) {
GURL url = https_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::WebContents* same_origin_popup = OpenPopup(url);
EXPECT_TRUE(content::ExecJs(same_origin_popup, "window.opener.close()"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessClose, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClose, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageCloseCrossOrigin) {
GURL url = https_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a cross-origin access register use counters.
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
EXPECT_TRUE(content::ExecJs(cross_origin_popup, "window.opener.close()"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessClose, 1);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClose, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageIndexedGetter) {
GURL url = https_server().GetURL("a.test", "/iframe.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::WebContents* same_origin_popup = OpenPopup(url);
EXPECT_TRUE(content::ExecJs(same_origin_popup, "window.opener[0]"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessIndexedGetter, 0);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageIndexedGetter, 0);
// Check that a cross-origin access register use counters.
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
EXPECT_TRUE(content::ExecJs(cross_origin_popup, "window.opener[0]"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessIndexedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageIndexedGetter, 1);
// A failed access should not register the use counter.
EXPECT_FALSE(content::ExecJs(cross_origin_popup, "window.opener[1]"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessIndexedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageIndexedGetter, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageLocationSetSameOrigin) {
GURL url = https_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::WebContents* same_origin_popup = OpenPopup(url);
EXPECT_TRUE(
content::ExecJs(same_origin_popup,
content::JsReplace("window.opener.location = $1", url)));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessLocation, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageLocationSetCrossOrigin) {
GURL url = https_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a cross-origin access register use counters.
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
EXPECT_TRUE(
content::ExecJs(cross_origin_popup,
content::JsReplace("window.opener.location = $1", url)));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessLocation, 1);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageNamedGetter) {
GURL url = https_server().GetURL("a.test", "/iframe_about_blank.html");
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::WebContents* same_origin_popup = OpenPopup(url);
EXPECT_TRUE(content::ExecJs(same_origin_popup,
"window.opener['about_blank_iframe']"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessNamedGetter, 0);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageNamedGetter, 0);
// Check that a cross-origin access register use counters.
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
EXPECT_TRUE(content::ExecJs(cross_origin_popup,
"window.opener['about_blank_iframe']"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessNamedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageNamedGetter, 1);
// A failed access should not register the use counter.
EXPECT_FALSE(
content::ExecJs(cross_origin_popup, "window.opener['wrongName']"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessNamedGetter, 1);
CheckCounter(
WebFeature::kWindowProxyCrossOriginAccessFromOtherPageNamedGetter, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageOpenerSet) {
GURL url = https_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::WebContents* same_origin_popup = OpenPopup(url);
EXPECT_TRUE(content::ExecJs(same_origin_popup, "window.opener.opener = ''"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessOpener, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener, 0);
// Check that a cross-origin access doesn't register use counters because it
// is blocked by the same-origin policy.
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
EXPECT_FALSE(
content::ExecJs(cross_origin_popup, "window.opener.opener = ''"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessOpener, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
WindowProxyAccessFromOtherPageWindow) {
GURL url = https_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
// Check that a same-origin access does not register use counters.
content::WebContents* same_origin_popup = OpenPopup(url);
EXPECT_TRUE(content::ExecJs(same_origin_popup, "window.opener.window"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessWindow, 0);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageWindow, 0);
// Check that a cross-origin access register use counters.
GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
content::WebContents* cross_origin_popup = OpenPopup(cross_origin_url);
EXPECT_TRUE(content::ExecJs(cross_origin_popup, "window.opener.window"));
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessWindow, 1);
CheckCounter(WebFeature::kWindowProxyCrossOriginAccessFromOtherPageWindow, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
AnonymousIframeInitialEmptyDocumentControl) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
const iframe = document.createElement("iframe");
iframe.credentialless = false;
document.body.appendChild(iframe);
)"));
CheckCounter(WebFeature::kAnonymousIframe, 0);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", false, 0);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", true, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
AnonymousIframeInitialEmptyDocument) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
const iframe = document.createElement("iframe");
iframe.credentialless = true;
document.body.appendChild(iframe);
)"));
CheckCounter(WebFeature::kAnonymousIframe, 1);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", false, 1);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", true, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
AnonymousIframeNavigationControl) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = location.href;
iframe.credentialless = false;
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kAnonymousIframe, 0);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", false, 0);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", true, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
AnonymousIframeNavigation) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = location.href;
iframe.credentialless = true;
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kAnonymousIframe, 1);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", false, 1);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", true, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
AnonymousIframeIsSandboxedControl) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = location.href;
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kAnonymousIframe, 0);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", false, 0);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", true, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
AnonymousIframeIsSandboxed) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
const createIframe = sandbox => {
let iframe = document.createElement("iframe");
iframe.src = location.href;
iframe.credentialless = true;
if (sandbox)
iframe.sandbox = "";
document.body.appendChild(iframe);
return new Promise(resolve => iframe.onload = resolve);
};
Promise.all([
createIframe(false),
createIframe(false),
createIframe(false),
createIframe(true),
createIframe(true),
]);
)"));
CheckCounter(WebFeature::kAnonymousIframe, 1);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", false, 3);
CheckHistogramCount("Navigation.AnonymousIframeIsSandboxed", true, 2);
}
using SameDocumentCrossOriginInitiatorTest =
ChromeWebPlatformSecurityMetricsBrowserTest;
IN_PROC_BROWSER_TEST_F(SameDocumentCrossOriginInitiatorTest, SameOrigin) {
const GURL parent_url = https_server().GetURL("a.test", "/empty.html");
const GURL child_url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), parent_url));
LoadIFrame(child_url);
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
EXPECT_TRUE(content::ExecJs(
web_contents(), "document.querySelector('iframe').src += '#foo';"));
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
}
IN_PROC_BROWSER_TEST_F(SameDocumentCrossOriginInitiatorTest, SameSite) {
const GURL parent_url = https_server().GetURL("a.a.test", "/empty.html");
const GURL child_url = https_server().GetURL("b.a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), parent_url));
LoadIFrame(child_url);
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
EXPECT_TRUE(content::ExecJs(
web_contents(), "document.querySelector('iframe').src += '#foo';"));
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
// TODO(crbug.com/40062719) It seems the initiator origin is wrong,
// e.g. `child_url` instead of `parent_url`, causing the metrics not to be
// recorded.
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
}
IN_PROC_BROWSER_TEST_F(SameDocumentCrossOriginInitiatorTest, CrossOrigin) {
const GURL parent_url = https_server().GetURL("a.test", "/empty.html");
const GURL child_url = https_server().GetURL("b.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), parent_url));
LoadIFrame(child_url);
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
EXPECT_TRUE(content::ExecJs(
web_contents(), "document.querySelector('iframe').src += '#foo';"));
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 1);
}
IN_PROC_BROWSER_TEST_F(SameDocumentCrossOriginInitiatorTest,
SameOriginInitiated) {
const GURL parent_url = https_server().GetURL("a.test", "/empty.html");
const GURL child_url = https_server().GetURL("b.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), parent_url));
LoadIFrame(child_url);
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
EXPECT_TRUE(
content::ExecJs(GetChild(*(web_contents()->GetPrimaryMainFrame())),
"location.href += '#foo';"));
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
CheckCounter(WebFeature::kSameDocumentCrossOriginInitiator, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
JavascriptUrlNavigationInIFrame) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = 'javascript:1';
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kExecutedEmptyJavaScriptURLFromFrame, 0);
CheckCounter(WebFeature::kExecutedJavaScriptURLFromFrame, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
EmptyStringJavascriptUrlNavigationInIFrame) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = 'javascript:""';
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kExecutedEmptyJavaScriptURLFromFrame, 1);
CheckCounter(WebFeature::kExecutedJavaScriptURLFromFrame, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
JavascriptUrlNavigationInTopFrame) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
location.href = 'javascript:""';
)"));
CheckCounter(WebFeature::kExecutedEmptyJavaScriptURLFromFrame, 0);
CheckCounter(WebFeature::kExecutedJavaScriptURLFromFrame, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
DanglingMarkupInIframeName) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = '/empty.html';
iframe.name = "<\n>";
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kDanglingMarkupInWindowName, 1);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT,
0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTarget, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
DanglingMarkupInNameWithGreaterThan) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = '/empty.html';
iframe.name = "<\n";
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kDanglingMarkupInWindowName, 1);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT,
0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 1);
CheckCounter(WebFeature::kDanglingMarkupInTarget, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
DanglingMarkupInNameWithNewLineOrGreaterThan) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
new Promise(resolve => {
let iframe = document.createElement("iframe");
iframe.src = '/empty.html';
iframe.name = "<\ntest";
iframe.onload = resolve;
document.body.appendChild(iframe);
});
)"));
CheckCounter(WebFeature::kDanglingMarkupInWindowName, 1);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT,
1);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 1);
CheckCounter(WebFeature::kDanglingMarkupInTarget, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
DanglingMarkupInTarget) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
let link = document.createElement("a");
link.href = '/empty.html';
link.target = "<\n>";
document.body.appendChild(link);
link.click();
)"));
CheckCounter(WebFeature::kDanglingMarkupInWindowName, 0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT,
0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTarget, 1);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0);
}
// TODO(crbug.com/40283243): Fix and reenable the test for Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_DanglingMarkupInTargetWithNewLineOrGreaterThan \
DISABLED_DanglingMarkupInTargetWithNewLineOrGreaterThan
#else
#define MAYBE_DanglingMarkupInTargetWithNewLineOrGreaterThan \
DanglingMarkupInTargetWithNewLineOrGreaterThan
#endif
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
MAYBE_DanglingMarkupInTargetWithNewLineOrGreaterThan) {
GURL url = https_server().GetURL("a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
document.write("<a>test</a>");
let link = document.querySelector("a");
link.href = '/empty.html';
link.target = "<\n";
link.click();
)"));
CheckCounter(WebFeature::kDanglingMarkupInWindowName, 0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT,
0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTarget, 1);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 1);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
document.write("<base><a>test</a>");
let base = document.querySelector("base");
base.target = "<\ntest";
let link = document.querySelector("a");
link.href = '/empty.html';
link.click();
)"));
CheckCounter(WebFeature::kDanglingMarkupInWindowName, 0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT,
0);
CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0);
CheckCounter(WebFeature::kDanglingMarkupInTarget, 2);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 2);
CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
DocumentOpenAliasedOriginDocumentDomain) {
GURL url = https_server().GetURL("sub.a.test", "/empty.html");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
const iframe = document.createElement("iframe");
iframe.src = location.href;
iframe.onload = () => {
iframe.contentDocument.write("<div></div>");
document.domain = "a.test";
};
document.body.appendChild(iframe);
)"));
CheckCounter(WebFeature::kDocumentOpenAliasedOriginDocumentDomain, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToHTMLDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/hello.html"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
// Plain HTML should not count as a browser-generated document.
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToXHTMLDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/security/minimal.xhtml"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
// XHTML should not count as a browser-generated document, even though it is
// technically XML.
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToSVGDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/circle.svg"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
// SVG should not count as a browser-generated document, even though it is
// technically XML.
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToImageDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/image.jpg"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToMediaDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/media/bear.mp4"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToTextDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/site_isolation/valid.json"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 1);
}
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CrossWindowAccessToXMLDocument) {
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/site_isolation/valid.xml"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 1);
}
#if BUILDFLAG(ENABLE_PDF)
class ChromeWebPlatformSecurityMetricsBrowserPdfTest
: public base::test::WithFeatureOverride,
public ChromeWebPlatformSecurityMetricsBrowserTest {
public:
ChromeWebPlatformSecurityMetricsBrowserPdfTest()
: base::test::WithFeatureOverride(chrome_pdf::features::kPdfOopif),
ChromeWebPlatformSecurityMetricsBrowserTest() {}
bool UseOopif() const { return GetParam(); }
std::vector<base::test::FeatureRef> GetEnabledFeatures() const override {
std::vector<base::test::FeatureRef> enabled =
ChromeWebPlatformSecurityMetricsBrowserTest::GetEnabledFeatures();
if (UseOopif()) {
enabled.push_back(chrome_pdf::features::kPdfOopif);
}
return enabled;
}
std::vector<base::test::FeatureRef> GetDisabledFeatures() const override {
std::vector<base::test::FeatureRef> disabled =
ChromeWebPlatformSecurityMetricsBrowserTest::GetDisabledFeatures();
if (!UseOopif()) {
disabled.push_back(chrome_pdf::features::kPdfOopif);
}
return disabled;
}
};
IN_PROC_BROWSER_TEST_P(ChromeWebPlatformSecurityMetricsBrowserPdfTest,
CrossWindowAccessToPluginDocument) {
const char kAccessInnerFrameDocumentScript[] = R"(
(() => {
try {
window.frames[0].frames[0].contentDocument;
} catch (e) {
return e.name;
}
return "success";
})()
)";
EXPECT_TRUE(content::NavigateToURL(web_contents(),
https_server().GetURL("/empty.html")));
LoadIFrame(https_server().GetURL("/site_isolation/fake.pdf"));
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
// This should throw a `SecurityError` according to the spec, but does not due
// to https://crbug.com/1257611.
EXPECT_TRUE(content::ExecJs(web_contents(), R"(
window.frames[0].contentDocument;
)"));
// We would like to count such accesses for the purposes of estimating the
// impact of fixing https://crbug.com/1257611, but it does not seem to be as
// easy as for other document classes. The enclosing document does not seem to
// count as a "plugin document".
CheckCounter(WebFeature::kCrossWindowAccessToBrowserGeneratedDocument, 0);
// For OOPIF PDF viewer, accessing the inner frame throws a `TypeError` due to
// shadow DOM. For GuestView PDF viewer, accessing the inner frame throws a
// `SecurityError`.
const std::string expected = UseOopif() ? "TypeError" : "SecurityError";
content::EvalJsResult actual =
content::EvalJs(web_contents(), kAccessInnerFrameDocumentScript);
EXPECT_EQ(expected, actual);
}
// TODO(crbug.com/1445746): Stop testing both modes after OOPIF PDF viewer
// launches.
INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
ChromeWebPlatformSecurityMetricsBrowserPdfTest);
#endif
IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest,
CSPEESameOriginWithSameCSPHeader) {
GURL url = http_server().GetURL("a.test",
"/set-header?"
"Content-Security-Policy: img-src 'none'");
EXPECT_TRUE(content::NavigateToURL(web_contents(), url));
EXPECT_TRUE(content::ExecJs(web_contents(), content::JsReplace(R"(
const iframe = document.createElement("iframe");
iframe.csp = "img-src 'none'";
iframe.src = $1;
document.body.appendChild(iframe);
)",
url)));
CheckCounter(WebFeature::kCSPEESameOriginBlanketEnforcement, 0);
}
// TODO(arthursonzogni): Add basic test(s) for the WebFeatures:
// [ ] CrossOriginOpenerPolicySameOrigin
// [ ] CrossOriginOpenerPolicySameOriginAllowPopups
// [X] CoopAndCoepIsolated
//
// Added by:
// https://chromium-review.googlesource.com/c/chromium/src/+/2122140
} // namespace