diff --git a/blink/common/fenced_frame/fenced_frame_utils.cc b/blink/common/fenced_frame/fenced_frame_utils.cc index 0b2f9394e1b..1932339ddf1 100644 --- a/blink/common/fenced_frame/fenced_frame_utils.cc +++ b/blink/common/fenced_frame/fenced_frame_utils.cc @@ -16,8 +16,9 @@ namespace blink { bool IsValidFencedFrameURL(const GURL& url) { if (!url.is_valid()) return false; - return url.SchemeIs(url::kHttpsScheme) || url.IsAboutBlank() || - net::IsLocalhost(url); + return (url.SchemeIs(url::kHttpsScheme) || url.IsAboutBlank() || + net::IsLocalhost(url)) && + !url.parsed_for_possibly_invalid_spec().potentially_dangling_markup; } const char kURNUUIDprefix[] = "urn:uuid:"; diff --git a/blink/web_tests/platform/generic/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt b/blink/web_tests/platform/generic/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt new file mode 100644 index 00000000000..130205a23fe --- /dev/null +++ b/blink/web_tests/platform/generic/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/disallowed-navigations.https-expected.txt @@ -0,0 +1,42 @@ +This is a testharness.js-based test. +PASS iframe data: URL +PASS iframe blob: URL +PASS iframe javascript: URL +PASS fenced frame mode=opaque-ads data: URL +PASS fenced frame mode=opaque-ads blob: URL +PASS fenced frame mode=opaque-ads javascript: URL +PASS fenced frame mode=opaque-ads http: URL +PASS fenced frame mode=opaque-ads dangling-markup URL with 'blo +ck data: URL +PASS fenced frame opaque URN => blob: URL +PASS fenced frame opaque URN => javascript: URL +PASS fenced frame opaque URN => http: URL +FAIL fenced frame opaque URN => https: URL with dangling markup 'blo +ck + t.step_timeout(() => resolve("NOT LOADED"), 2000)); +} + // The following tests ensure that an embedder cannot navigate a fenced frame // (with different modes) to: // - data: URLs // - blob: URLs // - javascript: URLs // - http: URLs -function getTimeoutPromise(t) { - return new Promise(resolve => - t.step_timeout(() => resolve("NOT LOADED"), 2000)); -} - +// - https: URLs with dangling markup for (const mode of ['opaque-ads', 'default']) { promise_test(async t => { @@ -126,6 +138,19 @@ assert_equals(result, "NOT LOADED"); }, `fenced frame mode=${mode} http: URL`); + // These tests assert that fenced frames cannot be navigated to HTTPs URLs + // with dangling markup. + for (substring of kDanglingMarkupSubstrings) { + promise_test(async t => { + const key = token(); + let url_string = generateURL("resources/embeddee.html?blocked", [key]).toString(); + url_string = url_string.replace("blocked", substring); + const fencedframe = attachFencedFrame(url_string, /*mode=*/mode); + const loaded_promise = nextValueFromServer(key); + const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]); + assert_equals(result, "NOT LOADED"); + }, `fenced frame mode=${mode} dangling-markup URL with '${substring}'`); + } } // end for. // The following tests ensure that an embedder cannot navigate a @@ -134,6 +159,7 @@ // - blob: URL // - javascript: URL // - http: URL +// - https: URL with dangling markup promise_test(async t => { const key = token(); const urn = await generateURN(`data:text/html,${createLocalSource(key)}`); @@ -175,6 +201,36 @@ const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]); assert_equals(result, "NOT LOADED"); }, "fenced frame opaque URN => http: URL"); + +// These tests assert that fenced frames cannot be navigated to a urn:uuid URL +// that represents an HTTPS URLs with dangling markup. +for (substring of kDanglingMarkupSubstrings) { + promise_test(async t => { + const key = token(); + + // Copied from from `generateURN()`, since we have to modify the final URL + // that goes into `selectURL`. + try { + await sharedStorage.worklet.addModule( + "/wpt_internal/shared_storage/resources/simple-module.js"); + } catch (e) { + // See documentation in `generateURN()`. + } + + let url_string = generateURL("resources/report-url.html?blocked", [key]).toString(); + url_string = url_string.replace("blocked", substring); + + const urn = await sharedStorage.selectURL( + "test-url-selection-operation", [url_string], {data: {'mockResult': 0}} + ); + + const fencedframe = attachFencedFrame(urn, /*mode=*/'opaque-ads'); + const loaded_promise = nextValueFromServer(key); + const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]); + assert_equals(result, "NOT LOADED"); + }, `fenced frame opaque URN => https: URL with dangling markup '${substring}'`); +} + diff --git a/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html b/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html new file mode 100644 index 00000000000..e0b7d0982ae --- /dev/null +++ b/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html @@ -0,0 +1,7 @@ + + +A page embedded as a fenced frame that reports the document URL + diff --git a/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html.headers b/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html.headers new file mode 100644 index 00000000000..6247f6d6321 --- /dev/null +++ b/blink/web_tests/wpt_internal/fenced_frame/resources/report-url.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame \ No newline at end of file