diff options
Diffstat (limited to 'testing/web-platform/tests/fetch')
19 files changed, 645 insertions, 136 deletions
diff --git a/testing/web-platform/tests/fetch/api/basic/request-upload.h2.any.js b/testing/web-platform/tests/fetch/api/basic/request-upload.h2.any.js index eedc2bf6a7..68122278cc 100644 --- a/testing/web-platform/tests/fetch/api/basic/request-upload.h2.any.js +++ b/testing/web-platform/tests/fetch/api/basic/request-upload.h2.any.js @@ -184,3 +184,26 @@ promise_test(async (t) => { await promise_rejects_js(t, TypeError, fetch(url, { method, body, duplex })); }, "Streaming upload should fail on a 401 response"); +promise_test(async (t) => { + const abortMessage = 'foo abort'; + let streamCancelPromise = new Promise(async res => { + var stream = new ReadableStream({ + cancel: function(reason) { + res(reason); + } + }); + let abortController = new AbortController(); + let fetchPromise = promise_rejects_exactly(t, abortMessage, fetch('', { + method: 'POST', + body: stream, + duplex: 'half', + signal: abortController.signal + })); + abortController.abort(abortMessage); + await fetchPromise; + }); + + let cancelReason = await streamCancelPromise; + assert_equals( + cancelReason, abortMessage, 'ReadableStream.cancel should be called.'); +}, 'ReadbleStream should be closed on signal.abort'); diff --git a/testing/web-platform/tests/fetch/api/body/formdata.any.js b/testing/web-platform/tests/fetch/api/body/formdata.any.js index e25035923c..6733fa0ed7 100644 --- a/testing/web-platform/tests/fetch/api/body/formdata.any.js +++ b/testing/web-platform/tests/fetch/api/body/formdata.any.js @@ -12,3 +12,14 @@ promise_test(async t => { const fd = await req.formData(); assert_true(fd instanceof FormData); }, 'Consume empty request.formData() as FormData'); + +promise_test(async t => { + let formdata = new FormData(); + formdata.append('foo', new Blob([JSON.stringify({ bar: "baz", })], { type: "application/json" })); + let blob = await new Response(formdata).blob(); + let body = await blob.text(); + blob = new Blob([body.toLowerCase()], { type: blob.type.toLowerCase() }); + let formdataWithLowercaseBody = await new Response(blob).formData(); + assert_true(formdataWithLowercaseBody.has("foo")); + assert_equals(formdataWithLowercaseBody.get("foo").type, "application/json"); +}, 'Consume multipart/form-data headers case-insensitively'); diff --git a/testing/web-platform/tests/fetch/api/request/request-consume-empty.any.js b/testing/web-platform/tests/fetch/api/request/request-consume-empty.any.js index 034a86041a..0bf9672a79 100644 --- a/testing/web-platform/tests/fetch/api/request/request-consume-empty.any.js +++ b/testing/web-platform/tests/fetch/api/request/request-consume-empty.any.js @@ -8,23 +8,11 @@ function checkBodyText(test, request) { }); } -function checkBodyBlob(test, request) { - return request.blob().then(function(bodyAsBlob) { - var promise = new Promise(function(resolve, reject) { - var reader = new FileReader(); - reader.onload = function(evt) { - resolve(reader.result) - }; - reader.onerror = function() { - reject("Blob's reader failed"); - }; - reader.readAsText(bodyAsBlob); - }); - return promise.then(function(body) { - assert_equals(body, "", "Resolved value should be empty"); - assert_false(request.bodyUsed); - }); - }); +async function checkBodyBlob(test, request) { + const bodyAsBlob = await request.blob(); + const body = await bodyAsBlob.text(); + assert_equals(body, "", "Resolved value should be empty"); + assert_false(request.bodyUsed); } function checkBodyArrayBuffer(test, request) { diff --git a/testing/web-platform/tests/fetch/api/request/request-consume.any.js b/testing/web-platform/tests/fetch/api/request/request-consume.any.js index aff5d65244..3db9e8f265 100644 --- a/testing/web-platform/tests/fetch/api/request/request-consume.any.js +++ b/testing/web-platform/tests/fetch/api/request/request-consume.any.js @@ -9,26 +9,15 @@ function checkBodyText(request, expectedBody) { }); } -function checkBodyBlob(request, expectedBody, checkContentType) { - return request.blob().then(function(bodyAsBlob) { - if (checkContentType) - assert_equals(bodyAsBlob.type, "text/plain", "Blob body type should be computed from the request Content-Type"); - - var promise = new Promise(function (resolve, reject) { - var reader = new FileReader(); - reader.onload = function(evt) { - resolve(reader.result) - }; - reader.onerror = function() { - reject("Blob's reader failed"); - }; - reader.readAsText(bodyAsBlob); - }); - return promise.then(function(body) { - assert_equals(body, expectedBody, "Retrieve and verify request's body"); - assert_true(request.bodyUsed, "body as blob: bodyUsed turned true"); - }); - }); +async function checkBodyBlob(request, expectedBody, checkContentType) { + const bodyAsBlob = await request.blob(); + + if (checkContentType) + assert_equals(bodyAsBlob.type, "text/plain", "Blob body type should be computed from the request Content-Type"); + + const body = await bodyAsBlob.text(); + assert_equals(body, expectedBody, "Retrieve and verify request's body"); + assert_true(request.bodyUsed, "body as blob: bodyUsed turned true"); } function checkBodyArrayBuffer(request, expectedBody) { diff --git a/testing/web-platform/tests/fetch/api/response/response-consume-empty.any.js b/testing/web-platform/tests/fetch/api/response/response-consume-empty.any.js index 0fa85ecbcb..a5df356258 100644 --- a/testing/web-platform/tests/fetch/api/response/response-consume-empty.any.js +++ b/testing/web-platform/tests/fetch/api/response/response-consume-empty.any.js @@ -8,23 +8,12 @@ function checkBodyText(test, response) { }); } -function checkBodyBlob(test, response) { - return response.blob().then(function(bodyAsBlob) { - var promise = new Promise(function(resolve, reject) { - var reader = new FileReader(); - reader.onload = function(evt) { - resolve(reader.result) - }; - reader.onerror = function() { - reject("Blob's reader failed"); - }; - reader.readAsText(bodyAsBlob); - }); - return promise.then(function(body) { - assert_equals(body, "", "Resolved value should be empty"); - assert_false(response.bodyUsed); - }); - }); +async function checkBodyBlob(test, response) { + const bodyAsBlob = await response.blob(); + const body = await bodyAsBlob.text(); + + assert_equals(body, "", "Resolved value should be empty"); + assert_false(response.bodyUsed); } function checkBodyArrayBuffer(test, response) { diff --git a/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js index 4e860ad381..f5473868b7 100644 --- a/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js @@ -149,6 +149,44 @@ subsetTestByKey("from-public", promise_test_parallel, t => anchorTest(t, { expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); +subsetTestByKey( + 'from-public', promise_test_parallel, + t => anchorTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.noCorsHeader(token()), + } + }), + } + }, + expected: NavigationTestResult.FAILURE, + }), + 'public to public redirected to private: missing CORS headers.'); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => anchorTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.navigation(token()), + } + }), + } + }, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public to private: success.'); + // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address. diff --git a/testing/web-platform/tests/fetch/private-network-access/resources/service-worker-fetch-all.js b/testing/web-platform/tests/fetch/private-network-access/resources/service-worker-fetch-all.js new file mode 100644 index 0000000000..78ac8d1576 --- /dev/null +++ b/testing/web-platform/tests/fetch/private-network-access/resources/service-worker-fetch-all.js @@ -0,0 +1,20 @@ +self.addEventListener("install", () => { + // Skip waiting before replacing the previously-active service worker, if any. + // This allows the bridge script to notice the controller change and query + // the install time via fetch. + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + // Claim all clients so that the bridge script notices the activation. + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("fetch", (event) => { + const url = new URL(event.request.url).searchParams.get("proxied-url"); + if (url) { + event.respondWith(fetch(url)); + } else { + event.respondWith(fetch(event.request)); + } +}); diff --git a/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js b/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js index 46a9d9e076..1cb432b787 100644 --- a/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js +++ b/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js @@ -480,6 +480,13 @@ const NavigationTestResult = { }; async function windowOpenTest(t, { source, target, expected }) { + if (target.behavior && target.behavior.redirect) { + target.behavior.redirect.searchParams.set('file', 'openee.html'); + target.behavior.redirect.searchParams.set( + 'file-if-no-preflight-received', + 'no-preflight-received.html', + ); + } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -507,6 +514,13 @@ async function windowOpenTest(t, { source, target, expected }) { } async function windowOpenExistingTest(t, { source, target, expected }) { + if (target.behavior && target.behavior.redirect) { + target.behavior.redirect.searchParams.set('file', 'openee.html'); + target.behavior.redirect.searchParams.set( + 'file-if-no-preflight-received', + 'no-preflight-received.html', + ); + } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -535,6 +549,13 @@ async function windowOpenExistingTest(t, { source, target, expected }) { } async function anchorTest(t, { source, target, expected }) { + if (target.behavior && target.behavior.redirect) { + target.behavior.redirect.searchParams.set('file', 'openee.html'); + target.behavior.redirect.searchParams.set( + 'file-if-no-preflight-received', + 'no-preflight-received.html', + ); + } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -855,3 +876,66 @@ async function sharedWorkerBlobFetchTest(t, { source, target, expected }) { assert_equals(status, expected.status, "response status"); assert_equals(body, expected.body, "response body"); } + +async function makeServiceWorkerTest(t, { source, target, expected, fetch_document=false }) { + const bridgeUrl = resolveUrl( + "resources/service-worker-bridge.html", + sourceResolveOptions({ server: source.server })); + + const scriptUrl = fetch_document? + resolveUrl("resources/service-worker-fetch-all.js", sourceResolveOptions(source)): + resolveUrl("resources/service-worker.js", sourceResolveOptions(source)); + + const realTargetUrl = preflightUrl(target); + + // Fetch a URL within the service worker's scope, but tell it which URL to + // really fetch. + const targetUrl = new URL("service-worker-proxy", scriptUrl); + targetUrl.searchParams.append("proxied-url", realTargetUrl.href); + + const iframe = await appendIframe(t, document, bridgeUrl); + + const request = (message) => { + const reply = futureMessage(); + iframe.contentWindow.postMessage(message, "*"); + return reply; + }; + + { + const { error, loaded } = await request({ + action: "register", + url: scriptUrl.href, + }); + + assert_equals(error, undefined, "register error"); + assert_true(loaded, "response loaded"); + } + + try { + const { controlled, numControllerChanges } = await request({ + action: "wait", + numControllerChanges: 1, + }); + + assert_equals(numControllerChanges, 1, "controller change"); + assert_true(controlled, "bridge script is controlled"); + + const { error, ok, body } = await request({ + action: "fetch", + url: targetUrl.href, + }); + + assert_equals(error, expected.error, "fetch error"); + assert_equals(ok, expected.ok, "response ok"); + assert_equals(body, expected.body, "response body"); + } finally { + // Always unregister the service worker. + const { error, unregistered } = await request({ + action: "unregister", + scope: new URL("./", scriptUrl).href, + }); + + assert_equals(error, undefined, "unregister error"); + assert_true(unregistered, "unregistered"); + } +} diff --git a/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js new file mode 100644 index 0000000000..6fc29ce472 --- /dev/null +++ b/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js @@ -0,0 +1,101 @@ +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// +// Spec: https://wicg.github.io/private-network-access/#integration-fetch +// +// These tests check that fetches from within `ServiceWorker` scripts are +// subject to Private Network Access checks, just like fetches from within +// documents. + +// Results that may be expected in tests. +const TestResult = { + SUCCESS: { ok: true, body: "success" }, + FAILURE: { error: "TypeError" }, +}; + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.OTHER_HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "treat-as-public to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.OTHER_HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTPS_LOCAL }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to local (same-origin): no preflight required."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "treat-as-public to private: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { response: ResponseBehavior.allowCrossOrigin() }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to public: success."); diff --git a/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js new file mode 100644 index 0000000000..ec380555a8 --- /dev/null +++ b/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js @@ -0,0 +1,114 @@ +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// +// Spec: https://wicg.github.io/private-network-access/#integration-fetch +// +// These tests check that fetches from within `ServiceWorker` scripts are +// subject to Private Network Access checks, just like fetches from within +// documents. + +// Results that may be expected in tests. +const TestResult = { + SUCCESS: { ok: true, body: "success" }, + FAILURE: { error: "TypeError" }, +}; + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_LOCAL }, + target: { server: Server.HTTPS_LOCAL }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "local to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "private to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "private to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { server: Server.HTTPS_PRIVATE }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "private to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "public to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "public to private: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { server: Server.HTTPS_PUBLIC }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to public: success."); + diff --git a/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js index cb6d1f79b0..5fc5800ba0 100644 --- a/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js @@ -16,84 +16,25 @@ const TestResult = { FAILURE: { error: "TypeError" }, }; -async function makeTest(t, { source, target, expected }) { - const bridgeUrl = resolveUrl( - "resources/service-worker-bridge.html", - sourceResolveOptions({ server: source.server })); - - const scriptUrl = - resolveUrl("resources/service-worker.js", sourceResolveOptions(source)); - - const realTargetUrl = preflightUrl(target); - - // Fetch a URL within the service worker's scope, but tell it which URL to - // really fetch. - const targetUrl = new URL("service-worker-proxy", scriptUrl); - targetUrl.searchParams.append("proxied-url", realTargetUrl.href); - - const iframe = await appendIframe(t, document, bridgeUrl); - - const request = (message) => { - const reply = futureMessage(); - iframe.contentWindow.postMessage(message, "*"); - return reply; - }; - - { - const { error, loaded } = await request({ - action: "register", - url: scriptUrl.href, - }); - - assert_equals(error, undefined, "register error"); - assert_true(loaded, "response loaded"); - } - - try { - const { controlled, numControllerChanges } = await request({ - action: "wait", - numControllerChanges: 1, - }); - - assert_equals(numControllerChanges, 1, "controller change"); - assert_true(controlled, "bridge script is controlled"); - - const { error, ok, body } = await request({ - action: "fetch", - url: targetUrl.href, - }); - - assert_equals(error, expected.error, "fetch error"); - assert_equals(ok, expected.ok, "response ok"); - assert_equals(body, expected.body, "response body"); - } finally { - // Always unregister the service worker. - const { error, unregistered } = await request({ - action: "unregister", - scope: new URL("./", scriptUrl).href, - }); - - assert_equals(error, undefined, "unregister error"); - assert_true(unregistered, "unregistered"); - } -} - -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL }, target: { server: Server.HTTPS_LOCAL }, expected: TestResult.SUCCESS, }), "local to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "private to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_LOCAL, @@ -105,22 +46,25 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "private to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_PRIVATE }, expected: TestResult.SUCCESS, }), "private to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "public to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_LOCAL, @@ -132,16 +76,19 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "public to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PRIVATE, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "public to private: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PRIVATE, @@ -153,25 +100,28 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "public to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PUBLIC }, expected: TestResult.SUCCESS, }), "public to public: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: { server: Server.OTHER_HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "treat-as-public to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -186,7 +136,7 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "treat-as-public to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -195,19 +145,22 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "treat-as-public to local (same-origin): no preflight required."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: { server: Server.HTTPS_PRIVATE, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "treat-as-public to private: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -222,7 +175,7 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "treat-as-public to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, diff --git a/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js index 6a2a624fc8..565a2117a8 100644 --- a/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js @@ -167,6 +167,44 @@ subsetTestByKey( }), 'public to public: no preflight required.'); +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.noCorsHeader(token()), + } + }), + } + }, + expected: NavigationTestResult.FAILURE, + }), + 'public to public redirected to private: missing CORS headers.'); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.navigation(token()), + } + }), + } + }, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public to private: success.'); + // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address. diff --git a/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js index 6793d1f3b4..42d70af4e4 100644 --- a/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js @@ -149,6 +149,44 @@ subsetTestByKey("from-public", promise_test_parallel, t => windowOpenTest(t, { expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.noCorsHeader(token()), + } + }), + } + }, + expected: NavigationTestResult.FAILURE, + }), + 'public to public redirected to private: missing CORS headers.'); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { + redirect: preflightUrl({ + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.navigation(token()), + } + }), + } + }, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public to private: success.'); + // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address. diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html new file mode 100644 index 0000000000..66456a8876 --- /dev/null +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + const blank = 'about:blank'; + const dangling_url = 'resources/empty.html?\n<'; + const api_calls = [ + `window.open(\`${dangling_url}\`,'_self')`, + `location.replace(\`${dangling_url}\`)`, + ]; + + api_calls.forEach(call => { + async_test(t => { + const iframe = + document.body.appendChild(document.createElement('iframe')); + t.step(() => { + iframe.contentWindow.eval(call) + t.step_timeout(()=>{ + assert_false(iframe.contentWindow.location.href.endsWith(blank)); + t.done(); + }, 500); + }); + }, `Does not block ${call}`); + }); +</script> diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.sub.html index f27735daa1..f27735daa1 100644 --- a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.sub.html diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.html index 61a931608b..61a931608b 100644 --- a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.html diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.https.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.https.html new file mode 100644 index 0000000000..3f038cbb7b --- /dev/null +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.https.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<meta name="timeout" content="long"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + function get_requests(worker, expected) { + return new Promise(resolve => { + navigator.serviceWorker.addEventListener('message', function onMsg(evt) { + if (evt.data.size >= expected) { + navigator.serviceWorker.removeEventListener('message', onMsg); + resolve(evt.data); + } else { + worker.postMessage(""); + } + }); + worker.postMessage(""); + }); + } + + const resources = [ + x=>`<link rel="stylesheet" href="404/style?${x}">`, + x=>`<link rel="prefetch" as="style" href="404/prefetch?${x}">`, + x=>`<script src="404/script?${x}"><\/script>`, + x=>`<iframe src="404/iframe?${x}"></iframe>`, + x=>`<meta http-equiv="refresh" content="0;url=404/meta?${x}">`, + x=>`<a href="404/a?${x}">click</a><script>document.querySelector('a').click()<\/script>`, + x=>`<base href="404/base?${x}"><a href>me</a><script>document.querySelector('a').click()<\/script>`, + x=>`<video controls poster="404/poster?${x}"></video>`, + x=>`<input type="image" src="404/input?${x}">`, + x=>`<form method="GET" action="404/form?${x}"></form><script>document.querySelector('form').submit()<\/script>`, + x=>`<body background="404/body?${x}"></body>`, + ]; + + async_test(t => { + const script = 'service-worker.js'; + const paths = []; + navigator.serviceWorker.register(script); + t.step(async () => { + const registration = await navigator.serviceWorker.ready; + for (const html of resources) { + const iframe1 = + document.body.appendChild(document.createElement('iframe')); + iframe1.src = 'resources.html?html=' + html`%0A<`; + const iframe2 = + document.body.appendChild(document.createElement('iframe')); + iframe2.src = 'resources.html?html=' + html``; + const path = html`EOP`; + paths.push(path.substring(path.search('404\\/')+4, path.search('EOP'))); + } + + const requests = await get_requests(registration.active, resources.length); + paths.forEach(path => { + assert_true(requests.has(path), + `${path} should appear in requests sent`); + }); + await registration.unregister(); + t.done(); + }); + }, 'Only blocks dangling markup requests'); +</script> diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/resources/empty.html b/testing/web-platform/tests/fetch/security/dangling-markup/resources/empty.html new file mode 100644 index 0000000000..0e76edd65b --- /dev/null +++ b/testing/web-platform/tests/fetch/security/dangling-markup/resources/empty.html @@ -0,0 +1 @@ +<!DOCTYPE html> diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js b/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js new file mode 100644 index 0000000000..837e216a01 --- /dev/null +++ b/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js @@ -0,0 +1,35 @@ +const requests = new Set(); + +addEventListener('install', evt => { + evt.waitUntil(self.skipWaiting()); +}); + +addEventListener('activate', evt => { + evt.waitUntil(self.clients.claim()); +}); + +addEventListener('message', evt => { + evt.source.postMessage(requests); +}); + +addEventListener('fetch', evt => { + const url = new URL(evt.request.url); + const path = url.pathname; + const search = url.search || "?"; + if (path.includes('404')) { + const dir = path.split('/'); + const request = dir[dir.length-1] + search; + if (!requests.has(request)) { + requests.add(request); + } + evt.respondWith(new Response("")); + } else if (path.endsWith('resources.html')) { + const html = (new URLSearchParams(search)).get('html'); + evt.respondWith(new Response(html, { + headers: { + "Content-Type": "text/html" + } + })); + } + return; +}); |