diff --git a/api/.gitignore b/api/.gitignore index bed7e484..dd1e2b57 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -3,3 +3,4 @@ docs/*.md # subsequent PRs will un-ignore areas that are under review until # all docs are complete and we can drop this ignore file entirely +!docs/tough-cookie.prefixsecurity.md diff --git a/api/docs/tough-cookie.prefixsecurity.md b/api/docs/tough-cookie.prefixsecurity.md new file mode 100644 index 00000000..8887f1ab --- /dev/null +++ b/api/docs/tough-cookie.prefixsecurity.md @@ -0,0 +1,83 @@ + + +[Home](./index.md) > [tough-cookie](./tough-cookie.md) > [PrefixSecurity](./tough-cookie.prefixsecurity.md) + +## PrefixSecurity enum + +Cookie prefixes are a way to indicate that a given cookie was set with a set of attributes simply by inspecting the first few characters of the cookie's name. These are defined in [RFC6265bis - Section 4.1.3](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-13#section-4.1.3). + +The following values can be used to configure how a [CookieJar](./tough-cookie.cookiejar.md) enforces attribute restrictions for Cookie prefixes. + +**Signature:** + +```typescript +export declare enum PrefixSecurity +``` + +## Enumeration Members + + + + + +
+ +Member + + + + +Value + + + + +Description + + +
+ +DISABLED + + + + +`"unsafe-disabled"` + + + + +Disables cookie prefix checking. + + +
+ +SILENT + + + + +`"silent"` + + + + +Enable cookie prefix checking but silently ignores the cookie if conditions are not met. This is the default configuration for a [CookieJar](./tough-cookie.cookiejar.md). + + +
+ +STRICT + + + + +`"strict"` + + + + +Enables cookie prefix checking and will raise an error if conditions are not met. + + +
diff --git a/api/tough-cookie.api.md b/api/tough-cookie.api.md index eca22a33..5f1cbce7 100644 --- a/api/tough-cookie.api.md +++ b/api/tough-cookie.api.md @@ -6,19 +6,21 @@ /// +// Warning: (ae-forgotten-export) The symbol "Nullable" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export function canonicalDomain(str: string | null): string | null; +export function canonicalDomain(str: Nullable): string | undefined; // @public (undocumented) export class Cookie { // Warning: (ae-forgotten-export) The symbol "CreateCookieOptions" needs to be exported by the entry point index.d.ts constructor(options?: CreateCookieOptions); // (undocumented) - canonicalizedDomain(): string | null; + canonicalizedDomain(): string | undefined; // (undocumented) - cdomain(): string | null; + cdomain(): string | undefined; // (undocumented) - clone(): Cookie | null; + clone(): Cookie | undefined; // (undocumented) static cookiesCreated: number; // (undocumented) @@ -100,7 +102,7 @@ export function cookieCompare(a: Cookie, b: Cookie): number; // @public (undocumented) export class CookieJar { // Warning: (ae-forgotten-export) The symbol "CreateCookieJarOptions" needs to be exported by the entry point index.d.ts - constructor(store?: Store | null | undefined, options?: CreateCookieJarOptions | boolean); + constructor(store?: Nullable, options?: CreateCookieJarOptions | boolean); // (undocumented) clone(callback: Callback): void; // (undocumented) @@ -198,29 +200,29 @@ export class CookieJar { } // @public (undocumented) -export function defaultPath(path?: string | null): string; +export function defaultPath(path?: Nullable): string; // @public (undocumented) -export function domainMatch(str?: string | null, domStr?: string | null, canonicalize?: boolean): boolean | null; +export function domainMatch(str?: Nullable, domStr?: Nullable, canonicalize?: boolean): boolean | undefined; // @public export function formatDate(date: Date): string; // @public (undocumented) -export const fromJSON: (str: unknown) => Cookie | null; +export const fromJSON: (str: unknown) => Cookie | undefined; // Warning: (ae-forgotten-export) The symbol "GetPublicSuffixOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function getPublicSuffix(domain: string, options?: GetPublicSuffixOptions): string | null; +export function getPublicSuffix(domain: string, options?: GetPublicSuffixOptions): string | undefined; // @public (undocumented) export class MemoryCookieStore extends Store { constructor(); // (undocumented) - findCookie(domain: string | null, path: string | null, key: string | undefined): Promise; + findCookie(domain: Nullable, path: Nullable, key: Nullable): Promise; // (undocumented) - findCookie(domain: string | null, path: string | null, key: string | undefined, callback: Callback): void; + findCookie(domain: Nullable, path: Nullable, key: Nullable, callback: Callback): void; // (undocumented) findCookies(domain: string, path: string, allowSpecialUseDomain?: boolean): Promise; // (undocumented) @@ -264,38 +266,38 @@ export class ParameterError extends Error { // @public (undocumented) export const parse: (str: string, options?: { loose?: boolean | undefined; -} | undefined) => Cookie | null | undefined; +} | undefined) => Cookie | undefined; // @public (undocumented) -export function parseDate(str: string | undefined | null): Date | undefined; +export function parseDate(str: Nullable): Date | undefined; // @public (undocumented) export function pathMatch(reqPath: string, cookiePath: string): boolean; // @public (undocumented) -export function permuteDomain(domain: string, allowSpecialUseDomain?: boolean): string[] | null; +export function permuteDomain(domain: string, allowSpecialUseDomain?: boolean): string[] | undefined; // @public export function permutePath(path: string): string[]; -// @public (undocumented) -export const PrefixSecurityEnum: Readonly<{ - SILENT: "silent"; - STRICT: "strict"; - DISABLED: "unsafe-disabled"; -}>; +// @public +export enum PrefixSecurity { + DISABLED = "unsafe-disabled", + SILENT = "silent", + STRICT = "strict" +} // @public (undocumented) export class Store { constructor(); // (undocumented) - findCookie(domain: string | null, path: string | null, key: string | undefined): Promise; + findCookie(domain: Nullable, path: Nullable, key: Nullable): Promise; // (undocumented) - findCookie(domain: string | null, path: string | null, key: string | undefined, callback: Callback): void; + findCookie(domain: Nullable, path: Nullable, key: Nullable, callback: Callback): void; // (undocumented) - findCookies(domain: string | null, path: string | null, allowSpecialUseDomain?: boolean): Promise; + findCookies(domain: Nullable, path: Nullable, allowSpecialUseDomain?: boolean): Promise; // (undocumented) - findCookies(domain: string | null, path: string | null, allowSpecialUseDomain?: boolean, callback?: Callback): void; + findCookies(domain: Nullable, path: Nullable, allowSpecialUseDomain?: boolean, callback?: Callback): void; // (undocumented) getAllCookies(): Promise; // (undocumented) @@ -309,13 +311,13 @@ export class Store { // (undocumented) removeAllCookies(callback: ErrorCallback): void; // (undocumented) - removeCookie(domain: string | null | undefined, path: string | null | undefined, key: string | null | undefined): Promise; + removeCookie(domain: Nullable, path: Nullable, key: Nullable): Promise; // (undocumented) - removeCookie(domain: string | null | undefined, path: string | null | undefined, key: string | null | undefined, callback: ErrorCallback): void; + removeCookie(domain: Nullable, path: Nullable, key: Nullable, callback: ErrorCallback): void; // (undocumented) - removeCookies(domain: string, path: string | null): Promise; + removeCookies(domain: string, path: Nullable): Promise; // (undocumented) - removeCookies(domain: string, path: string | null, callback: ErrorCallback): void; + removeCookies(domain: string, path: Nullable, callback: ErrorCallback): void; // (undocumented) synchronous: boolean; // (undocumented) diff --git a/lib/__tests__/cookiePrefixes.spec.ts b/lib/__tests__/cookiePrefixes.spec.ts index 770f53a9..96759fb9 100644 --- a/lib/__tests__/cookiePrefixes.spec.ts +++ b/lib/__tests__/cookiePrefixes.spec.ts @@ -1,4 +1,4 @@ -import { PrefixSecurityEnum } from '../cookie/constants' +import { PrefixSecurity } from '../cookie/constants' import { CookieJar } from '../cookie/cookieJar' let cookieJar: CookieJar @@ -11,7 +11,7 @@ describe('When `prefixSecurity` is enabled for `CookieJar`', () => { cookieJar = new CookieJar(null, { prefixSecurity: 'silent', }) - expect(cookieJar.prefixSecurity).toBe(PrefixSecurityEnum.SILENT) + expect(cookieJar.prefixSecurity).toBe(PrefixSecurity.SILENT) }) describe('__Secure prefix', () => { @@ -106,7 +106,7 @@ describe('When `prefixSecurity` is enabled for `CookieJar`', () => { cookieJar = new CookieJar(null, { prefixSecurity: 'strict', }) - expect(cookieJar.prefixSecurity).toBe(PrefixSecurityEnum.STRICT) + expect(cookieJar.prefixSecurity).toBe(PrefixSecurity.STRICT) }) describe('__Secure prefix', () => { @@ -173,7 +173,7 @@ describe('When `prefixSecurity` is enabled for `CookieJar`', () => { cookieJar = new CookieJar(null, { prefixSecurity: 'unsafe-disabled', }) - expect(cookieJar.prefixSecurity).toBe(PrefixSecurityEnum.DISABLED) + expect(cookieJar.prefixSecurity).toBe(PrefixSecurity.DISABLED) }) describe('__Secure prefix', () => { diff --git a/lib/cookie/constants.ts b/lib/cookie/constants.ts index 278449b3..335cd5b5 100644 --- a/lib/cookie/constants.ts +++ b/lib/cookie/constants.ts @@ -1,8 +1,24 @@ -export const PrefixSecurityEnum = Object.freeze({ - SILENT: 'silent', - STRICT: 'strict', - DISABLED: 'unsafe-disabled', -}) +/** + * Cookie prefixes are a way to indicate that a given cookie was set with a set of attributes simply by inspecting the + * first few characters of the cookie's name. These are defined in {@link https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-13#section-4.1.3 | RFC6265bis - Section 4.1.3}. + * + * The following values can be used to configure how a {@link CookieJar} enforces attribute restrictions for Cookie prefixes. + * @public + */ +export enum PrefixSecurity { + /** + * Enable cookie prefix checking but silently ignores the cookie if conditions are not met. This is the default configuration for a {@link CookieJar}. + */ + SILENT = 'silent', + /** + * Enables cookie prefix checking and will raise an error if conditions are not met. + */ + STRICT = 'strict', + /** + * Disables cookie prefix checking. + */ + DISABLED = 'unsafe-disabled', +} const IP_V6_REGEX = ` \\[?(?: diff --git a/lib/cookie/cookieJar.ts b/lib/cookie/cookieJar.ts index ec881ae9..868d0ef3 100644 --- a/lib/cookie/cookieJar.ts +++ b/lib/cookie/cookieJar.ts @@ -9,16 +9,16 @@ import { pathMatch } from '../pathMatch' import { Cookie } from './cookie' import { Callback, - ErrorCallback, - Nullable, createPromiseCallback, + ErrorCallback, inOperator, + Nullable, safeToString, } from '../utils' import { canonicalDomain } from './canonicalDomain' import { IP_V6_REGEX_OBJECT, - PrefixSecurityEnum, + PrefixSecurity, SerializedCookieJar, } from './constants' import { defaultPath } from './defaultPath' @@ -135,8 +135,7 @@ function isHostPrefixConditionMet(cookie: Cookie): boolean { ) } -type PrefixSecurityValue = - (typeof PrefixSecurityEnum)[keyof typeof PrefixSecurityEnum] +type PrefixSecurityValue = (typeof PrefixSecurity)[keyof typeof PrefixSecurity] function getNormalizedPrefixSecurity( prefixSecurity: string, ): PrefixSecurityValue { @@ -144,14 +143,14 @@ function getNormalizedPrefixSecurity( const normalizedPrefixSecurity = prefixSecurity.toLowerCase() /* The three supported options */ switch (normalizedPrefixSecurity) { - case PrefixSecurityEnum.STRICT: - case PrefixSecurityEnum.SILENT: - case PrefixSecurityEnum.DISABLED: + case PrefixSecurity.STRICT: + case PrefixSecurity.SILENT: + case PrefixSecurity.DISABLED: return normalizedPrefixSecurity } } /* Default is SILENT */ - return PrefixSecurityEnum.SILENT + return PrefixSecurity.SILENT } export class CookieJar { @@ -402,9 +401,9 @@ export class CookieJar { /* 6265bis-02 S5.4 Steps 15 & 16 */ const ignoreErrorForPrefixSecurity = - this.prefixSecurity === PrefixSecurityEnum.SILENT + this.prefixSecurity === PrefixSecurity.SILENT const prefixSecurityDisabled = - this.prefixSecurity === PrefixSecurityEnum.DISABLED + this.prefixSecurity === PrefixSecurity.DISABLED /* If prefix checking is not disabled ...*/ if (!prefixSecurityDisabled) { let errorFound = false diff --git a/lib/cookie/index.ts b/lib/cookie/index.ts index 7b94bdd4..89495df0 100644 --- a/lib/cookie/index.ts +++ b/lib/cookie/index.ts @@ -7,7 +7,7 @@ export { ParameterError } from '../validators' export { version } from '../version' export { canonicalDomain } from './canonicalDomain' -export { PrefixSecurityEnum } from './constants' +export { PrefixSecurity } from './constants' export { Cookie } from './cookie' export { cookieCompare } from './cookieCompare' export { CookieJar } from './cookieJar' diff --git a/test/cookie_prefixes_test.js b/test/cookie_prefixes_test.js index 20f9fb04..ef8624a1 100644 --- a/test/cookie_prefixes_test.js +++ b/test/cookie_prefixes_test.js @@ -33,7 +33,7 @@ const vows = require("vows"); const assert = require("assert"); const tough = require("../dist/cookie"); const CookieJar = tough.CookieJar; -const PrefixSecurityEnum = tough.PrefixSecurityEnum; +const PrefixSecurity = tough.PrefixSecurity; vows .describe("Cookie Prefixes") @@ -45,7 +45,7 @@ vows return new CookieJar(null, { prefixSecurity: "silent" }); }, "with no Secure attribute, should fail silently": function(cj) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Secure-SID=12345; Domain=example.com", "http://www.example.com", @@ -55,7 +55,7 @@ vows assert.isEmpty(cookies); // no cookies set }, "with Secure attribute and over https, should work": function(cj) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Secure-SID=12345; Domain=example.com; Secure", "https://www.example.com", @@ -69,7 +69,7 @@ vows "with Secure attribute but not over https, should fail silently": function( cj ) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Secure-SID=12345; Domain=example.com; Secure", "http://www.example.com", @@ -86,13 +86,13 @@ vows "with no Secure attribute or Domain or Path, should fail silently": function( cj ) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync("__Host-SID=12345", "http://www.example.com", {}); const cookies = cj.getCookiesSync("http://www.example.com"); assert.isEmpty(cookies); // no cookies set }, "with no Domain or Path, should fail silently": function(cj) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Host-SID=12345; Secure", "http://www.example.com", @@ -102,7 +102,7 @@ vows assert.isEmpty(cookies); // no cookies set }, "with no Path, should fail silently": function(cj) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Host-SID=12345; Secure; Domain=example.com", "http://www.example.com", @@ -112,7 +112,7 @@ vows assert.isEmpty(cookies); // no cookies set }, "with Domain, should fail silently": function(cj) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Host-SID=12345; Secure; Domain=example.com; Path=/", "http://www.example.com", @@ -124,7 +124,7 @@ vows "with Secure and Path but no Domain over https, should work": function( cj ) { - assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity); + assert.equal(PrefixSecurity.SILENT, cj.prefixSecurity); cj.setCookieSync( "__Host-SID=12345; Secure; Path=/", "https://www.example.com", @@ -144,7 +144,7 @@ vows return new CookieJar(null, { prefixSecurity: "strict" }); }, passes: function(cj) { - assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity); + assert.equal(PrefixSecurity.STRICT, cj.prefixSecurity); cj.setCookieSync( "__Secure-SID=12345; Secure; Domain=example.com", "https://www.example.com", @@ -159,7 +159,7 @@ vows "for invalid cookie": { topic: function() { const cj = new CookieJar(null, { prefixSecurity: "strict" }); - assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity); + assert.equal(PrefixSecurity.STRICT, cj.prefixSecurity); cj.setCookieSync( "__Secure-SID=12345; Domain=example.com", "http://www.example.com", @@ -176,7 +176,7 @@ vows "for invalid cookie": { topic: function() { const cj = new CookieJar(null, { prefixSecurity: "strict" }); - assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity); + assert.equal(PrefixSecurity.STRICT, cj.prefixSecurity); cj.setCookieSync( "__Host-SID=12345; Secure; Domain=example.com", "https://www.example.com", @@ -193,7 +193,7 @@ vows return new CookieJar(null, { prefixSecurity: "strict" }); }, passes: function(cj) { - assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity); + assert.equal(PrefixSecurity.STRICT, cj.prefixSecurity); cj.setCookieSync( "__Host-SID=12345; Secure; Path=/", "https://www.foo.com", @@ -213,7 +213,7 @@ vows return new CookieJar(null, { prefixSecurity: "unsafe-disabled" }); }, "does not fail": function(cj) { - assert.equal(PrefixSecurityEnum.DISABLED, cj.prefixSecurity); + assert.equal(PrefixSecurity.DISABLED, cj.prefixSecurity); cj.setCookieSync( "__Secure-SID=12345; Domain=example.com", "http://www.example.com", @@ -230,7 +230,7 @@ vows return new CookieJar(null, { prefixSecurity: "unsafe-disabled" }); }, "does not fail": function(cj) { - assert.equal(PrefixSecurityEnum.DISABLED, cj.prefixSecurity); + assert.equal(PrefixSecurity.DISABLED, cj.prefixSecurity); /* Failure case because Domain defined */ cj.setCookieSync( "__Host-SID=12345; Domain=example.com",