1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/command_line.h"
8 #include "base/feature_list.h"
9 #include "base/functional/bind.h"
10 #include "base/functional/callback_helpers.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/unguessable_token.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/extensions/extension_browsertest.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_commands.h"
23 #include "chrome/browser/ui/singleton_tabs.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/test/base/ui_test_utils.h"
26 #include "content/public/browser/blob_handle.h"
27 #include "content/public/browser/render_frame_host.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/browser/site_isolation_policy.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/url_constants.h"
33 #include "content/public/test/browser_test.h"
34 #include "content/public/test/browser_test_utils.h"
35 #include "extensions/common/extension_urls.h"
36 #include "mojo/public/cpp/bindings/pending_remote.h"
37 #include "net/base/features.h"
38 #include "net/dns/mock_host_resolver.h"
39 #include "net/test/embedded_test_server/embedded_test_server.h"
40 #include "storage/browser/blob/blob_registry_impl.h"
41 #include "third_party/blink/public/common/blob/blob_utils.h"
42 #include "third_party/blink/public/common/features.h"
43 #include "third_party/blink/public/common/storage_key/storage_key.h"
44 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
46 #include "url/origin.h"
48 // The goal of these tests is to "simulate" exploited renderer processes, which
49 // can send arbitrary IPC messages and confuse browser process internal state,
50 // leading to security bugs. We are trying to verify that the browser doesn't
51 // perform any dangerous operations in such cases.
52 // This is similar to the security_exploit_browsertest.cc tests, but also
53 // includes chrome/ layer concepts such as extensions.
54 class ChromeSecurityExploitBrowserTest
55 : public extensions::ExtensionBrowserTest {
57 ChromeSecurityExploitBrowserTest() {}
59 ChromeSecurityExploitBrowserTest(const ChromeSecurityExploitBrowserTest&) =
61 ChromeSecurityExploitBrowserTest& operator=(
62 const ChromeSecurityExploitBrowserTest&) = delete;
64 ~ChromeSecurityExploitBrowserTest() override {}
66 void SetUpOnMainThread() override {
67 extensions::ExtensionBrowserTest::SetUpOnMainThread();
69 ASSERT_TRUE(embedded_test_server()->Start());
70 host_resolver()->AddRule("*", "127.0.0.1");
72 extension_ = LoadExtension(test_data_dir_.AppendASCII("simple_with_icon"));
75 const extensions::Extension* extension() { return extension_; }
77 std::unique_ptr<content::BlobHandle> CreateMemoryBackedBlob(
78 const std::string& contents,
79 const std::string& content_type) {
80 std::unique_ptr<content::BlobHandle> result;
82 profile()->CreateMemoryBackedBlob(
83 base::as_bytes(base::make_span(contents)), content_type,
85 [](std::unique_ptr<content::BlobHandle>* out_blob,
86 base::OnceClosure done,
87 std::unique_ptr<content::BlobHandle> blob) {
88 *out_blob = std::move(blob);
89 std::move(done).Run();
91 &result, loop.QuitClosure()));
98 raw_ptr<const extensions::Extension, DanglingUntriaged> extension_;
101 // Subclass of ChromeSecurityExploitBrowserTest that uses --disable-web-security
102 // to simulate an exploited renderer. Note that this also disables some browser
103 // process checks, so it's not ideal for all exploit tests.
104 class ChromeWebSecurityDisabledBrowserTest
105 : public ChromeSecurityExploitBrowserTest {
107 ChromeWebSecurityDisabledBrowserTest() {}
109 ChromeWebSecurityDisabledBrowserTest(
110 const ChromeWebSecurityDisabledBrowserTest&) = delete;
111 ChromeWebSecurityDisabledBrowserTest& operator=(
112 const ChromeWebSecurityDisabledBrowserTest&) = delete;
114 ~ChromeWebSecurityDisabledBrowserTest() override {}
116 void SetUpCommandLine(base::CommandLine* command_line) override {
117 ChromeSecurityExploitBrowserTest::SetUpCommandLine(command_line);
118 command_line->AppendSwitch(switches::kDisableWebSecurity);
122 // TODO(nasko): This test as written is incompatible with Site Isolation
123 // restrictions, which disallow the cross-origin pushState call.
124 // Find a different way to implement issuing the illegal request or just
125 // delete the test if we have coverage elsewhere. See https://crbug.com/929161.
126 IN_PROC_BROWSER_TEST_F(ChromeWebSecurityDisabledBrowserTest,
127 DISABLED_ChromeExtensionResources) {
128 // Load a page that requests a chrome-extension:// image through XHR. We
129 // expect this load to fail, as it is an illegal request.
130 GURL foo = embedded_test_server()->GetURL("foo.com",
131 "/chrome_extension_resource.html");
133 content::DOMMessageQueue msg_queue(
134 browser()->tab_strip_model()->GetActiveWebContents());
136 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), foo));
139 std::string expected_status("0");
140 EXPECT_TRUE(msg_queue.WaitForMessage(&status));
141 EXPECT_STREQ(status.c_str(), expected_status.c_str());
144 // Tests that a normal web process cannot send a commit for a Chrome Web Store
145 // URL. See https://crbug.com/172119.
146 IN_PROC_BROWSER_TEST_F(ChromeSecurityExploitBrowserTest,
147 CommitWebStoreURLInWebProcess) {
148 GURL foo = embedded_test_server()->GetURL("foo.com", "/title1.html");
150 content::WebContents* web_contents =
151 browser()->tab_strip_model()->GetActiveWebContents();
152 content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame();
154 // This IPC should result in a kill because the Chrome Web Store is not
155 // allowed to commit in |rfh->GetProcess()|.
156 base::HistogramTester histograms;
157 content::RenderProcessHostWatcher crash_observer(
159 content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
161 // Modify an IPC for a commit of a blank URL, which would otherwise be allowed
162 // to commit in any process.
163 GURL blank_url = GURL(url::kAboutBlankURL);
164 GURL webstore_url = extension_urls::GetWebstoreLaunchURL();
165 content::PwnCommitIPC(web_contents, blank_url, webstore_url,
166 url::Origin::Create(GURL(webstore_url)));
167 web_contents->GetController().LoadURL(
168 blank_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
170 // If the process is killed in CanCommitURL, this test passes.
171 crash_observer.Wait();
172 histograms.ExpectUniqueSample("Stability.BadMessageTerminated.Content", 1, 1);
175 // Tests that a non-extension process cannot send a commit of a blank URL with
176 // an extension origin.
177 IN_PROC_BROWSER_TEST_F(ChromeSecurityExploitBrowserTest,
178 CommitExtensionOriginInWebProcess) {
179 GURL foo = embedded_test_server()->GetURL("foo.com", "/title1.html");
181 content::WebContents* web_contents =
182 browser()->tab_strip_model()->GetActiveWebContents();
183 content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame();
185 // This IPC should result in a kill because |ext_origin| is not allowed to
186 // commit in |rfh->GetProcess()|.
187 base::HistogramTester histograms;
188 content::RenderProcessHostWatcher crash_observer(
190 content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
192 // Modify an IPC for a commit of a blank URL, which would otherwise be allowed
193 // to commit in any process.
194 GURL blank_url = GURL(url::kAboutBlankURL);
195 std::string ext_origin = "chrome-extension://" + extension()->id();
196 content::PwnCommitIPC(web_contents, blank_url, blank_url,
197 url::Origin::Create(GURL(ext_origin)));
198 web_contents->GetController().LoadURL(
199 blank_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
201 // If the process is killed in CanCommitOrigin, this test passes.
202 crash_observer.Wait();
203 histograms.ExpectUniqueSample("Stability.BadMessageTerminated.Content", 114,
207 // Tests that a non-extension process cannot send a commit of an extension URL.
208 IN_PROC_BROWSER_TEST_F(ChromeSecurityExploitBrowserTest,
209 CommitExtensionURLInWebProcess) {
210 GURL foo = embedded_test_server()->GetURL("foo.com", "/title1.html");
212 content::WebContents* web_contents =
213 browser()->tab_strip_model()->GetActiveWebContents();
214 content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame();
216 // This IPC should result in a kill because extension URLs are not allowed to
217 // commit in |rfh->GetProcess()|.
218 base::HistogramTester histograms;
219 content::RenderProcessHostWatcher crash_observer(
221 content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
223 // Modify an IPC for a commit of a blank URL, which would otherwise be allowed
224 // to commit in any process.
225 GURL blank_url = GURL(url::kAboutBlankURL);
226 std::string ext_origin = "chrome-extension://" + extension()->id();
227 content::PwnCommitIPC(web_contents, blank_url, GURL(ext_origin),
228 url::Origin::Create(GURL(ext_origin)));
229 web_contents->GetController().LoadURL(
230 blank_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
232 // If the process is killed in CanCommitURL, this test passes.
233 crash_observer.Wait();
234 histograms.ExpectUniqueSample("Stability.BadMessageTerminated.Content", 1, 1);
237 // Tests that a non-extension process cannot send a commit of an extension
239 IN_PROC_BROWSER_TEST_F(ChromeSecurityExploitBrowserTest,
240 CommitExtensionFilesystemURLInWebProcess) {
241 GURL foo = embedded_test_server()->GetURL("foo.com", "/title1.html");
243 content::WebContents* web_contents =
244 browser()->tab_strip_model()->GetActiveWebContents();
245 content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame();
247 // This IPC should result in a kill because extension filesystem URLs are not
248 // allowed to commit in |rfh->GetProcess()|.
249 base::HistogramTester histograms;
250 content::RenderProcessHostWatcher crash_observer(
252 content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
254 // Modify an IPC for a commit of a blank URL, which would otherwise be allowed
255 // to commit in any process.
256 GURL blank_url = GURL(url::kAboutBlankURL);
257 std::string ext_origin = "chrome-extension://" + extension()->id();
258 content::PwnCommitIPC(web_contents, blank_url,
259 GURL("filesystem:" + ext_origin + "/foo"),
260 url::Origin::Create(GURL(ext_origin)));
261 web_contents->GetController().LoadURL(
262 blank_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
264 // If the process is killed in CanCommitURL, this test passes.
265 crash_observer.Wait();
266 histograms.ExpectUniqueSample("Stability.BadMessageTerminated.Content", 1, 1);
269 // chrome://xyz should not be able to create a "filesystem:chrome://abc"
271 IN_PROC_BROWSER_TEST_F(ChromeSecurityExploitBrowserTest,
272 CreateFilesystemURLInOtherChromeUIOrigin) {
274 ui_test_utils::NavigateToURL(browser(), GURL("chrome://version")));
276 content::RenderFrameHost* rfh = browser()
278 ->GetActiveWebContents()
279 ->GetPrimaryMainFrame();
281 // Block the renderer on operation that never completes, to shield it from
282 // receiving unexpected browser->renderer IPCs that might CHECK.
283 rfh->ExecuteJavaScriptWithUserGestureForTests(
284 u"var r = new XMLHttpRequest();"
285 u"r.open('GET', '/slow?99999', false);"
288 base::NullCallback());
290 std::string payload = "<p>Hello world!</p>";
291 std::string payload_type = "text/html";
293 // Target an extension.
294 std::string target_origin = "chrome://downloads";
296 // Set up a blob ID and populate it with the attacker-controlled payload. This
297 // is just using the blob APIs directly since creating arbitrary blobs is not
298 // what is prohibited; this data is not in any origin.
299 std::unique_ptr<content::BlobHandle> blob =
300 CreateMemoryBackedBlob(payload, payload_type);
301 std::string blob_id = blob->GetUUID();
303 // Note: a well-behaved renderer would always send the following message here,
304 // but it's actually not necessary for the original attack to succeed, so we
305 // omit it. As a result there are some log warnings from the quota observer.
307 // IPC::IpcSecurityTestUtil::PwnMessageReceived(
308 // rfh->GetProcess()->GetChannel(),
309 // FileSystemHostMsg_OpenFileSystem(22, GURL(target_origin),
310 // storage::kFileSystemTypeTemporary));
313 GURL("filesystem:" + target_origin + "/temporary/exploit.html");
315 content::PwnMessageHelper::FileSystemCreate(
316 rfh->GetProcess(), 23, target_url, false, false, false,
317 blink::StorageKey::CreateFirstParty(url::Origin::Create(target_url)));
319 // Write the blob into the file. If successful, this places an
320 // attacker-controlled value in a resource on the extension origin.
321 content::PwnMessageHelper::FileSystemWrite(
322 rfh->GetProcess(), 24, target_url, blob_id, 0,
323 blink::StorageKey::CreateFirstParty(url::Origin::Create(target_url)));
325 // Now navigate to |target_url| in a new tab. It should not contain |payload|.
326 ASSERT_FALSE(AddTabAtIndex(0, target_url, ui::PAGE_TRANSITION_TYPED));
327 EXPECT_FALSE(content::WaitForLoadStop(
328 browser()->tab_strip_model()->GetWebContentsAt(0)));
331 ->GetActiveWebContents()
332 ->GetPrimaryMainFrame();
334 // If the attack is unsuccessful, the navigation ends up in an error
336 if (content::SiteIsolationPolicy::IsErrorPageIsolationEnabled(
337 !rfh->GetParent())) {
338 EXPECT_EQ(GURL(content::kUnreachableWebDataURL),
339 rfh->GetSiteInstance()->GetSiteURL());
341 EXPECT_EQ(GURL(target_origin), rfh->GetSiteInstance()->GetSiteURL());
343 std::string script = R"(
344 var textContent = document.body.innerText.replace(/\n+/g, '\n');
348 std::string body = content::EvalJs(rfh, script).ExtractString();
350 "Your file couldn’t be accessed\n"
351 "It may have been moved, edited, or deleted.\n"
352 "ERR_FILE_NOT_FOUND",
356 // Extension isolation prevents a normal renderer process from being able to
357 // create a "filesystem:chrome-extension://sdgkjaghsdg/temporary/" resource.
358 IN_PROC_BROWSER_TEST_F(ChromeSecurityExploitBrowserTest,
359 CreateFilesystemURLInExtensionOrigin) {
361 embedded_test_server()->GetURL("a.root-servers.net", "/title1.html");
362 ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url));
364 content::RenderFrameHost* rfh = browser()
366 ->GetActiveWebContents()
367 ->GetPrimaryMainFrame();
369 // Block the renderer on operation that never completes, to shield it from
370 // receiving unexpected browser->renderer IPCs that might CHECK.
371 rfh->ExecuteJavaScriptWithUserGestureForTests(
372 u"var r = new XMLHttpRequest();"
373 u"r.open('GET', '/slow?99999', false);"
376 base::NullCallback());
378 // JS code that the attacker would like to run in an extension process.
379 std::string payload = "<html><body>pwned.</body></html>";
380 std::string payload_type = "text/html";
382 // Target an extension.
383 std::string target_origin = "chrome-extension://" + extension()->id();
385 // Set up a blob ID and populate it with the attacker-controlled payload. This
386 // is just using the blob APIs directly since creating arbitrary blobs is not
387 // what is prohibited; this data is not in any origin.
388 std::unique_ptr<content::BlobHandle> blob =
389 CreateMemoryBackedBlob(payload, payload_type);
390 std::string blob_id = blob->GetUUID();
392 // Note: a well-behaved renderer would always call Open first before calling
393 // Create and Write, but it's actually not necessary for the original attack
394 // to succeed, so we omit it. As a result there are some log warnings from the
398 GURL("filesystem:" + target_origin + "/temporary/exploit.html");
400 content::PwnMessageHelper::FileSystemCreate(
401 rfh->GetProcess(), 23, target_url, false, false, false,
402 blink::StorageKey::CreateFirstParty(url::Origin::Create(target_url)));
404 // Write the blob into the file. If successful, this places an
405 // attacker-controlled value in a resource on the extension origin.
406 content::PwnMessageHelper::FileSystemWrite(
407 rfh->GetProcess(), 24, target_url, blob_id, 0,
408 blink::StorageKey::CreateFirstParty(url::Origin::Create(target_url)));
410 // Now navigate to |target_url| in a new tab. It should not contain |payload|.
411 ASSERT_FALSE(AddTabAtIndex(0, target_url, ui::PAGE_TRANSITION_TYPED));
412 EXPECT_FALSE(content::WaitForLoadStop(
413 browser()->tab_strip_model()->GetWebContentsAt(0)));
416 ->GetActiveWebContents()
417 ->GetPrimaryMainFrame();
419 // If the attack is unsuccessful, the navigation ends up in an error
421 if (content::SiteIsolationPolicy::IsErrorPageIsolationEnabled(
422 !rfh->GetParent())) {
423 EXPECT_EQ(GURL(content::kUnreachableWebDataURL),
424 rfh->GetSiteInstance()->GetSiteURL());
426 EXPECT_EQ(GURL(target_origin), rfh->GetSiteInstance()->GetSiteURL());
429 std::string script = R"(
430 var textContent = document.body.innerText.replace(/\n+/g, '\n');
434 body = content::EvalJs(rfh, script).ExtractString();
436 "Your file couldn’t be accessed\n"
437 "It may have been moved, edited, or deleted.\n"
438 "ERR_FILE_NOT_FOUND",
442 enum class ChromeSecurityExploitBrowserTestMojoBlobURLsTestCase {
443 kSupportPartitionedBlobUrlDisabled,
444 kSupportPartitionedBlobUrlEnabled,
447 class ChromeSecurityExploitBrowserTestMojoBlobURLsP
448 : public ChromeSecurityExploitBrowserTest,
449 public testing::WithParamInterface<
450 ChromeSecurityExploitBrowserTestMojoBlobURLsTestCase> {
452 ChromeSecurityExploitBrowserTestMojoBlobURLsP() = default;
454 void SetUp() override {
455 test_case_ = GetParam();
456 scoped_feature_list_.InitWithFeatureState(
457 net::features::kSupportPartitionedBlobUrl,
458 test_case_ == ChromeSecurityExploitBrowserTestMojoBlobURLsTestCase::
459 kSupportPartitionedBlobUrlEnabled);
461 ChromeSecurityExploitBrowserTest::SetUp();
464 void TearDown() override {
465 if (base::FeatureList::IsEnabled(
466 net::features::kSupportPartitionedBlobUrl)) {
467 storage::BlobUrlRegistry::SetURLStoreCreationHookForTesting(nullptr);
469 storage::BlobRegistryImpl::SetURLStoreCreationHookForTesting(nullptr);
474 ChromeSecurityExploitBrowserTestMojoBlobURLsTestCase test_case_;
475 base::test::ScopedFeatureList scoped_feature_list_;
478 // Extension isolation prevents a normal renderer process from being able to
479 // create a "blob:chrome-extension://" resource.
480 IN_PROC_BROWSER_TEST_P(ChromeSecurityExploitBrowserTestMojoBlobURLsP,
481 CreateBlobInExtensionOrigin) {
482 // Target an extension.
483 std::string target_origin = "chrome-extension://" + extension()->id();
484 std::string blob_path = "5881f76e-10d2-410d-8c61-ef210502acfd";
486 base::RepeatingCallback<void(storage::BlobUrlRegistry*, mojo::ReceiverId)>
487 blob_url_registry_intercept_hook;
488 base::RepeatingCallback<void(
489 mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::BlobURLStore>)>
490 blob_registry_impl_intercept_hook;
492 if (base::FeatureList::IsEnabled(net::features::kSupportPartitionedBlobUrl)) {
493 blob_url_registry_intercept_hook =
494 base::BindRepeating(&content::BlobURLStoreInterceptor::Intercept,
495 GURL("blob:" + target_origin + "/" + blob_path));
496 storage::BlobUrlRegistry::SetURLStoreCreationHookForTesting(
497 &blob_url_registry_intercept_hook);
499 blob_registry_impl_intercept_hook = base::BindRepeating(
500 &content::BlobURLStoreInterceptor::InterceptDeprecated,
501 GURL("blob:" + target_origin + "/" + blob_path));
502 storage::BlobRegistryImpl::SetURLStoreCreationHookForTesting(
503 &blob_registry_impl_intercept_hook);
506 ASSERT_TRUE(ui_test_utils::NavigateToURL(
508 embedded_test_server()->GetURL("a.root-servers.net", "/title1.html")));
510 content::RenderFrameHost* rfh = browser()
512 ->GetActiveWebContents()
513 ->GetPrimaryMainFrame();
515 content::RenderProcessHostBadMojoMessageWaiter crash_observer(
518 // The renderer should always get killed, but sometimes ExecJs returns
519 // true anyway, so just ignore the result.
520 std::ignore = content::ExecJs(rfh, "URL.createObjectURL(new Blob(['foo']))");
522 // If the process is killed, this test passes.
524 "Received bad user message: "
525 "URL with invalid origin passed to BlobURLStore::Register",
526 crash_observer.Wait());
529 // chrome://xyz should not be able to create a "blob:chrome://abc" resource.
530 IN_PROC_BROWSER_TEST_P(ChromeSecurityExploitBrowserTestMojoBlobURLsP,
531 CreateBlobInOtherChromeUIOrigin) {
533 ui_test_utils::NavigateToURL(browser(), GURL("chrome://version")));
535 // All these are attacker controlled values.
536 std::string blob_type = "text/html";
537 std::string blob_contents = "<p>Hello world!</p>";
538 std::string blob_path = "f7dfbeb5-8e41-4c4a-8486-a52fed33c4c0";
540 // Target an extension.
541 std::string target_origin = "chrome://downloads";
543 base::RepeatingCallback<void(storage::BlobUrlRegistry*, mojo::ReceiverId)>
544 blob_url_registry_intercept_hook;
545 base::RepeatingCallback<void(
546 mojo::SelfOwnedAssociatedReceiverRef<blink::mojom::BlobURLStore>)>
547 blob_registry_impl_intercept_hook;
549 if (base::FeatureList::IsEnabled(net::features::kSupportPartitionedBlobUrl)) {
550 blob_url_registry_intercept_hook =
551 base::BindRepeating(&content::BlobURLStoreInterceptor::Intercept,
552 GURL("blob:" + target_origin + "/" + blob_path));
553 storage::BlobUrlRegistry::SetURLStoreCreationHookForTesting(
554 &blob_url_registry_intercept_hook);
556 blob_registry_impl_intercept_hook = base::BindRepeating(
557 &content::BlobURLStoreInterceptor::InterceptDeprecated,
558 GURL("blob:" + target_origin + "/" + blob_path));
559 storage::BlobRegistryImpl::SetURLStoreCreationHookForTesting(
560 &blob_registry_impl_intercept_hook);
563 content::RenderFrameHost* rfh = browser()
565 ->GetActiveWebContents()
566 ->GetPrimaryMainFrame();
568 content::RenderProcessHostBadMojoMessageWaiter crash_observer(
571 // The renderer should always get killed, but sometimes ExecJs returns
572 // true anyway, so just ignore the result.
573 std::ignore = content::ExecJs(rfh, "URL.createObjectURL(new Blob(['foo']))");
575 // If the process is killed, this test passes.
577 "Received bad user message: "
578 "URL with invalid origin passed to BlobURLStore::Register",
579 crash_observer.Wait());
582 INSTANTIATE_TEST_SUITE_P(
583 ChromeSecurityExploitBrowserTestMojoBlobURLs,
584 ChromeSecurityExploitBrowserTestMojoBlobURLsP,
585 ::testing::Values(ChromeSecurityExploitBrowserTestMojoBlobURLsTestCase::
586 kSupportPartitionedBlobUrlDisabled,
587 ChromeSecurityExploitBrowserTestMojoBlobURLsTestCase::
588 kSupportPartitionedBlobUrlEnabled));