diff --git a/src/index.ts b/src/index.ts index 23fe8ea..a3b0974 100644 --- a/src/index.ts +++ b/src/index.ts @@ -208,6 +208,7 @@ export interface SetCookie { maxAge?: number; /** * Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.1). + * When serializing, the UTC year must be between 1601 and 9999 inclusive. * When no expiration is set, clients consider this a "non-persistent cookie" and delete it when the current session is over. * * The [cookie storage model specification](https://tools.ietf.org/html/rfc6265#section-5.3) states that if both `expires` and @@ -325,7 +326,12 @@ export function stringifySetCookie( } if (cookie.expires) { - if (!Number.isFinite(cookie.expires.valueOf())) { + const year = cookie.expires.getUTCFullYear(); + if ( + !Number.isFinite(cookie.expires.valueOf()) || + year < 1601 || + year > 9999 + ) { throw new TypeError(`option expires is invalid: ${cookie.expires}`); } diff --git a/src/stringify-set-cookie.spec.ts b/src/stringify-set-cookie.spec.ts index f4e9cd4..cd3da59 100644 --- a/src/stringify-set-cookie.spec.ts +++ b/src/stringify-set-cookie.spec.ts @@ -165,6 +165,28 @@ describe("cookie.stringifySetCookie", () => { ).toThrow(/option expires is invalid/); }); + it.each([-1, 0, 49, 50, 69, 99, 1600, 10000])( + "should reject expires with UTC year %s outside cookie bounds", + (year) => { + const expires = new Date(0); + expires.setUTCFullYear(year); + expect(() => + stringifySetCookie({ name: "foo", value: "bar", expires }), + ).toThrow(/option expires is invalid/); + }, + ); + + it.each([1601, 1970, 9999])( + "should preserve expires with supported UTC year %s", + (year) => { + const expires = new Date(0); + expires.setUTCFullYear(year); + expect(stringifySetCookie({ name: "foo", value: "bar", expires })).toBe( + `foo=bar; Expires=${expires.toUTCString()}`, + ); + }, + ); + it("should set expires to given date", () => { expect( stringifySetCookie({