@nathapp/nestjs-csrf 4.0.2 → 4.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,4 +26,49 @@ The host app **must enable cookie parsing** so `req.cookies` is populated:
26
26
  These are not auto-wired. If cookie parsing is not enabled, mutating requests fail
27
27
  closed (403).
28
28
 
29
+ ## Options
30
+
31
+ All options are optional except `secret`:
32
+
33
+ | Option | Default | Description |
34
+ | ------ | ------- | ----------- |
35
+ | `secret` | — (required) | HMAC secret used to mint/verify tokens. |
36
+ | `enabled` | `true` | Global kill switch for the guard. |
37
+ | `headerName` | `x-csrf-token` | Header the client must echo the cookie value in. |
38
+ | `ignoreMethods` | `['GET', 'HEAD', 'OPTIONS']` | Safe methods that skip verification (and receive the cookie). Entries are uppercased before comparison. |
39
+ | `skipWhenBearer` | `true` | Skip verification when an `Authorization: Bearer …` header is present. |
40
+ | `originCheck` | `true` | When an `Origin` or `Referer` header is present and its origin is not allowed, the request is rejected with 403 `csrf_origin_mismatch` (before the token check). Requests carrying neither header (native apps) fall through to the token check. |
41
+ | `allowedOrigins` | `[]` | Extra origins accepted by the origin check, in addition to the request's own origin (e.g. `['https://cdn.example.com']`). |
42
+ | `cookie` | see below | Cookie attributes: `name` (`csrf-token`), `path` (`/`), `httpOnly` (`false`), `secure` (`true`), `sameSite` (`lax`). |
43
+
44
+ The request's **own origin** is host-bound: the host comes from `x-forwarded-host`
45
+ (falling back to `host`) and the scheme from `x-forwarded-proto` (first value, if the
46
+ proxy sends a comma-separated chain) or `req.socket.encrypted`. When the request does
47
+ **not** advertise a determinate scheme — for example a TLS-terminating proxy that does
48
+ not set `x-forwarded-proto` — either `https://<host>` or `http://<host>` is accepted,
49
+ so legitimate same-origin mutations are not rejected on such deployments. An origin on
50
+ any *other* host is still rejected. Setting `x-forwarded-proto: https` at the proxy is
51
+ still recommended so the check is exact.
52
+
53
+ `cookie.secure` defaults to `true`. Over plain HTTP, browsers drop secure cookies, so
54
+ every protected mutation would fail with 403. To make this misconfiguration
55
+ diagnosable, the guard logs a **one-time warning** on the first protected request that
56
+ arrives over plain HTTP (no `x-forwarded-proto: https` and no `req.socket.encrypted`)
57
+ while `secure: true`. Serve over HTTPS or set `cookie.secure: false` for local
58
+ plain-HTTP development.
59
+
60
+ ## Security notes and residual risk
61
+
62
+ The double-submit token is **not bound to a user session or identity** — it proves
63
+ same-origin presence of the cookie value, not authentication state. The mitigating
64
+ control is the origin check: a cross-site attacker's forged request either carries an
65
+ `Origin`/`Referer` that fails the allow-check, or omits them — but an attacker cannot
66
+ read the victim's cookies to echo a valid token in the `x-csrf-token` header, so the
67
+ double-submit check still holds. Scenarios where an attacker can **inject** a cookie
68
+ value into the victim's browser (e.g. cookie injection/fixation from a sibling
69
+ subdomain) remain conditional: they depend on the injected value surviving and the
70
+ token not being tied to anything server-side, and are not fully eliminated by the
71
+ double-submit pattern alone. If session binding is required, pair this guard with a
72
+ session-bound token check.
73
+
29
74
  See `docs/superpowers/specs/2026-07-08-nestjs-csrf-design.md`.
@@ -7,7 +7,11 @@ export declare class DefaultCsrfGuard implements CanActivate {
7
7
  private readonly reflector;
8
8
  private readonly logger;
9
9
  private readonly options;
10
+ private warnedInsecureCookie;
10
11
  constructor(rawOptions: CsrfOptions, tokens: CsrfTokenService, reflector: Reflector);
11
12
  canActivate(context: ExecutionContext): boolean;
12
13
  private issue;
14
+ private isOriginAllowed;
15
+ private firstHeaderValue;
16
+ private warnIfInsecureCookie;
13
17
  }
