Tests: Import a WPT test for cookie domain processing

This commit is contained in:
Shannon Booth 2026-06-10 22:32:29 +02:00 committed by Shannon Booth
parent 790b9bd36a
commit 2c33de1583
13 changed files with 831 additions and 1 deletions

View file

@ -1,6 +1,7 @@
[LoadFromHttpServer]
; Cookies require HTTP(s) scheme.
; Tests cookies.
Text/input/cookie-working.html
Text/input/wpt-import/cookies/attributes/domain.sub.html
; Performs cross-origin Worker fetch
Text/input/wpt-import/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html

View file

@ -0,0 +1,61 @@
Harness status: OK
Found 55 tests
53 Pass
2 Fail
Pass Test cookie domain attribute parsing
Pass Return cookie for a domain match
Pass No cookie returned for domain mismatch (subdomains differ post-redirect)
Pass Return cookie for a domain match with leading '.'
Pass Return cookie for domain match (domain attribute is suffix of the host name and first level subdomain)
Pass Return cookie for domain match (domain attribute is suffix of the host name and first level subdomain, with leading '.')
Pass No cookie returned for domain mismatch (subdomains differ, with leading '.')
Pass No cookie returned for domain mismatch when cookie was created (which would match after the redirect, with one subdomain level)
Pass Return cookie for domain match (domain attribute is suffix of the host name, with leading '.')
Pass Return cookie for domain match (domain attribute is suffix of the host name)
Pass No cookie returned for domain attribute with double leading '.'
Pass No cookie returned for domain attribute with subdomain followed by ..
Pass Return cookie for a domain match with leading whitespace and '.'
Pass No cookie returned for domain attribute with whitespace that surrounds a leading '.'
Pass No cookie returned for domain attribute with trailing '.'
Pass No cookie returned for domain attribute with trailing '..'
Pass No cookie returned for domain attribute with trailing whitespace and '.'
Fail No cookie returned for domain attribute with TLD as value
Pass No cookie returned for domain attribute with TLD as value, with leading '.'
Pass No cookie returned for domain attribute with TLD as value, with leading and trailing '.'
Pass Return multiple cookies that match on domain (without and with leading '.')
Pass Return multiple cookies that match on domain (with and without leading '.')
Pass No cookie returned for domain attribute value between quotes
Pass Return multiple cookies that match on subdomain and domain (without and with leading '.')
Pass Return multiple cookies that match on domain and subdomain (with and without leading '.')
Pass No cookie returned when domain attribute does not domain-match (and first does)
Pass Return cookie for domain attribute match (first does not, but second does)
Pass Return cookie for domain match (with bizarre capitalization for domain attribute value)
Pass No cookie returned for domain attribute value with port
Pass No cookie returned for domain mismatch when cookie was created (which would match after the redirect, with two subdomain levels)
Pass No cookie returned for cookie set on different domain (with no domain attribute)
Pass Return cookie set with bare domain= attribute
Pass Return cookie that domain-matches with bizarre-cased URL
Pass No cookie returned for domain attribute mismatch (first attribute matches, but second does not)
Pass Return cookie for domain match (first attribute doesn't, but second does)
Pass Return cookie for domain match (first attribute matches, second doesn't, third does)
Pass No cookie returned for domain attribute mismatch (first attribute doesn't, second does, third doesn't)
Pass Return cookie for domain match (with two identical domain attributes)
Pass Return cookie for domain match (with first domain attribute a match for host name and second as suffix of host name)
Pass Return cookie for domain match (with first domain attribute as suffix of host name and second a match for host name)
Pass No cookie set on domain mismatch before a (domain matching) redirect
Pass No cookie set on domain mismatch before a (domain matching) redirect (for second level subdomain)
Pass Return cookie for domain match (with first domain attribute as suffix of host name and second a bare attribute)
Pass Cookie returned for bare domain attribute following mismatched domain attribute (after redirect to same-origin page).
Pass No cookie returned for domain mismatch (first attribute is a different subdomain and second is bare)
Pass Cookies with same name, path, and domain (differing only in leading '.') overwrite each other ('.' second)
Pass Cookies with same name, path, and domain (differing only in leading '.') overwrite each other ('.' first)
Fail No cookie returned for domain with single dot ('.') value.
Pass Return cookie with valid domain after domain with single dot ('.') value.
Pass Empty domain treated as host cookie 1
Pass Empty domain treated as host cookie 2
Pass Empty domain treated as host cookie 3
Pass No host cookies returned for host cookies after non-host redirect 1
Pass No host cookies returned for host cookies after non-host redirect 2
Pass No host cookies returned for host cookies after non-host redirect 3

View file

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Test cookie domain attribute parsing</title>
<meta name=help href="https://tools.ietf.org/html/rfc6265#section-5.2.3">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
</head>
<body>
<script>
const port = "{{ports[http][0]}}";
const wwwHost = "{{domains[www]}}";
test(t => {
const win = window.open(`http://${wwwHost}:${port}/cookies/attributes/resources/domain-child.sub.html`);
fetch_tests_from_window(win);
});
</script>
</body>
</html>

View file

@ -0,0 +1,401 @@
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Test cookie domain attribute parsing</title>
<meta name=help href="https://tools.ietf.org/html/rfc6265#section-5.2.3">
<meta name="timeout" content="long">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testdriver.js"></script>
<script src="../../../resources/testdriver-vendor.js"></script>
<script src="../../../cookies/resources/cookie-test.js"></script>
</head>
<body>
<script>
const path = "path=/cookies/attributes"
const port = "{{ports[http][0]}}";
const host = "{{host}}"; // example.org
const wwwHost = "{{domains[www]}}"; // home.example.org
const www1Host = "{{domains[www1]}}"; // sibling.example.org
const www2wwwHost = "{{domains[www2.www]}}"; // subdomain.home.example.org
// naive helper method to return the TLD for a given domain
const getTLD = domain => {
let match = /\.[a-z]+$/.exec(domain);
if (match) {
return match[0];
} else {
throw 'Domain is malformed!';
}
}
// helper to take a domain like "www.example.org"
// and return a string like "www.eXaMpLe.org"
const makeBizarre = domain => {
let bizarre = "";
let domainArray = domain.split(".");
let secondLevel = domainArray[domainArray.length - 2];
for (let i in secondLevel) {
if (i % 2 == 1) {
bizarre += secondLevel[i].toUpperCase();
} else {
bizarre += secondLevel[i];
}
}
domainArray[domainArray.length - 2] = bizarre;
return domainArray.join(".");
}
// helper to change the current TLD to a TLD that doesn't exist, and is
// unlikely to exist in the future. (the main point is that the TLD
// *changes*, so there is no domain match, but we cant' predict how WPT
// servers may be set up in the wild so picking any valid TLD has the risk
// of future (unintentional) domain matching.
const changeTLD = domain => {
let domainArray = domain.split(".");
domainArray[domainArray.length - 1] += "zzz";
return domainArray.join(".");
}
const domainTests = [
{
cookie: `test=1; domain=${wwwHost}`,
expected: "test=1",
name: "Return cookie for a domain match",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=2; domain=${wwwHost}`,
expected: "",
name: "No cookie returned for domain mismatch (subdomains differ post-redirect)",
location: `http://${www1Host}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=3; domain=.${wwwHost}`,
expected: "test=3",
name: "Return cookie for a domain match with leading '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=4; domain=${wwwHost}`,
expected: "test=4",
name: "Return cookie for domain match (domain attribute is suffix of the host name and first level subdomain)",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=5; domain=.${wwwHost}`,
expected: "test=5",
name: "Return cookie for domain match (domain attribute is suffix of the host name and first level subdomain, with leading '.')",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=6; domain=.${wwwHost}`,
expected: "",
name: "No cookie returned for domain mismatch (subdomains differ, with leading '.')",
location: `http://${www1Host}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=7; domain=${www1Host}`,
expected: "",
name: "No cookie returned for domain mismatch when cookie was created (which would match after the redirect, with one subdomain level)",
location: `http://${www1Host}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=8; domain=.${host}`,
expected: "test=8",
name: "Return cookie for domain match (domain attribute is suffix of the host name, with leading '.')",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=9; domain=${host}`,
expected: "test=9",
name: "Return cookie for domain match (domain attribute is suffix of the host name)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=10; domain=..${wwwHost}`,
expected: "",
name: "No cookie returned for domain attribute with double leading '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=11; domain=www..${host}`,
expected: "",
name: "No cookie returned for domain attribute with subdomain followed by ..",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=12; domain= .${wwwHost}`,
expected: "test=12",
name: "Return cookie for a domain match with leading whitespace and '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=13; domain= . ${wwwHost}`,
expected: "",
name: "No cookie returned for domain attribute with whitespace that surrounds a leading '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=14; domain=${wwwHost}.`,
expected: "",
name: "No cookie returned for domain attribute with trailing '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=15; domain=${wwwHost}..`,
expected: "",
name: "No cookie returned for domain attribute with trailing '..'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=16; domain=${wwwHost} .`,
expected: "",
name: "No cookie returned for domain attribute with trailing whitespace and '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=17; domain=${getTLD(host)}`,
expected: "",
name: "No cookie returned for domain attribute with TLD as value",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=18; domain=.${getTLD(host)}`,
expected: "",
name: "No cookie returned for domain attribute with TLD as value, with leading '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=18b; domain=.${getTLD(host)}.`,
expected: "",
name: "No cookie returned for domain attribute with TLD as value, with leading and trailing '.'",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: [`testA=19; domain=${wwwHost}`, `testB=19; domain=.${wwwHost}`],
expected: "testA=19; testB=19",
name: "Return multiple cookies that match on domain (without and with leading '.')",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: [`testB=20; domain=.${wwwHost}`, `testA=20; domain=${wwwHost}`],
expected: "testB=20; testA=20",
name: "Return multiple cookies that match on domain (with and without leading '.')",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=21; domain="${wwwHost}"`,
expected: "",
name: "No cookie returned for domain attribute value between quotes",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: [`testA=22; domain=${wwwHost}`, `testB=22; domain=.${host}`],
expected: "testA=22; testB=22",
name: "Return multiple cookies that match on subdomain and domain (without and with leading '.')",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: [`testB=23; domain=.${host}`, `testA=23; domain=${wwwHost}`],
expected: "testB=23; testA=23",
name: "Return multiple cookies that match on domain and subdomain (with and without leading '.')",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=24; domain=.${host}; domain=${wwwHost}`,
expected: "",
name: "No cookie returned when domain attribute does not domain-match (and first does)",
location: `http://${www1Host}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=25; domain=${wwwHost}; domain=.${host}`,
expected: "test=25",
name: "Return cookie for domain attribute match (first does not, but second does)",
location: `http://${www1Host}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=26; domain=${makeBizarre(wwwHost)}`,
expected: "test=26",
name: "Return cookie for domain match (with bizarre capitalization for domain attribute value)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=27; domain="${wwwHost}:${port}"`,
expected: "",
name: "No cookie returned for domain attribute value with port",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=28; domain=${www2wwwHost}`,
expected: "",
name: "No cookie returned for domain mismatch when cookie was created (which would match after the redirect, with two subdomain levels)",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=29`,
expected: "",
name: "No cookie returned for cookie set on different domain (with no domain attribute)",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: "test=30; domain=",
expected: "test=30",
name: "Return cookie set with bare domain= attribute",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=31; domain=${wwwHost}`,
expected: "test=31",
name: "Return cookie that domain-matches with bizarre-cased URL",
location: `http://${makeBizarre(wwwHost)}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=32; domain=${wwwHost}; domain=${changeTLD(wwwHost)}`,
expected: "",
name: "No cookie returned for domain attribute mismatch (first attribute matches, but second does not)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=33; domain=${changeTLD(wwwHost)}; domain=${wwwHost}`,
expected: "test=33",
name: "Return cookie for domain match (first attribute doesn't, but second does)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=34; domain=${wwwHost}; domain=${changeTLD(wwwHost)}; domain=${wwwHost}`,
expected: "test=34",
name: "Return cookie for domain match (first attribute matches, second doesn't, third does)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=35; domain=${changeTLD(wwwHost)}; domain=${wwwHost}; domain=${changeTLD(wwwHost)}`,
expected: "",
name: "No cookie returned for domain attribute mismatch (first attribute doesn't, second does, third doesn't)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=36; domain=${wwwHost}; domain=${wwwHost}`,
expected: "test=36",
name: "Return cookie for domain match (with two identical domain attributes)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=37; domain=${wwwHost}; domain=${host}`,
expected: "test=37",
name: "Return cookie for domain match (with first domain attribute a match for host name and second as suffix of host name)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=38; domain=${host}; domain=${wwwHost}`,
expected: "test=38",
name: "Return cookie for domain match (with first domain attribute as suffix of host name and second a match for host name)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=39; domain=.${www1Host}`,
expected: "",
name: "No cookie set on domain mismatch before a (domain matching) redirect",
location: `http://${www1Host}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=40; domain=.${www2wwwHost}`,
expected: "",
name: "No cookie set on domain mismatch before a (domain matching) redirect (for second level subdomain)",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=41; domain=${host}; domain=`,
expected: "test=41",
name: "Return cookie for domain match (with first domain attribute as suffix of host name and second a bare attribute)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=42; domain=${www1Host}; domain=`,
expected: "test=42",
name: "Cookie returned for bare domain attribute following mismatched domain attribute (after redirect to same-origin page).",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=43; domain=${www1Host}; domain=`,
expected: "",
name: "No cookie returned for domain mismatch (first attribute is a different subdomain and second is bare)",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: [`test=not44; domain=${wwwHost}`, `test=44; domain=.${wwwHost}`],
expected: "test=44",
name: "Cookies with same name, path, and domain (differing only in leading '.') overwrite each other ('.' second)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: [`test=not45; domain=.${wwwHost}`, `test=45; domain=${wwwHost}`],
expected: "test=45",
name: "Cookies with same name, path, and domain (differing only in leading '.') overwrite each other ('.' first)",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=46; domain=.`,
expected: "",
name: "No cookie returned for domain with single dot ('.') value.",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: `test=46b; domain=.; domain=${host}`,
expected: "test=46b",
name: "Return cookie with valid domain after domain with single dot ('.') value.",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: ["test=47", `test=47b; domain=${host}`,`test=47b; domain=${www1Host}; domain=`],
expected: "test=47b; test=47b",
name: "Empty domain treated as host cookie 1",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: ["test=48", `test=48b; domain=${host}`,`test=48b; domain=${host}; domain=`],
expected: "test=48b; test=48b",
name: "Empty domain treated as host cookie 2",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: ["test=49", `test=49b; domain=${host}`,`test=49b; domain=`],
expected: "test=49b; test=49b",
name: "Empty domain treated as host cookie 3",
location: `http://${wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: ["test=50", `test=50b; domain=${host}`,`test=50b; domain=${www1Host}; domain=`],
expected: "test=50b",
name: "No host cookies returned for host cookies after non-host redirect 1",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: ["test=51", `test=51b; domain=${host}`,`test=51b; domain=${host}; domain=`],
expected: "test=51b",
name: "No host cookies returned for host cookies after non-host redirect 2",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
{
cookie: ["test=52", `test=52b; domain=${host}`,`test=52b; domain=`],
expected: "test=52b",
name: "No host cookies returned for host cookies after non-host redirect 3",
location: `http://${www2wwwHost}:${port}/cookies/attributes/resources/path.html`,
},
];
for (const test of domainTests) {
if (Array.isArray(test.cookie)) {
for (let i in test.cookie) {
test.cookie[i] += `; ${path}`;
}
} else {
test.cookie += `; ${path}`;
}
httpRedirectCookieTest(test.cookie, test.expected, test.name,
test.location);
}
</script>
</body>
</html>

View file

@ -0,0 +1,11 @@
// Note: this function has a dependency on testdriver.js. Any test files calling
// it should include testdriver.js and testdriver-vendor.js
window.addEventListener("message", (e) => {
setTestContextUsingRootWindow();
if (e.data == "getAndExpireCookiesForRedirectTest") {
const cookies = document.cookie;
test_driver.delete_all_cookies().then(() => {
e.source.postMessage({"cookies": cookies}, '*');
});
}
});

View file

@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>helper iframe for matching cookie path redirect tests</title>
<meta name=help href="http://tools.ietf.org/html/rfc6265#section-5.1.4">
</head>
<body>
<script src="../../../resources/testdriver.js"></script>
<script src="../../../resources/testdriver-vendor.js"></script>
<script src="../../../cookies/resources/cookie-test.js"></script>
<script src="../../../cookies/attributes/resources/path-redirect-shared.js"></script>
</body>
</html>

View file

@ -0,0 +1 @@
Access-Control-Allow-Origin: *

View file

@ -0,0 +1,185 @@
// getAndExpireCookiesForDefaultPathTest is a helper method to get and delete
// cookies using echo-cookie.html.
async function getAndExpireCookiesForDefaultPathTest() {
return new Promise((resolve, reject) => {
try {
const iframe = document.createElement('iframe');
iframe.style = 'display: none';
iframe.addEventListener('load', (e) => {
const win = e.target.contentWindow;
const iframeCookies = win.getCookies();
win.expireCookies().then(() => {
document.documentElement.removeChild(iframe);
resolve(iframeCookies);
});
}, {once: true});
iframe.src = '/cookies/resources/echo-cookie.html';
document.documentElement.appendChild(iframe);
} catch (e) {
reject(e);
}
});
}
// getAndExpireCookiesForRedirectTest is a helper method to get and delete
// cookies that were set from a Location header redirect.
async function getAndExpireCookiesForRedirectTest(location) {
return new Promise((resolve, reject) => {
try {
const iframe = document.createElement('iframe');
iframe.style = 'display: none';
const listener = (e) => {
if (typeof e.data == 'object' && 'cookies' in e.data) {
window.removeEventListener('message', listener);
document.documentElement.removeChild(iframe);
resolve(e.data.cookies);
}
};
window.addEventListener('message', listener);
iframe.addEventListener('load', (e) => {
e.target.contentWindow.postMessage('getAndExpireCookiesForRedirectTest', '*');
}, {once: true});
iframe.src = location;
document.documentElement.appendChild(iframe);
} catch (e) {
reject(e);
}
});
}
// httpCookieTest sets a `cookie` (via HTTP), then asserts it was or was not set
// via `expectedValue` (via the DOM). Then cleans it up (via test driver). Most
// tests do not set a Path attribute, so `defaultPath` defaults to true. If the
// cookie values are expected to cause the HTTP request or response to fail, the
// test can be made to pass when this happens via `allowFetchFailure`, which
// defaults to false.
//
// `cookie` may be a single cookie string, or an array of cookie strings, where
// the order of the array items represents the order of the Set-Cookie headers
// sent by the server.
//
// Note: this function has a dependency on testdriver.js. Any test files calling
// it should include testdriver.js and testdriver-vendor.js
function httpCookieTest(cookie, expectedValue, name, defaultPath = true,
allowFetchFailure = false) {
return promise_test((t) => {
var skipAssertions = false;
return new Promise(async (resolve, reject) => {
// The result is ignored as we're expiring cookies for cleaning here.
await getAndExpireCookiesForDefaultPathTest();
await test_driver.delete_all_cookies();
t.add_cleanup(test_driver.delete_all_cookies);
let encodedCookie = encodeURIComponent(JSON.stringify(cookie));
try {
await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`);
} catch {
if (allowFetchFailure) {
skipAssertions = true;
} else {
reject('Failed to fetch /cookies/resources/cookie.py');
}
}
let cookies = document.cookie;
if (defaultPath) {
// for the tests where a Path is set from the request-uri
// path, we need to go look for cookies in an iframe at that
// default path.
cookies = await getAndExpireCookiesForDefaultPathTest();
}
resolve(cookies);
}).then((cookies) => {
if (skipAssertions) {
return;
}
if (Boolean(expectedValue)) {
assert_equals(cookies, expectedValue, 'The cookie was set as expected.');
} else {
assert_equals(cookies, expectedValue, 'The cookie was rejected.');
}
});
}, name);
}
// This is a variation on httpCookieTest, where a redirect happens via
// the Location header and we check to see if cookies are sent via
// getRedirectedCookies
//
// Note: the locations targeted by this function have a dependency on
// path-redirect-shared.js and should be sure to include it.
function httpRedirectCookieTest(cookie, expectedValue, name, location) {
return promise_test(async (t) => {
// The result is ignored as we're expiring cookies for cleaning here.
await getAndExpireCookiesForRedirectTest(location);
const encodedCookie = encodeURIComponent(JSON.stringify(cookie));
const encodedLocation = encodeURIComponent(location);
const setParams = `?set=${encodedCookie}&location=${encodedLocation}`;
await fetch(`/cookies/resources/cookie.py${setParams}`);
// for the tests where a redirect happens, we need to head
// to that URI to get the cookies (and then delete them there)
const cookies = await getAndExpireCookiesForRedirectTest(location);
if (Boolean(expectedValue)) {
assert_equals(cookies, expectedValue, 'The cookie was set as expected.');
} else {
assert_equals(cookies, expectedValue, 'The cookie was rejected.');
}
}, name);
}
// Sets a `cookie` via the DOM, checks it against `expectedValue` via the DOM,
// then cleans it up via the DOM. This is needed in cases where going through
// HTTP headers may modify the cookie line (e.g. by stripping control
// characters).
//
// Note: this function has a dependency on testdriver.js. Any test files calling
// it should include testdriver.js and testdriver-vendor.js
function domCookieTest(cookie, expectedValue, name) {
return promise_test(async (t) => {
await test_driver.delete_all_cookies();
t.add_cleanup(test_driver.delete_all_cookies);
if (typeof cookie === "string") {
document.cookie = cookie;
} else if (Array.isArray(cookie)) {
for (const singlecookie of cookie) {
document.cookie = singlecookie;
}
} else {
throw new Error('Unexpected type passed into domCookieTest as cookie: ' + typeof cookie);
}
let cookies = document.cookie;
assert_equals(cookies, expectedValue, Boolean(expectedValue) ?
'The cookie was set as expected.' :
'The cookie was rejected.');
}, name);
}
// Returns an array of control characters along with their ASCII codes. Control
// characters are defined by RFC 5234 to be %x00-1F / %x7F.
function getCtlCharacters() {
const ctlCodes = [...Array(0x20).keys()]
.concat([0x7F]);
return ctlCodes.map(i => ({ code: i, chr: String.fromCharCode(i) }))
}
// Returns a cookie string with name set to "t" * nameLength and value
// set to "1" * valueLength. Passing in 0 for either allows for creating
// a name- or value-less cookie.
//
// Note: Cookie length checking should ignore the "=".
function cookieStringWithNameAndValueLengths(nameLength, valueLength) {
return `${"t".repeat(nameLength)}=${"1".repeat(valueLength)}`;
}
// Finds the root window.top.opener and directs test_driver commands to it.
//
// If you see a message like: "Error: Tried to run in a non-testharness window
// without a call to set_test_context." then you probably need to call this.
function setTestContextUsingRootWindow() {
let test_window = window.top;
while (test_window.opener && !test_window.opener.closed) {
test_window = test_window.opener.top;
}
test_driver.set_test_context(test_window);
}

View file

@ -0,0 +1,42 @@
import json
from cookies.resources.helpers import setNoCacheAndCORSHeaders
from wptserve.utils import isomorphic_decode
from wptserve.utils import isomorphic_encode
def set_cookie(headers, cookie_string):
"""Helper method to add a Set-Cookie header"""
headers.append((b'Set-Cookie', isomorphic_encode(cookie_string)))
def main(request, response):
"""Set a cookie via GET params.
Usage: `/cookie.py?set={cookie}`
The passed-in cookie string should be stringified via JSON.stringify() (in
the case of multiple cookie headers sent in an array) and encoded via
encodeURIComponent, otherwise `parse_qsl` will split on any semicolons
(used by the Request.GET property getter). Note that values returned by
Request.GET will decode any percent-encoded sequences sent in a GET param
(which may or may not be surprising depending on what you're doing).
Note: here we don't use Response.delete_cookie() or similar other methods
in this resources directory because there are edge cases that are impossible
to express via those APIs, namely a bare (`Path`) or empty Path (`Path=`)
attribute. Instead, we pipe through the entire cookie and append `max-age=0`
to it.
"""
headers = setNoCacheAndCORSHeaders(request, response)
if b'set' in request.GET:
cookie = isomorphic_decode(request.GET[b'set'])
cookie = json.loads(cookie)
cookies = cookie if isinstance(cookie, list) else [cookie]
for c in cookies:
set_cookie(headers, c)
if b'location' in request.GET:
headers.append((b'Location', request.GET[b'location']))
return 302, headers, b'{"redirect": true}'
return headers, b'{"success": true}'

View file

@ -0,0 +1,31 @@
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>helper iframe for matching cookie path tests</title>
<meta name=help href="http://tools.ietf.org/html/rfc6265#section-5.1.4">
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/cookies/resources/cookie-test.js"></script>
</head>
<body>
<script>
window.setCookie = function (name, path) {
document.cookie = name + '=1; Path=' + path + ';';
};
window.fetchCookieThen = function (name, path) {
return fetch("/cookies/resources/set-cookie.py?name=" + encodeURIComponent(name) + "&path=" + encodeURIComponent(path), {'credentials': 'include'});
};
window.isCookieSet = function (name, path) {
return document.cookie.match(name + '=1');
};
// Note: this function has a dependency on testdriver.js. Any test files calling
// it should include testdriver.js and testdriver-vendor.js
window.expireCookies = async () => {
setTestContextUsingRootWindow();
await test_driver.delete_all_cookies();
};
window.getCookies = () => document.cookie;
</script>
</body>
</html>

View file

@ -0,0 +1,59 @@
from urllib.parse import parse_qs
from wptserve.utils import isomorphic_encode
def setNoCacheAndCORSHeaders(request, response):
"""Set Cache-Control, CORS and Content-Type headers appropriate for the cookie tests."""
headers = [(b"Content-Type", b"application/json"),
(b"Access-Control-Allow-Credentials", b"true")]
origin = b"*"
if b"origin" in request.headers:
origin = request.headers[b"origin"]
headers.append((b"Access-Control-Allow-Origin", origin))
#headers.append(("Access-Control-Allow-Credentials", "true"))
headers.append((b"Cache-Control", b"no-cache"))
headers.append((b"Expires", b"Fri, 01 Jan 1990 00:00:00 GMT"))
return headers
def makeCookieHeader(name, value, otherAttrs):
"""Make a Set-Cookie header for a cookie with the name, value and attributes provided."""
def makeAV(a, v):
if None == v or b"" == v:
return a
if isinstance(v, int):
return b"%s=%i" % (a, v)
else:
return b"%s=%s" % (a, v)
# ensure cookie name is always first
attrs = [b"%s=%s" % (name, value)]
attrs.extend(makeAV(a, v) for (a, v) in otherAttrs.items())
return (b"Set-Cookie", b"; ".join((attrs)))
def makeDropCookie(name, secure):
attrs = {b"max-age": 0, b"path": b"/"}
if secure:
attrs[b"secure"] = b""
return makeCookieHeader(name, b"", attrs)
def readParameter(request, paramName, requireValue):
"""Read a parameter from the request. Raise if requireValue is set and the
parameter has an empty value or is not present."""
params = parse_qs(request.url_parts.query)
param = params[paramName][0].strip()
if len(param) == 0:
raise Exception(u"Empty or missing name parameter.")
return isomorphic_encode(param)
def readCookies(request):
"""Read the cookies from the client present in the request."""
cookies = {}
for key in request.cookies:
for cookie in request.cookies.get_list(key):
# do we care we'll clobber cookies here? If so, do we
# need to modify the test to take cookie names and value lists?
cookies[key] = cookie.value
return cookies