Files
ladybird/Tests/LibWeb/Text/input/cookie.html
Timothy Flynn fce003a8f5 LibWeb+LibWebView: Implement the latest cookie draft RFC
We currently implement the official cookie RFC, which was last updated
in 2011. Unfortunately, web reality conflicts with the RFC. For example,
all of the major browsers allow nameless cookies, which the RFC forbids.

There has since been draft versions of the RFC published to address such
issues. This patch implements the latest draft.

Major differences include:
* Allowing nameless or valueless (but not both) cookies
* Formal cookie length limits
* Formal same-site rules (not fully implemented here)
* More rules around cookie domains
2024-09-17 00:04:33 +01:00

204 lines
5.7 KiB
HTML

<div id="test"></div>
<script src="include.js"></script>
<script>
const printCookies = label => {
// There's no specified order for multiple cookies, and it's a bit non-deterministic in our implementation.
// Sort the cookies alphabetically here for ease of testing.
const cookies = document.cookie.split("; ").sort().join("; ");
println(`${label}: "${cookies}"`);
};
const deleteCookie = name => {
document.cookie = `${name}=""; max-age=-9999`;
};
const basicTest = () => {
document.cookie = "cookie=value";
printCookies("Basic test");
deleteCookie("cookie");
};
const multipleCookiesTest = () => {
document.cookie = "cookie1=value1";
document.cookie = "cookie2=value2";
document.cookie = "cookie3=value3";
printCookies("Multiple cookies");
deleteCookie("cookie1");
deleteCookie("cookie2");
deleteCookie("cookie3");
};
const namelessCookieTest = () => {
document.cookie = "=value";
printCookies("Nameless cookie");
deleteCookie("");
};
const valuelessCookieTest = () => {
document.cookie = "cookie=";
printCookies("Valueless cookie");
deleteCookie("cookie");
};
const namelessAndValuelessCookieTest = () => {
document.cookie = "=";
printCookies("Nameless and valueless cookie");
};
const invalidControlCharacterTest = () => {
document.cookie = "cookie=ab\ncd";
printCookies("Invalid control character");
};
const nonASCIIDomainTest = () => {
document.cookie = "cookie=value; domain=🤓";
printCookies("Non-ASCII domain");
};
const secureCookiePrefixTest = () => {
document.cookie = "__Secure-cookie=value";
printCookies("Secure cookie prefix");
};
const hostCookiePrefixTest = () => {
document.cookie = "__Host-cookie1=value";
document.cookie = "__Host-cookie2=value; secure";
document.cookie = "__Host-cookie3=value; secure; path=/foo";
printCookies("Host cookie prefix");
};
const largeValueTest = () => {
const value = "x".repeat(256);
document.cookie = `cookie=${value}`;
printCookies("Large value");
deleteCookie("cookie");
};
const overlyLargeValueTest = () => {
const value = "x".repeat(4096 - "cookie".length + 1);
document.cookie = `cookie=${value}`;
printCookies("Overly large value");
};
const httpOnlyTest = () => {
document.cookie = "cookie=value; httponly";
printCookies("HTTP only");
};
const publicSuffixTest = () => {
document.cookie = "cookie=value; domain=uk.gov";
printCookies("Public suffix");
};
const sameSiteTest = () => {
document.cookie = "cookie=value; SameSite=Lax";
printCookies("SameSite=Lax");
deleteCookie("cookie");
document.cookie = "cookie=value; SameSite=Strict";
printCookies("SameSite=Strict");
deleteCookie("cookie");
document.cookie = "cookie=value; SameSite=None";
printCookies("SameSite=None");
deleteCookie("cookie");
};
const maxAgeTest1 = () => {
document.cookie = "cookie-max-age=value; max-age=1";
printCookies("Max-Age (before expiration)");
};
const maxAgeTest2 = () => {
printCookies("Max-Age (after expiration)");
};
const maxAgeInPastTest = () => {
document.cookie = "cookie=value; max-age=-1";
printCookies("Max-Age in past");
};
const expiresTest1 = () => {
let expiry = new Date(Date.now() + 1000);
expiry = expiry.toUTCString();
document.cookie = `cookie-expires=value; expires=${expiry}`;
printCookies("Expires (before expiration)");
};
const expiresTest2 = () => {
printCookies("Expires (after expiration)");
};
const expiresInPastTest = () => {
document.cookie = "cookie=value; expires=Mon, 23 Jan 1989 08:10:36 GMT";
printCookies("Expires in past");
};
// Note that in these cases, the attribute is simply ignored, rather than the cookie being rejected.
const invalidExpiryTest = () => {
document.cookie = "cookie=value; expires=Sat, 31 Feb 2060 08:10:36 GMT";
printCookies("Invalid expiry (date does not exist)");
deleteCookie("cookie");
document.cookie = "cookie=value; expires=Sat, 31 Feb 2060 GMT";
printCookies("Invalid expiry (missing time)");
deleteCookie("cookie");
document.cookie = "cookie=value; expires=Sat, Feb 2060 08:10:36 GMT";
printCookies("Invalid expiry (missing day)");
deleteCookie("cookie");
document.cookie = "cookie=value; expires=Sat, 31 2060 08:10:36 GMT";
printCookies("Invalid expiry (missing month)");
deleteCookie("cookie");
document.cookie = "cookie=value; expires=Sat, 31 Feb 08:10:36 GMT";
printCookies("Invalid expiry (missing year)");
deleteCookie("cookie");
};
asyncTest(done => {
basicTest();
multipleCookiesTest();
namelessCookieTest();
valuelessCookieTest();
namelessAndValuelessCookieTest();
invalidControlCharacterTest();
nonASCIIDomainTest();
secureCookiePrefixTest();
hostCookiePrefixTest();
largeValueTest();
overlyLargeValueTest();
httpOnlyTest();
publicSuffixTest();
sameSiteTest();
maxAgeTest1();
expiresTest1();
setTimeout(() => {
maxAgeTest2();
expiresTest2();
maxAgeInPastTest();
expiresInPastTest();
invalidExpiryTest();
done();
}, 1200);
});
</script>