@@ -25,10 +25,11 @@ let DefaultCsrfGuard = DefaultCsrfGuard_1 = class DefaultCsrfGuard {
25
25
  this.tokens = tokens;
26
26
  this.reflector = reflector;
27
27
  this.logger = new common_1.Logger(DefaultCsrfGuard_1.name);
28
+ this.warnedInsecureCookie = false;
28
29
  this.options = (0, resolve_options_1.resolveCsrfOptions)(rawOptions);
29
30
  }
30
31
  canActivate(context) {
31
- var _a, _b, _c, _d, _e;
32
+ var _a, _b, _c, _d, _e, _f, _g, _h;
32
33
  if (this.options.enabled === false)
33
34
  return true;
34
35
  if (context.getType() !== 'http')
@@ -43,6 +44,7 @@ let DefaultCsrfGuard = DefaultCsrfGuard_1 = class DefaultCsrfGuard {
43
44
  }
44
45
  return true;
45
46
  }
47
+ this.warnIfInsecureCookie(req);
46
48
  const reflectionTargets = [context.getHandler(), context.getClass()];
47
49
  const validTargets = reflectionTargets.filter((t) => t != null);
48
50
  if (validTargets.length > 0 &&
@@ -54,8 +56,14 @@ let DefaultCsrfGuard = DefaultCsrfGuard_1 = class DefaultCsrfGuard {
54
56
  /^Bearer\s/i.test(req.headers['authorization'])) {
55
57
  return true;
56
58
  }
57
- const cookie = (_d = req.cookies) === null || _d === void 0 ? void 0 : _d[this.options.cookie.name];
58
- const headerValue = (_e = req.headers) === null || _e === void 0 ? void 0 : _e[this.options.headerName];
59
+ if (this.options.originCheck !== false) {
60
+ const originHeader = ((_e = (_d = req.headers) === null || _d === void 0 ? void 0 : _d['origin']) !== null && _e !== void 0 ? _e : (_f = req.headers) === null || _f === void 0 ? void 0 : _f['referer']);
61
+ if (typeof originHeader === 'string' && originHeader.length > 0 && !this.isOriginAllowed(originHeader, req)) {
62
+ throw new nestjs_common_1.ForbiddenAppException({ reason: 'csrf_origin_mismatch' });
63
+ }
64
+ }
65
+ const cookie = (_g = req.cookies) === null || _g === void 0 ? void 0 : _g[this.options.cookie.name];
66
+ const headerValue = (_h = req.headers) === null || _h === void 0 ? void 0 : _h[this.options.headerName];
59
67
  const header = Array.isArray(headerValue) ? headerValue[0] : headerValue;
60
68
  if (!this.tokens.verify(this.options.secret, cookie, header)) {
61
69
  throw new nestjs_common_1.ForbiddenAppException({ reason: 'csrf_token_invalid' });
@@ -76,6 +84,50 @@ let DefaultCsrfGuard = DefaultCsrfGuard_1 = class DefaultCsrfGuard {
76
84
  'Ensure cookie-parser (Express) or @fastify/cookie (Fastify) is registered.');
77
85
  }
78
86
  }
