diff options
Diffstat (limited to 'testing/web-platform/tests/network-error-logging')
26 files changed, 867 insertions, 0 deletions
diff --git a/testing/web-platform/tests/network-error-logging/META.yml b/testing/web-platform/tests/network-error-logging/META.yml new file mode 100644 index 0000000000..6c1f88eafc --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/META.yml @@ -0,0 +1,3 @@ +spec: https://w3c.github.io/network-error-logging/ +suggested_reviewers: + - dcreager diff --git a/testing/web-platform/tests/network-error-logging/README.md b/testing/web-platform/tests/network-error-logging/README.md new file mode 100644 index 0000000000..7cf2c6fdce --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/README.md @@ -0,0 +1,74 @@ +# Network Error Logging + +The tests in this directory exercise the user agent's implementation of [Network +Error Logging](https://w3c.github.io/network-error-logging/) and +[Reporting](https://w3c.github.io/reporting/). + +## Collector + +Each test case generates a unique `reportID` that is used to distinguish the NEL +reports generated by that test case. + +The [support/report.py][] file is a [Python file handler][] that can be used as +a Reporting collector. Its default operation is to save any reports that it +receives into the [stash][]. If you pass in the optional `op` URL parameter, +with a value of `retrieve_report`, it will instead return a list of all of the +reports received for a particular `reportID`. + +[Python file handler]: https://wptserve.readthedocs.io/en/latest/handlers.html#python-file-handlers +[stash]: https://wptserve.readthedocs.io/en/latest/stash.html +[support/report.py]: support/report.py + +## Installing NEL policies + +NEL reports are only generated if the user agent has received a NEL policy for +the origin of the request. The current request counts; if its response contains +a policy, that policy is used for the current request and all future requests, +modulo the policy's `max_age` field. + +Most of the test cases will therefore make a request or two to install NEL +policies, and then make another request that should or should not be covered by +those policies. It will then assert that NEL reports were or were not created, +as required by the spec. + +The [support][] directory contains several images, each of which defines a +particular "kind" of NEL policy (e.g., `include_subdomains` set vs unset, no +policy at all, etc.). The [support/nel.sub.js][] file contains helper +JavaScript methods for requesting those images, so that the test cases +themselves are more descriptive. + +[support]: support +[support/nel.sub.js]: support/nel.sub.js + +## Avoiding spurious reports + +NEL policies apply to **all** future requests to the origin. We therefore serve +all of the test case's "infrastructure" (the test case itself, +[support/report.py][] and [support/nel.sub.js][]) on a different origin than +the requests that exercise the NEL implementation. That ensures that we don't +have to wade through NEL reports about the infrastructure when verifying the NEL +reports about the requests that we care about. + +## Browser configuration + +You must configure your browser's Reporting implementation to upload reports for +a request immediately. The test cases do not currently have any timeouts; they +assume that as soon as the Fetch API promise resolves, any NEL reports for the +request have already been uploaded. + +## Test parallelism + +Because NEL policies are stored in a global cache in the user agent, we need to +run the tests in this directory serially instead of in parallel. We implement a +simple spin-lock in [support/lock.py][] to ensure that only one test is allowed +to perform any NEL-related requests at a time. + +[support/lock.py]: support/lock.py + +## CORS preflights + +Reporting uploads are subject to CORS preflights. We want to test normal +operation (when preflight requests succeed) as well as failures of the CORS +preflight logic in the user agent. To support this, our test collector is +configured to always reject the CORS preflight for a single domain (www2), and +to always grant the CORS preflight for all other test subdomains. diff --git a/testing/web-platform/tests/network-error-logging/no-report-on-failed-cors-preflight.https.html b/testing/web-platform/tests/network-error-logging/no-report-on-failed-cors-preflight.https.html new file mode 100644 index 0000000000..3a35651b4e --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/no-report-on-failed-cors-preflight.https.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are not sent if the CORS preflight fails + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include a NEL + // policy, but where the report uploader will reject the CORS preflight. + await fetchResourceWithBasicPolicy('www2'); + // Because the CORS preflight is rejected, we should never receive a + // report about the request. + assert_false(await reportExists({ + url: getURLForResourceWithBasicPolicy('www2'), + type: "network-error", + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/no-report-on-subdomain-404.https.html b/testing/web-platform/tests/network-error-logging/no-report-on-subdomain-404.https.html new file mode 100644 index 0000000000..462f99e842 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/no-report-on-subdomain-404.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that include_subdomains policies do NOT report HTTP errors + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include an + // include_subdomains NEL policy. + await fetchResourceWithIncludeSubdomainsPolicy(); + // Make a request to another resource on a subdomain of the above. This + // resource doesn't exist, so the server should return a 404. + await fetchMissingResource('www'); + // The include_subdomains policy that we just received should NOT cover + // the second request, since include_subdomains policies can only report + // on DNS errors. + assert_false(await reportExists({ + url: getURLForMissingResource('www'), + type: "network-error", + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/no-report-on-subdomain-success.https.html b/testing/web-platform/tests/network-error-logging/no-report-on-subdomain-success.https.html new file mode 100644 index 0000000000..5fd6d4fb41 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/no-report-on-subdomain-success.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that include_subdomains policies do NOT report successful requests + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include an + // include_subdomains NEL policy. + await fetchResourceWithIncludeSubdomainsPolicy(); + // Make a request to another resource on a subdomain of the above, which + // does not define its own NEL policy. + await fetchResourceWithNoPolicy('www'); + // The include_subdomains policy that we just received should NOT cover + // the second request, since include_subdomains policies can only report + // on DNS errors. + assert_false(await reportExists({ + url: getURLForResourceWithNoPolicy('www'), + type: "network-error", + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/no-report-on-unexpired-cached-response.https.html b/testing/web-platform/tests/network-error-logging/no-report-on-unexpired-cached-response.https.html new file mode 100644 index 0000000000..8248aa52ec --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/no-report-on-unexpired-cached-response.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are not sent for cached responses that don't hit the + network + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Fetch a resource that can be cached without validation. Do this + // *before* fetching the NEL policy for this origin, to ensure that we + // don't generate any report about this request. + await fetchCachedResource(); + // Fetch the NEL policy for this origin. + await fetchResourceWithBasicPolicy(); + // Fetch the now-cached resource again. This should not generate a new + // network request. + await fetchCachedResource(); + // Because the cached request did not generate a network request, we + // should never receive a report about the request. + assert_false(await reportExists({ + url: getURLForCachedResource(), + type: "network-error", + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/reports-are-not-observable.https.html b/testing/web-platform/tests/network-error-logging/reports-are-not-observable.https.html new file mode 100644 index 0000000000..35ab4f3c23 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/reports-are-not-observable.https.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are not observable from JavaScript + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Register an observer for NEL reports. + var observer = new ReportingObserver((reports, _) => { + assert_unreached("NEL reports should not be observable"); + }, {"types": ["network-error"]}); + observer.observe(); + // Make a request to a resource whose response headers include a NEL + // policy. If NEL reports are observable, this will queue a task that + // calls the observer function above (which we don't want). + await fetchResourceWithBasicPolicy(); + // Wait for one second to give any observer callback task a chance to + // fire. + await new Promise(resolve => t.step_timeout(resolve, 1000 /* msec */)); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/sends-report-on-404.https.html b/testing/web-platform/tests/network-error-logging/sends-report-on-404.https.html new file mode 100644 index 0000000000..38bdc01450 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/sends-report-on-404.https.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are sent for HTTP errors + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include a NEL + // policy. + await fetchResourceWithBasicPolicy(); + // Make a request to another resource on the same domain. This resource + // doesn't exist, so the server should return a 404. + await fetchMissingResource(); + // The 404 won't contain its own NEL policy, but the policy we received in + // the first request should cover the second request, too, since they're + // at the same origin, so the collector should have received a report + // about it. + assert_true(await reportExists({ + url: getURLForMissingResource(), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 404, + phase: "application", + type: "http.error", + }, + metadata: { + content_type: "application/reports+json", + }, + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/sends-report-on-cache-validation.https.html b/testing/web-platform/tests/network-error-logging/sends-report-on-cache-validation.https.html new file mode 100644 index 0000000000..b87053cb7f --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/sends-report-on-cache-validation.https.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are sent for cache validation requests + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Fetch a resource that can be cached, but whose response requires + // validation. Do this *before* fetching the NEL policy for this origin, + // to ensure that we don't generate any report about this request. + await fetchValidatedCachedResource(); + // Fetch the NEL policy for this origin. + await fetchResourceWithBasicPolicy(); + // Fetch the now-cached resource again. Because the response requires + // validation, this will result in a network request. + await fetchValidatedCachedResource(); + // We should receive a report about the cache validation request. + assert_true(await reportExists({ + url: getURLForValidatedCachedResource(), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 304, + phase: "application", + type: "ok", + }, + metadata: { + content_type: "application/reports+json", + }, + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/sends-report-on-redirect.https.html b/testing/web-platform/tests/network-error-logging/sends-report-on-redirect.https.html new file mode 100644 index 0000000000..da6ae72c5a --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/sends-report-on-redirect.https.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are sent for redirects + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include a NEL + // policy. + await fetchResourceWithBasicPolicy(); + // Then make a request that results in a redirect. + await fetchRedirectedResource(); + // We should receive a report about the redirect itself, and also about + // the resource that we were redirected to. + assert_true(await reportExists({ + url: getURLForRedirectedResource(), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 302, + phase: "application", + type: "ok", + }, + metadata: { + content_type: "application/reports+json", + }, + }, true /* retain */), "receive report about redirected resource"); + assert_true(await reportExists({ + // This happens to be where we're redirected to. + url: getURLForResourceWithNoPolicy(), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 200, + phase: "application", + type: "ok", + }, + metadata: { + content_type: "application/reports+json", + }, + }), "receive report about redirect target"); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/sends-report-on-subdomain-dns-failure.https.html b/testing/web-platform/tests/network-error-logging/sends-report-on-subdomain-dns-failure.https.html new file mode 100644 index 0000000000..8913857af8 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/sends-report-on-subdomain-dns-failure.https.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that include_subdomains policies report DNS failures for subdomains + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include an + // include_subdomains NEL policy. + await fetchResourceWithIncludeSubdomainsPolicy(); + // Make a request to another resource on a nonexistent subdomain of the + // above. Since the subdomain doesn't exist, the request should fail with + // a DNS error. + await fetchResourceWithNoPolicy('nonexistent').then((response) => { + assert_unreached("Request to nonexistent domain should fail"); + }, (err) => { + // Silence the error, since it's expected. + }); + // The include_subdomains policy that we just received should cover the + // second request, since include_subdomains policies can report on DNS + // errors, so the collector should have received a report about it. + assert_true(await reportExists({ + url: getURLForResourceWithNoPolicy('nonexistent'), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 0, + phase: "dns", + type: "dns.name_not_resolved", + }, + metadata: { + content_type: "application/reports+json", + }, + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/sends-report-on-success-with-subdomain-policy.https.html b/testing/web-platform/tests/network-error-logging/sends-report-on-success-with-subdomain-policy.https.html new file mode 100644 index 0000000000..fce12cd3e9 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/sends-report-on-success-with-subdomain-policy.https.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are sent for successful requests + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include an + // include_subdomains NEL policy. + await fetchResourceWithIncludeSubdomainsPolicy(); + // That policy should apply to the request that delivered it. Even though + // the policy has include_subdomains set, it SHOULD generate a full, + // non-downgraded report about the request, since the request has the + // same origin as the policy. (I.e., since the origins are the same, the + // include_subdomains setting is irrelevant.) + assert_true(await reportExists({ + url: getURLForResourceWithIncludeSubdomainsPolicy(), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 200, + phase: "application", + type: "ok", + }, + metadata: { + content_type: "application/reports+json", + }, + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/sends-report-on-success.https.html b/testing/web-platform/tests/network-error-logging/sends-report-on-success.https.html new file mode 100644 index 0000000000..68fddaa0c7 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/sends-report-on-success.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <title> + Test that NEL reports are sent for successful requests + </title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='./support/nel.sub.js'></script> +</head> +<body> + <script> + nel_test(async t => { + // Make a request to a resource whose response headers include a NEL + // policy. + await fetchResourceWithBasicPolicy(); + // That policy should apply to the request that delivered it, so the + // collector should have received a report about the request. + assert_true(await reportExists({ + url: getURLForResourceWithBasicPolicy(), + user_agent: navigator.userAgent, + type: "network-error", + body: { + method: "GET", + sampling_fraction: 1.0, + status_code: 200, + phase: "application", + type: "ok", + }, + metadata: { + content_type: "application/reports+json", + }, + })); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/network-error-logging/support/cached-for-one-minute.png b/testing/web-platform/tests/network-error-logging/support/cached-for-one-minute.png Binary files differnew file mode 100644 index 0000000000..2fa1e0ac06 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/cached-for-one-minute.png diff --git a/testing/web-platform/tests/network-error-logging/support/cached-for-one-minute.png.sub.headers b/testing/web-platform/tests/network-error-logging/support/cached-for-one-minute.png.sub.headers new file mode 100644 index 0000000000..63d4c4de03 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/cached-for-one-minute.png.sub.headers @@ -0,0 +1 @@ +Cache-Control: public, max-age=60, must-revalidate diff --git a/testing/web-platform/tests/network-error-logging/support/cached-with-validation.py b/testing/web-platform/tests/network-error-logging/support/cached-with-validation.py new file mode 100644 index 0000000000..e5090e0392 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/cached-with-validation.py @@ -0,0 +1,17 @@ +ETAG = b'"123abc"' +CONTENT_TYPE = b"text/plain" +CONTENT = u"lorem ipsum dolor sit amet" + +def main(request, response): + # let caching kick in if possible (conditional GET) + etag = request.headers.get(b"If-None-Match", None) + if etag == ETAG: + response.headers.set(b"X-HTTP-STATUS", 304) + response.status = (304, b"Not Modified") + return u"" + + # cache miss, so respond with the actual content + response.status = (200, b"OK") + response.headers.set(b"ETag", ETAG) + response.headers.set(b"Content-Type", CONTENT_TYPE) + return CONTENT diff --git a/testing/web-platform/tests/network-error-logging/support/clear-policy-pass.png b/testing/web-platform/tests/network-error-logging/support/clear-policy-pass.png Binary files differnew file mode 100644 index 0000000000..2fa1e0ac06 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/clear-policy-pass.png diff --git a/testing/web-platform/tests/network-error-logging/support/clear-policy-pass.png.sub.headers b/testing/web-platform/tests/network-error-logging/support/clear-policy-pass.png.sub.headers new file mode 100644 index 0000000000..1085b8a987 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/clear-policy-pass.png.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Report-To: { "group": "nel-group", "max_age": 0, "endpoints": [] } +NEL: {"max_age": 0} diff --git a/testing/web-platform/tests/network-error-logging/support/lock.py b/testing/web-platform/tests/network-error-logging/support/lock.py new file mode 100644 index 0000000000..0c5ae6b6f5 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/lock.py @@ -0,0 +1,48 @@ +# This file implements a shared lock that lets us ensure that the test cases in +# this directory run serially. Each test case obtains this lock as its first +# step, and releases it as its last. (The nel_test helper function in +# nel.sub.js automates this process.) Because the lock needs to be shared +# across all of the test cases, we use a hard-coded stash key. This hard-coded +# key is a random UUID, which should not conflict with any other auto-generated +# stash keys. + +import time + +_LOCK_KEY = b"67966d2e-a847-41d8-b7c3-5f6aee3375ba" +_TIMEOUT = 5 # seconds + +def wait_for_lock(request): + t0 = time.time() + while time.time() - t0 < _TIMEOUT: + time.sleep(0.5) + value = request.server.stash.take(key=_LOCK_KEY) + if value is None: + return True + return False + +def lock(request, report_id): + with request.server.stash.lock: + # Loop until the lock is free + if not wait_for_lock(request): + return (503, [], b"Cannot obtain lock") + request.server.stash.put(key=_LOCK_KEY, value=report_id) + return b"Obtained lock for %s" % report_id + +def unlock(request, report_id): + with request.server.stash.lock: + lock_holder = request.server.stash.take(key=_LOCK_KEY) + if lock_holder != report_id: + # Return the lock holder to the stash + request.server.stash.put(key=_LOCK_KEY, value=lock_holder) + return (503, [], b"Cannot release lock held by %s" % lock_holder) + return b"Released lock for %s" % report_id + +def main(request, response): + op = request.GET.first(b"op") + report_id = request.GET.first(b"reportID") + if op == b"lock": + return lock(request, report_id) + elif op == b"unlock": + return unlock(request, report_id) + else: + return (400, [], b"Invalid op") diff --git a/testing/web-platform/tests/network-error-logging/support/nel.sub.js b/testing/web-platform/tests/network-error-logging/support/nel.sub.js new file mode 100644 index 0000000000..856af10cf7 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/nel.sub.js @@ -0,0 +1,293 @@ +const reportID = "{{$id:uuid()}}"; + +/* + * NEL tests have to run serially, since the user agent maintains a global cache + * of Reporting and NEL policies, and we don't want the policies for multiple + * tests to interfere with each other. These functions (along with a Python + * handler in lock.py) implement a simple spin lock. + */ + +function obtainNELLock() { + return fetch("/network-error-logging/support/lock.py?op=lock&reportID=" + reportID); +} + +function releaseNELLock() { + return fetch("/network-error-logging/support/lock.py?op=unlock&reportID=" + reportID); +} + +function nel_test(callback, name, properties) { + promise_test(async t => { + await obtainNELLock(); + await clearReportingAndNELConfigurations(); + await callback(t); + await releaseNELLock(); + }, name, properties); +} + +function nel_iframe_test(callback, name, properties) { + promise_test(async t => { + await obtainNELLock(); + await clearReportingAndNELConfigurationsInIframe(); + await callback(t); + await releaseNELLock(); + }, name, properties); +} + +/* + * Helper functions for constructing domain names that contain NEL policies. + */ +function _monitoredDomain(subdomain) { + if (subdomain == "www") { + return "{{hosts[alt][www]}}" + } else if (subdomain == "www1") { + return "{{hosts[alt][www1]}}" + } else if (subdomain == "www2") { + return "{{hosts[alt][www2]}}" + } else if (subdomain == "nonexistent") { + return "{{hosts[alt][nonexistent]}}" + } else { + return "{{hosts[alt][]}}" + } +} + +function _getNELResourceURL(subdomain, suffix) { + return "https://" + _monitoredDomain(subdomain) + + ":{{ports[https][0]}}/network-error-logging/support/" + suffix; +} + +/* + * Fetches a resource whose headers define a basic NEL policy (i.e., with no + * include_subdomains flag). We ensure that we request the resource from a + * different origin than is used for the main test case HTML file or for report + * uploads. This minimizes the number of reports that are generated for this + * policy. + */ + +function getURLForResourceWithBasicPolicy(subdomain) { + return _getNELResourceURL(subdomain, "pass.png?id="+reportID+"&success_fraction=1.0"); +} + +function fetchResourceWithBasicPolicy(subdomain) { + const url = getURLForResourceWithBasicPolicy(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +function fetchResourceWithZeroSuccessFractionPolicy(subdomain) { + const url = _getNELResourceURL(subdomain, "pass.png?id="+reportID+"&success_fraction=0.0"); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Similar to the above methods, but fetch resources in an iframe. Allows matching + * full context of reports sent from an iframe that's same-site relative to the domains + * a policy set. + */ + + function loadResourceWithBasicPolicyInIframe(subdomain) { + return loadResourceWithPolicyInIframe( + getURLForResourceWithBasicPolicy(subdomain)); +} + +function loadResourceWithZeroSuccessFractionPolicyInIframe(subdomain) { + return loadResourceWithPolicyInIframe( + _getNELResourceURL(subdomain, "pass.png?id="+reportID+"&success_fraction=0.0")); +} + +function clearResourceWithBasicPolicyInIframe(subdomain) { + return loadResourceWithPolicyInIframe( + getURLForClearingConfiguration(subdomain)); +} + +function loadResourceWithPolicyInIframe(url) { + return new Promise((resolve, reject) => { + const frame = document.createElement('iframe'); + frame.src = url; + frame.onload = () => resolve(frame); + frame.onerror = () => reject('failed to load ' + url); + document.body.appendChild(frame); + }); +} + +/* + * Fetches a resource whose headers define an include_subdomains NEL policy. + */ + +function getURLForResourceWithIncludeSubdomainsPolicy(subdomain) { + return _getNELResourceURL(subdomain, "subdomains-pass.png?id="+reportID); +} + +function fetchResourceWithIncludeSubdomainsPolicy(subdomain) { + const url = getURLForResourceWithIncludeSubdomainsPolicy(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Fetches a resource whose headers do NOT define a NEL policy. This may or may + * not generate a NEL report, depending on whether you've already successfully + * requested a resource from the same origin that included a NEL policy. + */ + +function getURLForResourceWithNoPolicy(subdomain) { + return _getNELResourceURL(subdomain, "no-policy-pass.png"); +} + +function fetchResourceWithNoPolicy(subdomain) { + const url = getURLForResourceWithNoPolicy(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Fetches a resource that doesn't exist. This may or may not generate a NEL + * report, depending on whether you've already successfully requested a resource + * from the same origin that included a NEL policy. + */ + +function getURLForMissingResource(subdomain) { + return _getNELResourceURL(subdomain, "nonexistent.png"); +} + +function fetchMissingResource(subdomain) { + const url = getURLForMissingResource(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Fetches a resource that can be cached without validation. + */ + +function getURLForCachedResource(subdomain) { + return _getNELResourceURL(subdomain, "cached-for-one-minute.png"); +} + +function fetchCachedResource(subdomain) { + const url = getURLForCachedResource(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Fetches a resource that can be cached but requires validation. + */ + +function getURLForValidatedCachedResource(subdomain) { + return _getNELResourceURL(subdomain, "cached-with-validation.py"); +} + +function fetchValidatedCachedResource(subdomain) { + const url = getURLForValidatedCachedResource(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Fetches a resource that redirects once before returning a successful + * response. + */ + +function getURLForRedirectedResource(subdomain) { + return _getNELResourceURL(subdomain, "redirect.py?id="+reportID); +} + +function fetchRedirectedResource(subdomain) { + const url = getURLForRedirectedResource(subdomain); + return fetch(url, {mode: "no-cors"}); +} + +/* + * Fetches resources that clear out any existing Reporting or NEL configurations + * for all origins that any test case might use. + */ + +function getURLForClearingConfiguration(subdomain) { + return _getNELResourceURL(subdomain, "clear-policy-pass.png?id="+reportID); +} + +async function clearReportingAndNELConfigurations(subdomain) { + await Promise.all([ + fetch(getURLForClearingConfiguration(""), {mode: "no-cors"}), + fetch(getURLForClearingConfiguration("www"), {mode: "no-cors"}), + fetch(getURLForClearingConfiguration("www1"), {mode: "no-cors"}), + fetch(getURLForClearingConfiguration("www2"), {mode: "no-cors"}), + ]); + return; +} + +async function clearReportingAndNELConfigurationsInIframe(subdomain) { + await Promise.all([ + clearResourceWithBasicPolicyInIframe(""), + clearResourceWithBasicPolicyInIframe("www"), + clearResourceWithBasicPolicyInIframe("www1"), + clearResourceWithBasicPolicyInIframe("www2"), + ]); + return; +} + +/* + * Returns whether all of the fields in obj1 also exist in obj2 with the same + * values. (Put another way, returns whether obj1 and obj2 are equal, ignoring + * any extra fields in obj2.) + */ + +function _isSubsetOf(obj1, obj2) { + for (const prop in obj1) { + if (typeof obj1[prop] === 'object') { + if (typeof obj2[prop] !== 'object') { + return false; + } + if (!_isSubsetOf(obj1[prop], obj2[prop])) { + return false; + } + } else if (obj1[prop] != obj2[prop]) { + return false; + } + } + return true; +} + +/* + * Verifies that a report was uploaded that contains all of the fields in + * expected. + */ + +async function reportExists(expected, retain_reports) { + var timeout = + document.querySelector("meta[name=timeout][content=long]") ? 50 : 1; + var reportLocation = + "/reporting/resources/report.py?op=retrieve_report&timeout=" + + timeout + "&reportID=" + reportID; + if (retain_reports) + reportLocation += "&retain=1"; + const response = await fetch(reportLocation); + const json = await response.json(); + for (const report of json) { + if (_isSubsetOf(expected, report)) { + return true; + } + } + return false; +} + +/* + * Verifies that reports were uploaded that contains all of the fields in + * expected. + */ + +async function reportsExist(expected_reports, retain_reports) { + const timeout = 10; + let reportLocation = + "/reporting/resources/report.py?op=retrieve_report&timeout=" + + timeout + "&reportID=" + reportID; + if (retain_reports) + reportLocation += "&retain"; + // There must be the report of pass.png, so adding 1. + const min_count = expected_reports.length + 1; + reportLocation += "&min_count=" + min_count; + const response = await fetch(reportLocation); + const json = await response.json(); + for (const expected of expected_reports) { + const found = json.some((report) => { + return _isSubsetOf(expected, report); + }); + if (!found) + return false; + } + return true; +} diff --git a/testing/web-platform/tests/network-error-logging/support/no-policy-pass.png b/testing/web-platform/tests/network-error-logging/support/no-policy-pass.png Binary files differnew file mode 100644 index 0000000000..2fa1e0ac06 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/no-policy-pass.png diff --git a/testing/web-platform/tests/network-error-logging/support/pass.png b/testing/web-platform/tests/network-error-logging/support/pass.png Binary files differnew file mode 100644 index 0000000000..2fa1e0ac06 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/pass.png diff --git a/testing/web-platform/tests/network-error-logging/support/pass.png.sub.headers b/testing/web-platform/tests/network-error-logging/support/pass.png.sub.headers new file mode 100644 index 0000000000..4cf91f1359 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/pass.png.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Report-To: { "group": "nel-group", "max_age": 10886400, "endpoints": [{ "url": "https://{{hosts[][www]}}:{{ports[https][0]}}/reporting/resources/report.py?op=put&reportID={{GET[id]}}" }] } +NEL: {"report_to": "nel-group", "max_age": 10886400, "success_fraction": {{GET[success_fraction]}}} diff --git a/testing/web-platform/tests/network-error-logging/support/redirect.py b/testing/web-platform/tests/network-error-logging/support/redirect.py new file mode 100644 index 0000000000..7da4504764 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/redirect.py @@ -0,0 +1,3 @@ +# Always redirects to no-policy-pass.png. +def main(request, response): + return 302, [(b"Location", b"no-policy-pass.png")], u"" diff --git a/testing/web-platform/tests/network-error-logging/support/subdomains-pass.png b/testing/web-platform/tests/network-error-logging/support/subdomains-pass.png Binary files differnew file mode 100644 index 0000000000..2fa1e0ac06 --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/subdomains-pass.png diff --git a/testing/web-platform/tests/network-error-logging/support/subdomains-pass.png.sub.headers b/testing/web-platform/tests/network-error-logging/support/subdomains-pass.png.sub.headers new file mode 100644 index 0000000000..ae6ce3f43c --- /dev/null +++ b/testing/web-platform/tests/network-error-logging/support/subdomains-pass.png.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Report-To: { "group": "nel-group", "max_age": 10886400, "include_subdomains": true, "endpoints": [{ "url": "https://{{hosts[][www]}}:{{ports[https][0]}}/reporting/resources/report.py?op=put&reportID={{GET[id]}}" }] } +NEL: {"report_to": "nel-group", "max_age": 10886400, "include_subdomains": true, "success_fraction": 1.0} |