diff options
Diffstat (limited to 'testing/web-platform/tests/network-error-logging/support/nel.sub.js')
-rw-r--r-- | testing/web-platform/tests/network-error-logging/support/nel.sub.js | 293 |
1 files changed, 293 insertions, 0 deletions
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; +} |