87
+ isOriginAllowed(originHeader, req) {
88
+ var _a, _b, _c, _d;
89
+ let origin;
90
+ try {
91
+ origin = new URL(originHeader, 'http://placeholder.invalid').origin;
92
+ }
93
+ catch (_e) {
94
+ return false;
95
+ }
96
+ const allowed = new Set(this.options.allowedOrigins);
97
+ const headers = (_a = req.headers) !== null && _a !== void 0 ? _a : {};
98
+ const forwardedProto = (_b = this.firstHeaderValue(headers['x-forwarded-proto'])) === null || _b === void 0 ? void 0 : _b.split(',')[0].trim();
99
+ const host = (_c = this.firstHeaderValue(headers['x-forwarded-host'])) !== null && _c !== void 0 ? _c : this.firstHeaderValue(headers['host']);
100
+ if (typeof host === 'string' && host.length > 0) {
101
+ if (forwardedProto) {
102
+ allowed.add(`${forwardedProto}://${host}`);
103
+ }
104
+ else if (((_d = req.socket) === null || _d === void 0 ? void 0 : _d.encrypted) === true) {
105
+ allowed.add(`https://${host}`);
106
+ }
107
+ else {
108
+ allowed.add(`https://${host}`);
109
+ allowed.add(`http://${host}`);
110
+ }
111
+ }
112
+ return allowed.has(origin);
113
+ }
114
+ firstHeaderValue(value) {
115
+ return Array.isArray(value) ? value[0] : value;
116
+ }
117
+ warnIfInsecureCookie(req) {
118
+ var _a, _b;
119
+ if (this.warnedInsecureCookie || !this.options.cookie.secure)
120
+ return;
121
+ const forwardedProto = this.firstHeaderValue((_a = req.headers) === null || _a === void 0 ? void 0 : _a['x-forwarded-proto']);
122
+ if (forwardedProto === 'https' || ((_b = req.socket) === null || _b === void 0 ? void 0 : _b.encrypted) === true)
123
+ return;
124
+ this.warnedInsecureCookie = true;
125
+ this.logger.warn('CSRF cookie is issued with secure=true but this request arrived over plain HTTP ' +
126
+ '(no "x-forwarded-proto: https" header and no req.socket.encrypted). Browsers drop ' +
127
+ 'secure cookies over plain HTTP, so the CSRF cookie will never be stored and every ' +
128
+ 'protected mutation will fail with 403. Serve the app over HTTPS (set ' +
129
+ 'x-forwarded-proto at your proxy) or set cookie.secure=false for plain-HTTP deployments.');
130
+ }
79
131
  };
80
132
  exports.DefaultCsrfGuard = DefaultCsrfGuard;
81
133
  exports.DefaultCsrfGuard = DefaultCsrfGuard = DefaultCsrfGuard_1 = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"csrf.guard.js","sourceRoot":"","sources":["../../lib/guard/csrf.guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"csrf.guard.js","sourceRoot":"","sources":["../../lib/guard/csrf.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAMwB;AACxB,uCAAyC;AACzC,0DAA+D;AAC/D,+DAAyF;AAEzF,sEAAiE;AACjE,gEAAgE;AAGzD,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAK3B,YAC+B,UAAuB,EACnC,MAAwB,EACxB,SAAoB;QADpB,WAAM,GAAN,MAAM,CAAkB;QACxB,cAAS,GAAT,SAAS,CAAW;QAPtB,WAAM,GAAG,IAAI,eAAM,CAAC,kBAAgB,CAAC,IAAI,CAAC,CAAC;QAEpD,yBAAoB,GAAG,KAAK,CAAC;QAOnC,IAAI,CAAC,OAAO,GAAG,IAAA,oCAAkB,EAAC,UAAU,CAAC,CAAC;IAChD,CAAC;IAED,WAAW,CAAC,OAAyB;;QACnC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QAIhD,IAAI,OAAO,CAAC,OAAO,EAAE,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAE9C,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAMvB,CAAC;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAGxB,CAAC;QAEL,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,MAAA,GAAG,CAAC,MAAM,mCAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC1E,MAAM,cAAc,GAAG,MAAA,GAAG,CAAC,OAAO,0CAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/D,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAE/B,MAAM,iBAAiB,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrE,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CACjB,CAAC;QACF,IACE,YAAY,CAAC,MAAM,GAAG,CAAC;YACvB,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,uCAAsB,EAAE,YAAY,CAAC,EACtE,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IACE,IAAI,CAAC,OAAO,CAAC,cAAc;YAC3B,OAAO,CAAA,MAAA,GAAG,CAAC,OAAO,0CAAG,eAAe,CAAC,CAAA,KAAK,QAAQ;YAClD,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAW,CAAC,EACzD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;YACvC,MAAM,YAAY,GAAG,CAAC,MAAA,MAAA,GAAG,CAAC,OAAO,0CAAG,QAAQ,CAAC,mCAAI,MAAA,GAAG,CAAC,OAAO,0CAAG,SAAS,CAAC,CAAuB,CAAC;YACjG,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC5G,MAAM,IAAI,qCAAqB,CAAC,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,OAAO,0CAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,WAAW,GAAG,MAAA,GAAG,CAAC,OAAO,0CAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAEzE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,qCAAqB,CAAC,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,GAGb;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAC9C,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;YACxC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC5C,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,qFAAqF;gBACnF,4EAA4E,CAC/E,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,eAAe,CACrB,YAAoB,EACpB,GAGC;;QAED,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,EAAE,4BAA4B,CAAC,CAAC,MAAM,CAAC;QACtE,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,MAAA,GAAG,CAAC,OAAO,mCAAI,EAAE,CAAC;QAGlC,MAAM,cAAc,GAAG,MAAA,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,0CACtE,KAAK,CAAC,GAAG,EAAE,CAAC,EACb,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,GACR,MAAA,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,mCAClD,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAEzC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CAAC,GAAG,cAAc,MAAM,IAAI,EAAE,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,CAAA,MAAA,GAAG,CAAC,MAAM,0CAAE,SAAS,MAAK,IAAI,EAAE,CAAC;gBAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;YACjC,CAAC;iBAAM,CAAC;gBAON,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAEO,gBAAgB,CAAC,KAAoC;QAC3D,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACjD,CAAC;IAEO,oBAAoB,CAAC,GAG5B;;QACC,IAAI,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO;QAErE,MAAM,cAAc,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAA,GAAG,CAAC,OAAO,0CAAG,mBAAmB,CAAC,CAAC,CAAC;QACjF,IAAI,cAAc,KAAK,OAAO,IAAI,CAAA,MAAA,GAAG,CAAC,MAAM,0CAAE,SAAS,MAAK,IAAI;YAAE,OAAO;QAEzE,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,kFAAkF;YAChF,oFAAoF;YACpF,oFAAoF;YACpF,uEAAuE;YACvE,yFAAyF,CAC5F,CAAC;IACJ,CAAC;CACF,CAAA;AArKY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;IAOR,WAAA,IAAA,eAAM,EAAC,oCAAmB,CAAC,CAAA;6CACH,qCAAgB;QACb,gBAAS;GAR5B,gBAAgB,CAqK5B"}
@@ -12,6 +12,8 @@ export interface CsrfOptions {
12
12
  headerName?: string;
13
13
  ignoreMethods?: string[];
14
14
  skipWhenBearer?: boolean;
15
+ allowedOrigins?: string[];
16
+ originCheck?: boolean;
15
17
  cookie?: Partial<CsrfCookieOptions>;
16
18
  }
17
19
  export interface ResolvedCsrfOptions {
@@ -20,6 +22,8 @@ export interface ResolvedCsrfOptions {
20
22
  headerName: string;
21
23
  ignoreMethods: string[];
22
24
  skipWhenBearer: boolean;
25
+ allowedOrigins: string[];
26
+ originCheck: boolean;
23
27
  cookie: CsrfCookieOptions;
24
28
  }
25
29
  export interface CsrfModuleOptionsFactory {
@@ -6,7 +6,7 @@ export type CsrfAsyncModuleOptions = Pick<ModuleMetadata, 'imports'> & {
6
6
  useFactory?: (...args: any[]) => Promise<CsrfOptions> | CsrfOptions;
7
7
  inject?: any[];
8
8
  };
9
- declare const CsrfModule_base: import("@nathapp/nestjs-common").ConfigurableModuleCls<CsrfOptions, "register", "createOptions", {}>;
9
+ declare const CsrfModule_base: import("@nathapp/nestjs-common").ConfigurableModuleCls<CsrfOptions, "register", "createOptions", object>;
10
10
  export declare class CsrfModule extends CsrfModule_base {
11
11
  static register(options: CsrfOptions): DynamicModule;
12
12
  static registerAsync(options: CsrfAsyncModuleOptions): DynamicModule;
@@ -10,14 +10,14 @@ const DEFAULT_COOKIE_SECURE = true;
10
10
  const DEFAULT_COOKIE_SAME_SITE = 'lax';
11
11
  function resolveCsrfOptions(options) {
12
12
  var _a, _b, _c, _d, _e;
13
- const { secret, enabled = true, headerName: headerNameOverride = DEFAULT_HEADER_NAME, ignoreMethods: ignoreMethodsOverride, skipWhenBearer = true, cookie: cookieOverrides, } = options;
13
+ const { secret, enabled = true, headerName: headerNameOverride = DEFAULT_HEADER_NAME, ignoreMethods: ignoreMethodsOverride, skipWhenBearer = true, allowedOrigins: allowedOriginsOverride, originCheck: originCheckOverride, cookie: cookieOverrides, } = options;
14
14
  if (typeof secret !== 'string' || secret.length === 0) {
15
15
  throw new Error('CsrfOptions.secret must be a non-empty string');
16
16
  }
17
17
  const headerName = headerNameOverride.toLowerCase();
18
- const ignoreMethods = ignoreMethodsOverride
19
- ? [...ignoreMethodsOverride]
20
- : [...DEFAULT_IGNORE_METHODS];
18
+ const ignoreMethods = (ignoreMethodsOverride !== null && ignoreMethodsOverride !== void 0 ? ignoreMethodsOverride : DEFAULT_IGNORE_METHODS).map((m) => m.toUpperCase());
19
+ const allowedOrigins = [...(allowedOriginsOverride !== null && allowedOriginsOverride !== void 0 ? allowedOriginsOverride : [])];
20
+ const originCheck = originCheckOverride !== null && originCheckOverride !== void 0 ? originCheckOverride : true;
21
21
  const cookie = {
22
22
  name: (_a = cookieOverrides === null || cookieOverrides === void 0 ? void 0 : cookieOverrides.name) !== null && _a !== void 0 ? _a : DEFAULT_COOKIE_NAME,
23
23
  path: (_b = cookieOverrides === null || cookieOverrides === void 0 ? void 0 : cookieOverrides.path) !== null && _b !== void 0 ? _b : DEFAULT_COOKIE_PATH,
@@ -31,6 +31,8 @@ function resolveCsrfOptions(options) {
31
31
  headerName,
32
32
  ignoreMethods,
33
33
  skipWhenBearer,
34
+ allowedOrigins,
35
+ originCheck,
34
36
  cookie,
35
37
  };
36
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-options.js","sourceRoot":"","sources":["../../lib/service/resolve-options.ts"],"names":[],"mappings":";;AAUA,gDAoCC;AA5CD,MAAM,mBAAmB,GAAG,YAAY,CAAC;AACzC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAC3C,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAC1D,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AACvC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AACnC,MAAM,wBAAwB,GAAkC,KAAK,CAAC;AAEtE,SAAgB,kBAAkB,CAAC,OAAoB;;IACrD,MAAM,EACJ,MAAM,EACN,OAAO,GAAG,IAAI,EACd,UAAU,EAAE,kBAAkB,GAAG,mBAAmB,EACpD,aAAa,EAAE,qBAAqB,EACpC,cAAc,GAAG,IAAI,EACrB,MAAM,EAAE,eAAe,GACxB,GAAG,OAAO,CAAC;IAEZ,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,WAAW,EAAE,CAAC;IAEpD,MAAM,aAAa,GAAG,qBAAqB;QACzC,CAAC,CAAC,CAAC,GAAG,qBAAqB,CAAC;QAC5B,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAsB;QAChC,IAAI,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI,mCAAI,mBAAmB;QAClD,IAAI,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI,mCAAI,mBAAmB;QAClD,QAAQ,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,QAAQ,mCAAI,wBAAwB;QAC/D,MAAM,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,MAAM,mCAAI,qBAAqB;QACxD,QAAQ,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,QAAQ,mCAAI,wBAAwB;KAChE,CAAC;IAEF,OAAO;QACL,MAAM;QACN,OAAO;QACP,UAAU;QACV,aAAa;QACb,cAAc;QACd,MAAM;KACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"resolve-options.js","sourceRoot":"","sources":["../../lib/service/resolve-options.ts"],"names":[],"mappings":";;AAUA,gDA6CC;AArDD,MAAM,mBAAmB,GAAG,YAAY,CAAC;AACzC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAC3C,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAC1D,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AACvC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AACnC,MAAM,wBAAwB,GAAkC,KAAK,CAAC;AAEtE,SAAgB,kBAAkB,CAAC,OAAoB;;IACrD,MAAM,EACJ,MAAM,EACN,OAAO,GAAG,IAAI,EACd,UAAU,EAAE,kBAAkB,GAAG,mBAAmB,EACpD,aAAa,EAAE,qBAAqB,EACpC,cAAc,GAAG,IAAI,EACrB,cAAc,EAAE,sBAAsB,EACtC,WAAW,EAAE,mBAAmB,EAChC,MAAM,EAAE,eAAe,GACxB,GAAG,OAAO,CAAC;IAEZ,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,WAAW,EAAE,CAAC;IAIpD,MAAM,aAAa,GAAG,CAAC,qBAAqB,aAArB,qBAAqB,cAArB,qBAAqB,GAAI,sBAAsB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChF,CAAC,CAAC,WAAW,EAAE,CAChB,CAAC;IAEF,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,sBAAsB,aAAtB,sBAAsB,cAAtB,sBAAsB,GAAI,EAAE,CAAC,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,mBAAmB,aAAnB,mBAAmB,cAAnB,mBAAmB,GAAI,IAAI,CAAC;IAEhD,MAAM,MAAM,GAAsB;QAChC,IAAI,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI,mCAAI,mBAAmB;QAClD,IAAI,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI,mCAAI,mBAAmB;QAClD,QAAQ,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,QAAQ,mCAAI,wBAAwB;QAC/D,MAAM,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,MAAM,mCAAI,qBAAqB;QACxD,QAAQ,EAAE,MAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,QAAQ,mCAAI,wBAAwB;KAChE,CAAC;IAEF,OAAO;QACL,MAAM;QACN,OAAO;QACP,UAAU;QACV,aAAa;QACb,cAAc;QACd,cAAc;QACd,WAAW;QACX,MAAM;KACP,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nestjs-csrf",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
4
4
  "description": "Stateless double-submit-cookie CSRF protection for NestJS (Express + Fastify), zero runtime dependencies",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,8 +12,9 @@
12
12
  "test:cov": "jest --config ./test/jest-test.json --coverage",
13
13
  "test:watch": "jest --config ./test/jest-test.json --watch",
14
14
  "type-check": "tsc --noEmit -p tsconfig.json",
15
- "lint": "eslint -c .eslintrc.js",
16
- "lint:fix": "eslint -c .eslintrc.js --fix"
15
+ "lint": "eslint -c .eslintrc.js \"lib/**/*.ts\" \"test/**/*.ts\"",
16
+ "lint:fix": "eslint -c .eslintrc.js --fix \"lib/**/*.ts\" \"test/**/*.ts\"",
17
+ "type-check:test": "tsc --noEmit -p tsconfig.test.json"
17
18
  },
18
19
  "author": "William Khoo",
19
20
  "license": "ISC",
@@ -29,7 +30,7 @@
29
30
  },
30
31
  "devDependencies": {
31
32
  "@nathapp/eslint-config": "1.0.1",
32
- "@nathapp/nestjs-common": "^4.0.2",
33
+ "@nathapp/nestjs-common": "^4.0.3",
33
34
  "@nathapp/typescript-config": "1.0.1",
34
35
  "@nestjs/common": "^11.0.0",
35
36
  "@nestjs/core": "^11.0.0",
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "rootDir": "."
6
+ },
7
+ "include": [
8
+ "lib/**/*.ts",
9
+ "test/**/*.ts"
10
+ ],
11
+ "exclude": [
12
+ "node_modules",
13
+ "dist",
14
+ "coverage"
15
+ ]
16
+ }