@strapi/plugin-users-permissions 5.44.0 → 5.45.1

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.
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ var rateLimit = {exports: {}};
4
+
5
+ exports.__module = rateLimit;
6
+ //# sourceMappingURL=rateLimit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimit.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
@@ -0,0 +1,4 @@
1
+ var rateLimit = {exports: {}};
2
+
3
+ export { rateLimit as __module };
4
+ //# sourceMappingURL=rateLimit.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimit.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -1,20 +1,98 @@
1
1
  'use strict';
2
2
 
3
+ var rateLimit = require('../_virtual/rateLimit.js');
3
4
  var require$$1$1 = require('path');
4
5
  var require$$1 = require('@strapi/utils');
5
6
  var require$$0 = require('lodash/fp');
6
7
  var require$$3 = require('koa2-ratelimit');
7
8
 
8
- var rateLimit;
9
9
  var hasRequiredRateLimit;
10
10
  function requireRateLimit() {
11
- if (hasRequiredRateLimit) return rateLimit;
11
+ if (hasRequiredRateLimit) return rateLimit.__module.exports;
12
12
  hasRequiredRateLimit = 1;
13
13
  const path = require$$1$1;
14
14
  const utils = require$$1;
15
15
  const { isString, has, toLower } = require$$0;
16
16
  const { RateLimitError } = utils.errors;
17
- rateLimit = (config, { strapi })=>async (ctx, next)=>{
17
+ /**
18
+ * Routes where the rate-limit key MUST NOT include a user identifier
19
+ * derived from `ctx.request.body.email`.
20
+ *
21
+ * On these routes the request body either has no `email` field
22
+ * (e.g. /auth/local uses `identifier`, /auth/reset-password uses
23
+ * `code`, /auth/change-password uses `currentPassword`) or the
24
+ * field is not part of the route contract. Including the
25
+ * attacker-controlled `body.email` in the rate-limit key on these
26
+ * routes lets a caller obtain a fresh key on every request by
27
+ * varying that field, effectively bypassing per-IP throttling.
28
+ *
29
+ * Comparison uses endsWith so the check is stable under any router
30
+ * mount prefix (e.g. `/api/auth/local`).
31
+ *
32
+ * @see https://github.com/strapi/strapi/security/advisories/GHSA-7mqx-wwh4-f9fw
33
+ *
34
+ * When adding a new `rateLimit`-protected auth route whose body does not
35
+ * use `email` as the real identifier, add its path suffix here (or an
36
+ * equivalent `routeUsesEmailIdentifier` rule) so the key cannot be split
37
+ * with arbitrary `body.email` values.
38
+ */ const ROUTES_WITHOUT_IDENTIFIER = [
39
+ '/auth/local',
40
+ '/auth/reset-password',
41
+ '/auth/change-password'
42
+ ];
43
+ const isOAuthCallbackPath = (requestPath)=>requestPath.includes('/connect/');
44
+ const routeUsesEmailIdentifier = (requestPath)=>{
45
+ if (isOAuthCallbackPath(requestPath)) {
46
+ return false;
47
+ }
48
+ return !ROUTES_WITHOUT_IDENTIFIER.some((route)=>requestPath.endsWith(route));
49
+ };
50
+ /**
51
+ * Paths suitable for route matching and prefix keys: POSIX-normalized,
52
+ * lower-cased, trailing slashes removed so `/api/auth/local` and
53
+ * `/api/auth/local/` share one bucket.
54
+ */ const normalizeRequestPathForRateLimit = (requestPath)=>{
55
+ const normalized = path.normalize(requestPath);
56
+ const lower = toLower(normalized);
57
+ return lower.replace(/\/+$/, '') || '/';
58
+ };
59
+ const getEmailIdentifierForKey = (body)=>{
60
+ if (!body || !isString(body.email) || body.email === '') {
61
+ return 'unknownIdentifier';
62
+ }
63
+ return toLower(body.email);
64
+ };
65
+ const buildPrefixKey = (ctx)=>{
66
+ let requestPath;
67
+ if (!isString(ctx.request.path)) {
68
+ requestPath = 'invalidPath';
69
+ } else {
70
+ requestPath = normalizeRequestPathForRateLimit(ctx.request.path);
71
+ if (requestPath === '.' || requestPath === '..') {
72
+ requestPath = 'invalidPath';
73
+ }
74
+ }
75
+ if (!routeUsesEmailIdentifier(requestPath)) {
76
+ return `noIdentifier:${requestPath}:${ctx.request.ip}`;
77
+ }
78
+ const userIdentifier = getEmailIdentifierForKey(ctx.request.body);
79
+ return `${userIdentifier}:${requestPath}:${ctx.request.ip}`;
80
+ };
81
+ const buildRateLimitLoadConfig = (ctx, rateLimitConfig, routeMiddlewareConfig)=>{
82
+ return {
83
+ interval: {
84
+ min: 5
85
+ },
86
+ max: 5,
87
+ ...rateLimitConfig,
88
+ ...routeMiddlewareConfig,
89
+ handler () {
90
+ throw new RateLimitError();
91
+ },
92
+ prefixKey: buildPrefixKey(ctx)
93
+ };
94
+ };
95
+ rateLimit.__module.exports = (config, { strapi })=>async (ctx, next)=>{
18
96
  let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');
19
97
  if (!rateLimitConfig) {
20
98
  rateLimitConfig = {
@@ -26,25 +104,16 @@ function requireRateLimit() {
26
104
  }
27
105
  if (rateLimitConfig.enabled === true) {
28
106
  const rateLimit = require$$3.RateLimit;
29
- const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';
30
- const requestPath = isString(ctx.request.path) ? toLower(path.normalize(ctx.request.path)) : 'invalidPath';
31
- const loadConfig = {
32
- interval: {
33
- min: 5
34
- },
35
- max: 5,
36
- prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,
37
- handler () {
38
- throw new RateLimitError();
39
- },
40
- ...rateLimitConfig,
41
- ...config
42
- };
107
+ const loadConfig = buildRateLimitLoadConfig(ctx, rateLimitConfig, config);
43
108
  return rateLimit.middleware(loadConfig)(ctx, next);
44
109
  }
45
110
  return next();
46
111
  };
47
- return rateLimit;
112
+ rateLimit.__module.exports.buildPrefixKey = buildPrefixKey;
113
+ rateLimit.__module.exports.ROUTES_WITHOUT_IDENTIFIER = ROUTES_WITHOUT_IDENTIFIER;
114
+ rateLimit.__module.exports.normalizeRequestPathForRateLimit = normalizeRequestPathForRateLimit;
115
+ rateLimit.__module.exports.buildRateLimitLoadConfig = buildRateLimitLoadConfig;
116
+ return rateLimit.__module.exports;
48
117
  }
49
118
 
50
119
  exports.__require = requireRateLimit;
@@ -1 +1 @@
1
- {"version":3,"file":"rateLimit.js","sources":["../../../server/middlewares/rateLimit.js"],"sourcesContent":["'use strict';\n\nconst path = require('path');\nconst utils = require('@strapi/utils');\nconst { isString, has, toLower } = require('lodash/fp');\n\nconst { RateLimitError } = utils.errors;\n\nmodule.exports =\n (config, { strapi }) =>\n async (ctx, next) => {\n let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');\n\n if (!rateLimitConfig) {\n rateLimitConfig = {\n enabled: true,\n };\n }\n\n if (!has('enabled', rateLimitConfig)) {\n rateLimitConfig.enabled = true;\n }\n\n if (rateLimitConfig.enabled === true) {\n const rateLimit = require('koa2-ratelimit').RateLimit;\n\n const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';\n const requestPath = isString(ctx.request.path)\n ? toLower(path.normalize(ctx.request.path))\n : 'invalidPath';\n\n const loadConfig = {\n interval: { min: 5 },\n max: 5,\n prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,\n handler() {\n throw new RateLimitError();\n },\n ...rateLimitConfig,\n ...config,\n };\n\n return rateLimit.middleware(loadConfig)(ctx, next);\n }\n\n return next();\n };\n"],"names":["path","require$$0","utils","require$$1","isString","has","toLower","require$$2","RateLimitError","errors","rateLimit","config","strapi","ctx","next","rateLimitConfig","get","enabled","require$$3","RateLimit","userIdentifier","request","body","email","requestPath","normalize","loadConfig","interval","min","max","prefixKey","ip","handler","middleware"],"mappings":";;;;;;;;;;;;AAEA,IAAA,MAAMA,IAAAA,GAAOC,YAAAA;AACb,IAAA,MAAMC,KAAAA,GAAQC,UAAAA;AACd,IAAA,MAAM,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,OAAO,EAAE,GAAGC,UAAAA;AAEnC,IAAA,MAAM,EAAEC,cAAc,EAAE,GAAGN,MAAMO,MAAM;AAEvCC,IAAAA,SAAAA,GACE,CAACC,MAAAA,EAAQ,EAAEC,MAAM,EAAE,GACnB,OAAOC,GAAAA,EAAKC,IAAAA,GAAAA;AACV,YAAA,IAAIC,eAAAA,GAAkBH,MAAAA,CAAOD,MAAM,CAACK,GAAG,CAAC,qCAAA,CAAA;AAExC,YAAA,IAAI,CAACD,eAAAA,EAAiB;gBACpBA,eAAAA,GAAkB;oBAChBE,OAAAA,EAAS;AACjB,iBAAA;AACA,YAAA;YAEI,IAAI,CAACZ,GAAAA,CAAI,SAAA,EAAWU,eAAAA,CAAAA,EAAkB;AACpCA,gBAAAA,eAAAA,CAAgBE,OAAO,GAAG,IAAA;AAChC,YAAA;YAEI,IAAIF,eAAAA,CAAgBE,OAAO,KAAK,IAAA,EAAM;gBACpC,MAAMP,SAAAA,GAAYQ,WAA0BC,SAAS;gBAErD,MAAMC,cAAAA,GAAiBd,QAAQO,GAAAA,CAAIQ,OAAO,CAACC,IAAI,CAACC,KAAK,CAAA,IAAK,mBAAA;AAC1D,gBAAA,MAAMC,WAAAA,GAAcpB,QAAAA,CAASS,GAAAA,CAAIQ,OAAO,CAACrB,IAAI,CAAA,GACzCM,OAAAA,CAAQN,IAAAA,CAAKyB,SAAS,CAACZ,GAAAA,CAAIQ,OAAO,CAACrB,IAAI,CAAA,CAAA,GACvC,aAAA;AAEJ,gBAAA,MAAM0B,UAAAA,GAAa;oBACjBC,QAAAA,EAAU;wBAAEC,GAAAA,EAAK;AAAC,qBAAA;oBAClBC,GAAAA,EAAK,CAAA;oBACLC,SAAAA,EAAW,CAAA,EAAGV,cAAAA,CAAe,CAAC,EAAEI,WAAAA,CAAY,CAAC,EAAEX,GAAAA,CAAIQ,OAAO,CAACU,EAAE,CAAA,CAAE;AAC/DC,oBAAAA,OAAAA,CAAAA,GAAAA;AACE,wBAAA,MAAM,IAAIxB,cAAAA,EAAAA;AACpB,oBAAA,CAAA;AACQ,oBAAA,GAAGO,eAAe;AAClB,oBAAA,GAAGJ;AACX,iBAAA;AAEM,gBAAA,OAAOD,SAAAA,CAAUuB,UAAU,CAACP,UAAAA,CAAAA,CAAYb,GAAAA,EAAKC,IAAAA,CAAAA;AACnD,YAAA;YAEI,OAAOA,IAAAA,EAAAA;AACX,QAAA,CAAA;;;;;;"}
1
+ {"version":3,"file":"rateLimit.js","sources":["../../../server/middlewares/rateLimit.js"],"sourcesContent":["'use strict';\n\nconst path = require('path');\nconst utils = require('@strapi/utils');\nconst { isString, has, toLower } = require('lodash/fp');\n\nconst { RateLimitError } = utils.errors;\n\n/**\n * Routes where the rate-limit key MUST NOT include a user identifier\n * derived from `ctx.request.body.email`.\n *\n * On these routes the request body either has no `email` field\n * (e.g. /auth/local uses `identifier`, /auth/reset-password uses\n * `code`, /auth/change-password uses `currentPassword`) or the\n * field is not part of the route contract. Including the\n * attacker-controlled `body.email` in the rate-limit key on these\n * routes lets a caller obtain a fresh key on every request by\n * varying that field, effectively bypassing per-IP throttling.\n *\n * Comparison uses endsWith so the check is stable under any router\n * mount prefix (e.g. `/api/auth/local`).\n *\n * @see https://github.com/strapi/strapi/security/advisories/GHSA-7mqx-wwh4-f9fw\n *\n * When adding a new `rateLimit`-protected auth route whose body does not\n * use `email` as the real identifier, add its path suffix here (or an\n * equivalent `routeUsesEmailIdentifier` rule) so the key cannot be split\n * with arbitrary `body.email` values.\n */\nconst ROUTES_WITHOUT_IDENTIFIER = ['/auth/local', '/auth/reset-password', '/auth/change-password'];\n\nconst isOAuthCallbackPath = (requestPath) => requestPath.includes('/connect/');\n\nconst routeUsesEmailIdentifier = (requestPath) => {\n if (isOAuthCallbackPath(requestPath)) {\n return false;\n }\n\n return !ROUTES_WITHOUT_IDENTIFIER.some((route) => requestPath.endsWith(route));\n};\n\n/**\n * Paths suitable for route matching and prefix keys: POSIX-normalized,\n * lower-cased, trailing slashes removed so `/api/auth/local` and\n * `/api/auth/local/` share one bucket.\n */\nconst normalizeRequestPathForRateLimit = (requestPath) => {\n const normalized = path.normalize(requestPath);\n const lower = toLower(normalized);\n return lower.replace(/\\/+$/, '') || '/';\n};\n\nconst getEmailIdentifierForKey = (body) => {\n if (!body || !isString(body.email) || body.email === '') {\n return 'unknownIdentifier';\n }\n\n return toLower(body.email);\n};\n\nconst buildPrefixKey = (ctx) => {\n let requestPath;\n if (!isString(ctx.request.path)) {\n requestPath = 'invalidPath';\n } else {\n requestPath = normalizeRequestPathForRateLimit(ctx.request.path);\n if (requestPath === '.' || requestPath === '..') {\n requestPath = 'invalidPath';\n }\n }\n\n if (!routeUsesEmailIdentifier(requestPath)) {\n return `noIdentifier:${requestPath}:${ctx.request.ip}`;\n }\n\n const userIdentifier = getEmailIdentifierForKey(ctx.request.body);\n return `${userIdentifier}:${requestPath}:${ctx.request.ip}`;\n};\n\nconst buildRateLimitLoadConfig = (ctx, rateLimitConfig, routeMiddlewareConfig) => {\n return {\n interval: { min: 5 },\n max: 5,\n ...rateLimitConfig,\n ...routeMiddlewareConfig,\n handler() {\n throw new RateLimitError();\n },\n prefixKey: buildPrefixKey(ctx),\n };\n};\n\nmodule.exports =\n (config, { strapi }) =>\n async (ctx, next) => {\n let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');\n\n if (!rateLimitConfig) {\n rateLimitConfig = {\n enabled: true,\n };\n }\n\n if (!has('enabled', rateLimitConfig)) {\n rateLimitConfig.enabled = true;\n }\n\n if (rateLimitConfig.enabled === true) {\n const rateLimit = require('koa2-ratelimit').RateLimit;\n\n const loadConfig = buildRateLimitLoadConfig(ctx, rateLimitConfig, config);\n\n return rateLimit.middleware(loadConfig)(ctx, next);\n }\n\n return next();\n };\n\nmodule.exports.buildPrefixKey = buildPrefixKey;\nmodule.exports.ROUTES_WITHOUT_IDENTIFIER = ROUTES_WITHOUT_IDENTIFIER;\nmodule.exports.normalizeRequestPathForRateLimit = normalizeRequestPathForRateLimit;\nmodule.exports.buildRateLimitLoadConfig = buildRateLimitLoadConfig;\n"],"names":["path","require$$0","utils","require$$1","isString","has","toLower","require$$2","RateLimitError","errors","ROUTES_WITHOUT_IDENTIFIER","isOAuthCallbackPath","requestPath","includes","routeUsesEmailIdentifier","some","route","endsWith","normalizeRequestPathForRateLimit","normalized","normalize","lower","replace","getEmailIdentifierForKey","body","email","buildPrefixKey","ctx","request","ip","userIdentifier","buildRateLimitLoadConfig","rateLimitConfig","routeMiddlewareConfig","interval","min","max","handler","prefixKey","rateLimitModule","exports","config","strapi","next","get","enabled","rateLimit","require$$3","RateLimit","loadConfig","middleware"],"mappings":";;;;;;;;;;;;AAEA,IAAA,MAAMA,IAAAA,GAAOC,YAAAA;AACb,IAAA,MAAMC,KAAAA,GAAQC,UAAAA;AACd,IAAA,MAAM,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,OAAO,EAAE,GAAGC,UAAAA;AAEnC,IAAA,MAAM,EAAEC,cAAc,EAAE,GAAGN,MAAMO,MAAM;AAEvC;;;;;;;;;;;;;;;;;;;;;AAqBA,KACA,MAAMC,yBAAAA,GAA4B;AAAC,QAAA,aAAA;AAAe,QAAA,sBAAA;AAAwB,QAAA;AAAwB,KAAA;AAElG,IAAA,MAAMC,mBAAAA,GAAsB,CAACC,WAAAA,GAAgBA,WAAAA,CAAYC,QAAQ,CAAC,WAAA,CAAA;AAElE,IAAA,MAAMC,2BAA2B,CAACF,WAAAA,GAAAA;AAChC,QAAA,IAAID,oBAAoBC,WAAAA,CAAAA,EAAc;YACpC,OAAO,KAAA;AACX,QAAA;QAEE,OAAO,CAACF,0BAA0BK,IAAI,CAAC,CAACC,KAAAA,GAAUJ,WAAAA,CAAYK,QAAQ,CAACD,KAAAA,CAAAA,CAAAA;AACzE,IAAA,CAAA;AAEA;;;;KAKA,MAAME,mCAAmC,CAACN,WAAAA,GAAAA;QACxC,MAAMO,UAAAA,GAAanB,IAAAA,CAAKoB,SAAS,CAACR,WAAAA,CAAAA;AAClC,QAAA,MAAMS,QAAQf,OAAAA,CAAQa,UAAAA,CAAAA;AACtB,QAAA,OAAOE,KAAAA,CAAMC,OAAO,CAAC,MAAA,EAAQ,EAAA,CAAA,IAAO,GAAA;AACtC,IAAA,CAAA;AAEA,IAAA,MAAMC,2BAA2B,CAACC,IAAAA,GAAAA;QAChC,IAAI,CAACA,IAAAA,IAAQ,CAACpB,QAAAA,CAASoB,IAAAA,CAAKC,KAAK,CAAA,IAAKD,IAAAA,CAAKC,KAAK,KAAK,EAAA,EAAI;YACvD,OAAO,mBAAA;AACX,QAAA;QAEE,OAAOnB,OAAAA,CAAQkB,KAAKC,KAAK,CAAA;AAC3B,IAAA,CAAA;AAEA,IAAA,MAAMC,iBAAiB,CAACC,GAAAA,GAAAA;QACtB,IAAIf,WAAAA;AACJ,QAAA,IAAI,CAACR,QAAAA,CAASuB,GAAAA,CAAIC,OAAO,CAAC5B,IAAI,CAAA,EAAG;YAC/BY,WAAAA,GAAc,aAAA;QAClB,CAAA,MAAS;AACLA,YAAAA,WAAAA,GAAcM,gCAAAA,CAAiCS,GAAAA,CAAIC,OAAO,CAAC5B,IAAI,CAAA;YAC/D,IAAIY,WAAAA,KAAgB,GAAA,IAAOA,WAAAA,KAAgB,IAAA,EAAM;gBAC/CA,WAAAA,GAAc,aAAA;AACpB,YAAA;AACA,QAAA;QAEE,IAAI,CAACE,yBAAyBF,WAAAA,CAAAA,EAAc;YAC1C,OAAO,CAAC,aAAa,EAAEA,WAAAA,CAAY,CAAC,EAAEe,GAAAA,CAAIC,OAAO,CAACC,EAAE,CAAA,CAAE;AAC1D,QAAA;AAEE,QAAA,MAAMC,cAAAA,GAAiBP,wBAAAA,CAAyBI,GAAAA,CAAIC,OAAO,CAACJ,IAAI,CAAA;QAChE,OAAO,CAAA,EAAGM,cAAAA,CAAe,CAAC,EAAElB,WAAAA,CAAY,CAAC,EAAEe,GAAAA,CAAIC,OAAO,CAACC,EAAE,CAAA,CAAE;AAC7D,IAAA,CAAA;IAEA,MAAME,wBAAAA,GAA2B,CAACJ,GAAAA,EAAKK,eAAAA,EAAiBC,qBAAAA,GAAAA;QACtD,OAAO;YACLC,QAAAA,EAAU;gBAAEC,GAAAA,EAAK;AAAC,aAAA;YAClBC,GAAAA,EAAK,CAAA;AACL,YAAA,GAAGJ,eAAe;AAClB,YAAA,GAAGC,qBAAqB;AACxBI,YAAAA,OAAAA,CAAAA,GAAAA;AACE,gBAAA,MAAM,IAAI7B,cAAAA,EAAAA;AAChB,YAAA,CAAA;AACI8B,YAAAA,SAAAA,EAAWZ,cAAAA,CAAeC,GAAAA;AAC9B,SAAA;AACA,IAAA,CAAA;IAEAY,kBAAAA,CAAAC,OAAc,GACZ,CAACC,MAAAA,EAAQ,EAAEC,MAAM,EAAE,GACnB,OAAOf,GAAAA,EAAKgB,IAAAA,GAAAA;AACV,YAAA,IAAIX,eAAAA,GAAkBU,MAAAA,CAAOD,MAAM,CAACG,GAAG,CAAC,qCAAA,CAAA;AAExC,YAAA,IAAI,CAACZ,eAAAA,EAAiB;gBACpBA,eAAAA,GAAkB;oBAChBa,OAAAA,EAAS;AACjB,iBAAA;AACA,YAAA;YAEI,IAAI,CAACxC,GAAAA,CAAI,SAAA,EAAW2B,eAAAA,CAAAA,EAAkB;AACpCA,gBAAAA,eAAAA,CAAgBa,OAAO,GAAG,IAAA;AAChC,YAAA;YAEI,IAAIb,eAAAA,CAAgBa,OAAO,KAAK,IAAA,EAAM;gBACpC,MAAMC,SAAAA,GAAYC,WAA0BC,SAAS;gBAErD,MAAMC,UAAAA,GAAalB,wBAAAA,CAAyBJ,GAAAA,EAAKK,eAAAA,EAAiBS,MAAAA,CAAAA;AAElE,gBAAA,OAAOK,SAAAA,CAAUI,UAAU,CAACD,UAAAA,CAAAA,CAAYtB,GAAAA,EAAKgB,IAAAA,CAAAA;AACnD,YAAA;YAEI,OAAOA,IAAAA,EAAAA;AACX,QAAA,CAAA;IAEAJ,kBAAAA,CAAAC,OAAA,CAAAd,cAA6B,GAAGA,cAAAA;IAChCa,kBAAAA,CAAAC,OAAA,CAAA9B,yBAAwC,GAAGA,yBAAAA;IAC3C6B,kBAAAA,CAAAC,OAAA,CAAAtB,gCAA+C,GAAGA,gCAAAA;IAClDqB,kBAAAA,CAAAC,OAAA,CAAAT,wBAAuC,GAAGA,wBAAAA;;;;;;"}
@@ -1,18 +1,96 @@
1
+ import { __module as rateLimit } from '../_virtual/rateLimit.mjs';
1
2
  import require$$1$1 from 'path';
2
3
  import require$$1 from '@strapi/utils';
3
4
  import require$$0 from 'lodash/fp';
4
5
  import require$$3 from 'koa2-ratelimit';
5
6
 
6
- var rateLimit;
7
7
  var hasRequiredRateLimit;
8
8
  function requireRateLimit() {
9
- if (hasRequiredRateLimit) return rateLimit;
9
+ if (hasRequiredRateLimit) return rateLimit.exports;
10
10
  hasRequiredRateLimit = 1;
11
11
  const path = require$$1$1;
12
12
  const utils = require$$1;
13
13
  const { isString, has, toLower } = require$$0;
14
14
  const { RateLimitError } = utils.errors;
15
- rateLimit = (config, { strapi })=>async (ctx, next)=>{
15
+ /**
16
+ * Routes where the rate-limit key MUST NOT include a user identifier
17
+ * derived from `ctx.request.body.email`.
18
+ *
19
+ * On these routes the request body either has no `email` field
20
+ * (e.g. /auth/local uses `identifier`, /auth/reset-password uses
21
+ * `code`, /auth/change-password uses `currentPassword`) or the
22
+ * field is not part of the route contract. Including the
23
+ * attacker-controlled `body.email` in the rate-limit key on these
24
+ * routes lets a caller obtain a fresh key on every request by
25
+ * varying that field, effectively bypassing per-IP throttling.
26
+ *
27
+ * Comparison uses endsWith so the check is stable under any router
28
+ * mount prefix (e.g. `/api/auth/local`).
29
+ *
30
+ * @see https://github.com/strapi/strapi/security/advisories/GHSA-7mqx-wwh4-f9fw
31
+ *
32
+ * When adding a new `rateLimit`-protected auth route whose body does not
33
+ * use `email` as the real identifier, add its path suffix here (or an
34
+ * equivalent `routeUsesEmailIdentifier` rule) so the key cannot be split
35
+ * with arbitrary `body.email` values.
36
+ */ const ROUTES_WITHOUT_IDENTIFIER = [
37
+ '/auth/local',
38
+ '/auth/reset-password',
39
+ '/auth/change-password'
40
+ ];
41
+ const isOAuthCallbackPath = (requestPath)=>requestPath.includes('/connect/');
42
+ const routeUsesEmailIdentifier = (requestPath)=>{
43
+ if (isOAuthCallbackPath(requestPath)) {
44
+ return false;
45
+ }
46
+ return !ROUTES_WITHOUT_IDENTIFIER.some((route)=>requestPath.endsWith(route));
47
+ };
48
+ /**
49
+ * Paths suitable for route matching and prefix keys: POSIX-normalized,
50
+ * lower-cased, trailing slashes removed so `/api/auth/local` and
51
+ * `/api/auth/local/` share one bucket.
52
+ */ const normalizeRequestPathForRateLimit = (requestPath)=>{
53
+ const normalized = path.normalize(requestPath);
54
+ const lower = toLower(normalized);
55
+ return lower.replace(/\/+$/, '') || '/';
56
+ };
57
+ const getEmailIdentifierForKey = (body)=>{
58
+ if (!body || !isString(body.email) || body.email === '') {
59
+ return 'unknownIdentifier';
60
+ }
61
+ return toLower(body.email);
62
+ };
63
+ const buildPrefixKey = (ctx)=>{
64
+ let requestPath;
65
+ if (!isString(ctx.request.path)) {
66
+ requestPath = 'invalidPath';
67
+ } else {
68
+ requestPath = normalizeRequestPathForRateLimit(ctx.request.path);
69
+ if (requestPath === '.' || requestPath === '..') {
70
+ requestPath = 'invalidPath';
71
+ }
72
+ }
73
+ if (!routeUsesEmailIdentifier(requestPath)) {
74
+ return `noIdentifier:${requestPath}:${ctx.request.ip}`;
75
+ }
76
+ const userIdentifier = getEmailIdentifierForKey(ctx.request.body);
77
+ return `${userIdentifier}:${requestPath}:${ctx.request.ip}`;
78
+ };
79
+ const buildRateLimitLoadConfig = (ctx, rateLimitConfig, routeMiddlewareConfig)=>{
80
+ return {
81
+ interval: {
82
+ min: 5
83
+ },
84
+ max: 5,
85
+ ...rateLimitConfig,
86
+ ...routeMiddlewareConfig,
87
+ handler () {
88
+ throw new RateLimitError();
89
+ },
90
+ prefixKey: buildPrefixKey(ctx)
91
+ };
92
+ };
93
+ rateLimit.exports = (config, { strapi })=>async (ctx, next)=>{
16
94
  let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');
17
95
  if (!rateLimitConfig) {
18
96
  rateLimitConfig = {
@@ -24,25 +102,16 @@ function requireRateLimit() {
24
102
  }
25
103
  if (rateLimitConfig.enabled === true) {
26
104
  const rateLimit = require$$3.RateLimit;
27
- const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';
28
- const requestPath = isString(ctx.request.path) ? toLower(path.normalize(ctx.request.path)) : 'invalidPath';
29
- const loadConfig = {
30
- interval: {
31
- min: 5
32
- },
33
- max: 5,
34
- prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,
35
- handler () {
36
- throw new RateLimitError();
37
- },
38
- ...rateLimitConfig,
39
- ...config
40
- };
105
+ const loadConfig = buildRateLimitLoadConfig(ctx, rateLimitConfig, config);
41
106
  return rateLimit.middleware(loadConfig)(ctx, next);
42
107
  }
43
108
  return next();
44
109
  };
45
- return rateLimit;
110
+ rateLimit.exports.buildPrefixKey = buildPrefixKey;
111
+ rateLimit.exports.ROUTES_WITHOUT_IDENTIFIER = ROUTES_WITHOUT_IDENTIFIER;
112
+ rateLimit.exports.normalizeRequestPathForRateLimit = normalizeRequestPathForRateLimit;
113
+ rateLimit.exports.buildRateLimitLoadConfig = buildRateLimitLoadConfig;
114
+ return rateLimit.exports;
46
115
  }
47
116
 
48
117
  export { requireRateLimit as __require };
@@ -1 +1 @@
1
- {"version":3,"file":"rateLimit.mjs","sources":["../../../server/middlewares/rateLimit.js"],"sourcesContent":["'use strict';\n\nconst path = require('path');\nconst utils = require('@strapi/utils');\nconst { isString, has, toLower } = require('lodash/fp');\n\nconst { RateLimitError } = utils.errors;\n\nmodule.exports =\n (config, { strapi }) =>\n async (ctx, next) => {\n let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');\n\n if (!rateLimitConfig) {\n rateLimitConfig = {\n enabled: true,\n };\n }\n\n if (!has('enabled', rateLimitConfig)) {\n rateLimitConfig.enabled = true;\n }\n\n if (rateLimitConfig.enabled === true) {\n const rateLimit = require('koa2-ratelimit').RateLimit;\n\n const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';\n const requestPath = isString(ctx.request.path)\n ? toLower(path.normalize(ctx.request.path))\n : 'invalidPath';\n\n const loadConfig = {\n interval: { min: 5 },\n max: 5,\n prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,\n handler() {\n throw new RateLimitError();\n },\n ...rateLimitConfig,\n ...config,\n };\n\n return rateLimit.middleware(loadConfig)(ctx, next);\n }\n\n return next();\n };\n"],"names":["path","require$$0","utils","require$$1","isString","has","toLower","require$$2","RateLimitError","errors","rateLimit","config","strapi","ctx","next","rateLimitConfig","get","enabled","require$$3","RateLimit","userIdentifier","request","body","email","requestPath","normalize","loadConfig","interval","min","max","prefixKey","ip","handler","middleware"],"mappings":";;;;;;;;;;AAEA,IAAA,MAAMA,IAAAA,GAAOC,YAAAA;AACb,IAAA,MAAMC,KAAAA,GAAQC,UAAAA;AACd,IAAA,MAAM,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,OAAO,EAAE,GAAGC,UAAAA;AAEnC,IAAA,MAAM,EAAEC,cAAc,EAAE,GAAGN,MAAMO,MAAM;AAEvCC,IAAAA,SAAAA,GACE,CAACC,MAAAA,EAAQ,EAAEC,MAAM,EAAE,GACnB,OAAOC,GAAAA,EAAKC,IAAAA,GAAAA;AACV,YAAA,IAAIC,eAAAA,GAAkBH,MAAAA,CAAOD,MAAM,CAACK,GAAG,CAAC,qCAAA,CAAA;AAExC,YAAA,IAAI,CAACD,eAAAA,EAAiB;gBACpBA,eAAAA,GAAkB;oBAChBE,OAAAA,EAAS;AACjB,iBAAA;AACA,YAAA;YAEI,IAAI,CAACZ,GAAAA,CAAI,SAAA,EAAWU,eAAAA,CAAAA,EAAkB;AACpCA,gBAAAA,eAAAA,CAAgBE,OAAO,GAAG,IAAA;AAChC,YAAA;YAEI,IAAIF,eAAAA,CAAgBE,OAAO,KAAK,IAAA,EAAM;gBACpC,MAAMP,SAAAA,GAAYQ,WAA0BC,SAAS;gBAErD,MAAMC,cAAAA,GAAiBd,QAAQO,GAAAA,CAAIQ,OAAO,CAACC,IAAI,CAACC,KAAK,CAAA,IAAK,mBAAA;AAC1D,gBAAA,MAAMC,WAAAA,GAAcpB,QAAAA,CAASS,GAAAA,CAAIQ,OAAO,CAACrB,IAAI,CAAA,GACzCM,OAAAA,CAAQN,IAAAA,CAAKyB,SAAS,CAACZ,GAAAA,CAAIQ,OAAO,CAACrB,IAAI,CAAA,CAAA,GACvC,aAAA;AAEJ,gBAAA,MAAM0B,UAAAA,GAAa;oBACjBC,QAAAA,EAAU;wBAAEC,GAAAA,EAAK;AAAC,qBAAA;oBAClBC,GAAAA,EAAK,CAAA;oBACLC,SAAAA,EAAW,CAAA,EAAGV,cAAAA,CAAe,CAAC,EAAEI,WAAAA,CAAY,CAAC,EAAEX,GAAAA,CAAIQ,OAAO,CAACU,EAAE,CAAA,CAAE;AAC/DC,oBAAAA,OAAAA,CAAAA,GAAAA;AACE,wBAAA,MAAM,IAAIxB,cAAAA,EAAAA;AACpB,oBAAA,CAAA;AACQ,oBAAA,GAAGO,eAAe;AAClB,oBAAA,GAAGJ;AACX,iBAAA;AAEM,gBAAA,OAAOD,SAAAA,CAAUuB,UAAU,CAACP,UAAAA,CAAAA,CAAYb,GAAAA,EAAKC,IAAAA,CAAAA;AACnD,YAAA;YAEI,OAAOA,IAAAA,EAAAA;AACX,QAAA,CAAA;;;;;;"}
1
+ {"version":3,"file":"rateLimit.mjs","sources":["../../../server/middlewares/rateLimit.js"],"sourcesContent":["'use strict';\n\nconst path = require('path');\nconst utils = require('@strapi/utils');\nconst { isString, has, toLower } = require('lodash/fp');\n\nconst { RateLimitError } = utils.errors;\n\n/**\n * Routes where the rate-limit key MUST NOT include a user identifier\n * derived from `ctx.request.body.email`.\n *\n * On these routes the request body either has no `email` field\n * (e.g. /auth/local uses `identifier`, /auth/reset-password uses\n * `code`, /auth/change-password uses `currentPassword`) or the\n * field is not part of the route contract. Including the\n * attacker-controlled `body.email` in the rate-limit key on these\n * routes lets a caller obtain a fresh key on every request by\n * varying that field, effectively bypassing per-IP throttling.\n *\n * Comparison uses endsWith so the check is stable under any router\n * mount prefix (e.g. `/api/auth/local`).\n *\n * @see https://github.com/strapi/strapi/security/advisories/GHSA-7mqx-wwh4-f9fw\n *\n * When adding a new `rateLimit`-protected auth route whose body does not\n * use `email` as the real identifier, add its path suffix here (or an\n * equivalent `routeUsesEmailIdentifier` rule) so the key cannot be split\n * with arbitrary `body.email` values.\n */\nconst ROUTES_WITHOUT_IDENTIFIER = ['/auth/local', '/auth/reset-password', '/auth/change-password'];\n\nconst isOAuthCallbackPath = (requestPath) => requestPath.includes('/connect/');\n\nconst routeUsesEmailIdentifier = (requestPath) => {\n if (isOAuthCallbackPath(requestPath)) {\n return false;\n }\n\n return !ROUTES_WITHOUT_IDENTIFIER.some((route) => requestPath.endsWith(route));\n};\n\n/**\n * Paths suitable for route matching and prefix keys: POSIX-normalized,\n * lower-cased, trailing slashes removed so `/api/auth/local` and\n * `/api/auth/local/` share one bucket.\n */\nconst normalizeRequestPathForRateLimit = (requestPath) => {\n const normalized = path.normalize(requestPath);\n const lower = toLower(normalized);\n return lower.replace(/\\/+$/, '') || '/';\n};\n\nconst getEmailIdentifierForKey = (body) => {\n if (!body || !isString(body.email) || body.email === '') {\n return 'unknownIdentifier';\n }\n\n return toLower(body.email);\n};\n\nconst buildPrefixKey = (ctx) => {\n let requestPath;\n if (!isString(ctx.request.path)) {\n requestPath = 'invalidPath';\n } else {\n requestPath = normalizeRequestPathForRateLimit(ctx.request.path);\n if (requestPath === '.' || requestPath === '..') {\n requestPath = 'invalidPath';\n }\n }\n\n if (!routeUsesEmailIdentifier(requestPath)) {\n return `noIdentifier:${requestPath}:${ctx.request.ip}`;\n }\n\n const userIdentifier = getEmailIdentifierForKey(ctx.request.body);\n return `${userIdentifier}:${requestPath}:${ctx.request.ip}`;\n};\n\nconst buildRateLimitLoadConfig = (ctx, rateLimitConfig, routeMiddlewareConfig) => {\n return {\n interval: { min: 5 },\n max: 5,\n ...rateLimitConfig,\n ...routeMiddlewareConfig,\n handler() {\n throw new RateLimitError();\n },\n prefixKey: buildPrefixKey(ctx),\n };\n};\n\nmodule.exports =\n (config, { strapi }) =>\n async (ctx, next) => {\n let rateLimitConfig = strapi.config.get('plugin::users-permissions.ratelimit');\n\n if (!rateLimitConfig) {\n rateLimitConfig = {\n enabled: true,\n };\n }\n\n if (!has('enabled', rateLimitConfig)) {\n rateLimitConfig.enabled = true;\n }\n\n if (rateLimitConfig.enabled === true) {\n const rateLimit = require('koa2-ratelimit').RateLimit;\n\n const loadConfig = buildRateLimitLoadConfig(ctx, rateLimitConfig, config);\n\n return rateLimit.middleware(loadConfig)(ctx, next);\n }\n\n return next();\n };\n\nmodule.exports.buildPrefixKey = buildPrefixKey;\nmodule.exports.ROUTES_WITHOUT_IDENTIFIER = ROUTES_WITHOUT_IDENTIFIER;\nmodule.exports.normalizeRequestPathForRateLimit = normalizeRequestPathForRateLimit;\nmodule.exports.buildRateLimitLoadConfig = buildRateLimitLoadConfig;\n"],"names":["path","require$$0","utils","require$$1","isString","has","toLower","require$$2","RateLimitError","errors","ROUTES_WITHOUT_IDENTIFIER","isOAuthCallbackPath","requestPath","includes","routeUsesEmailIdentifier","some","route","endsWith","normalizeRequestPathForRateLimit","normalized","normalize","lower","replace","getEmailIdentifierForKey","body","email","buildPrefixKey","ctx","request","ip","userIdentifier","buildRateLimitLoadConfig","rateLimitConfig","routeMiddlewareConfig","interval","min","max","handler","prefixKey","rateLimitModule","exports","config","strapi","next","get","enabled","rateLimit","require$$3","RateLimit","loadConfig","middleware"],"mappings":";;;;;;;;;;AAEA,IAAA,MAAMA,IAAAA,GAAOC,YAAAA;AACb,IAAA,MAAMC,KAAAA,GAAQC,UAAAA;AACd,IAAA,MAAM,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,OAAO,EAAE,GAAGC,UAAAA;AAEnC,IAAA,MAAM,EAAEC,cAAc,EAAE,GAAGN,MAAMO,MAAM;AAEvC;;;;;;;;;;;;;;;;;;;;;AAqBA,KACA,MAAMC,yBAAAA,GAA4B;AAAC,QAAA,aAAA;AAAe,QAAA,sBAAA;AAAwB,QAAA;AAAwB,KAAA;AAElG,IAAA,MAAMC,mBAAAA,GAAsB,CAACC,WAAAA,GAAgBA,WAAAA,CAAYC,QAAQ,CAAC,WAAA,CAAA;AAElE,IAAA,MAAMC,2BAA2B,CAACF,WAAAA,GAAAA;AAChC,QAAA,IAAID,oBAAoBC,WAAAA,CAAAA,EAAc;YACpC,OAAO,KAAA;AACX,QAAA;QAEE,OAAO,CAACF,0BAA0BK,IAAI,CAAC,CAACC,KAAAA,GAAUJ,WAAAA,CAAYK,QAAQ,CAACD,KAAAA,CAAAA,CAAAA;AACzE,IAAA,CAAA;AAEA;;;;KAKA,MAAME,mCAAmC,CAACN,WAAAA,GAAAA;QACxC,MAAMO,UAAAA,GAAanB,IAAAA,CAAKoB,SAAS,CAACR,WAAAA,CAAAA;AAClC,QAAA,MAAMS,QAAQf,OAAAA,CAAQa,UAAAA,CAAAA;AACtB,QAAA,OAAOE,KAAAA,CAAMC,OAAO,CAAC,MAAA,EAAQ,EAAA,CAAA,IAAO,GAAA;AACtC,IAAA,CAAA;AAEA,IAAA,MAAMC,2BAA2B,CAACC,IAAAA,GAAAA;QAChC,IAAI,CAACA,IAAAA,IAAQ,CAACpB,QAAAA,CAASoB,IAAAA,CAAKC,KAAK,CAAA,IAAKD,IAAAA,CAAKC,KAAK,KAAK,EAAA,EAAI;YACvD,OAAO,mBAAA;AACX,QAAA;QAEE,OAAOnB,OAAAA,CAAQkB,KAAKC,KAAK,CAAA;AAC3B,IAAA,CAAA;AAEA,IAAA,MAAMC,iBAAiB,CAACC,GAAAA,GAAAA;QACtB,IAAIf,WAAAA;AACJ,QAAA,IAAI,CAACR,QAAAA,CAASuB,GAAAA,CAAIC,OAAO,CAAC5B,IAAI,CAAA,EAAG;YAC/BY,WAAAA,GAAc,aAAA;QAClB,CAAA,MAAS;AACLA,YAAAA,WAAAA,GAAcM,gCAAAA,CAAiCS,GAAAA,CAAIC,OAAO,CAAC5B,IAAI,CAAA;YAC/D,IAAIY,WAAAA,KAAgB,GAAA,IAAOA,WAAAA,KAAgB,IAAA,EAAM;gBAC/CA,WAAAA,GAAc,aAAA;AACpB,YAAA;AACA,QAAA;QAEE,IAAI,CAACE,yBAAyBF,WAAAA,CAAAA,EAAc;YAC1C,OAAO,CAAC,aAAa,EAAEA,WAAAA,CAAY,CAAC,EAAEe,GAAAA,CAAIC,OAAO,CAACC,EAAE,CAAA,CAAE;AAC1D,QAAA;AAEE,QAAA,MAAMC,cAAAA,GAAiBP,wBAAAA,CAAyBI,GAAAA,CAAIC,OAAO,CAACJ,IAAI,CAAA;QAChE,OAAO,CAAA,EAAGM,cAAAA,CAAe,CAAC,EAAElB,WAAAA,CAAY,CAAC,EAAEe,GAAAA,CAAIC,OAAO,CAACC,EAAE,CAAA,CAAE;AAC7D,IAAA,CAAA;IAEA,MAAME,wBAAAA,GAA2B,CAACJ,GAAAA,EAAKK,eAAAA,EAAiBC,qBAAAA,GAAAA;QACtD,OAAO;YACLC,QAAAA,EAAU;gBAAEC,GAAAA,EAAK;AAAC,aAAA;YAClBC,GAAAA,EAAK,CAAA;AACL,YAAA,GAAGJ,eAAe;AAClB,YAAA,GAAGC,qBAAqB;AACxBI,YAAAA,OAAAA,CAAAA,GAAAA;AACE,gBAAA,MAAM,IAAI7B,cAAAA,EAAAA;AAChB,YAAA,CAAA;AACI8B,YAAAA,SAAAA,EAAWZ,cAAAA,CAAeC,GAAAA;AAC9B,SAAA;AACA,IAAA,CAAA;IAEAY,SAAAA,CAAAC,OAAc,GACZ,CAACC,MAAAA,EAAQ,EAAEC,MAAM,EAAE,GACnB,OAAOf,GAAAA,EAAKgB,IAAAA,GAAAA;AACV,YAAA,IAAIX,eAAAA,GAAkBU,MAAAA,CAAOD,MAAM,CAACG,GAAG,CAAC,qCAAA,CAAA;AAExC,YAAA,IAAI,CAACZ,eAAAA,EAAiB;gBACpBA,eAAAA,GAAkB;oBAChBa,OAAAA,EAAS;AACjB,iBAAA;AACA,YAAA;YAEI,IAAI,CAACxC,GAAAA,CAAI,SAAA,EAAW2B,eAAAA,CAAAA,EAAkB;AACpCA,gBAAAA,eAAAA,CAAgBa,OAAO,GAAG,IAAA;AAChC,YAAA;YAEI,IAAIb,eAAAA,CAAgBa,OAAO,KAAK,IAAA,EAAM;gBACpC,MAAMC,SAAAA,GAAYC,WAA0BC,SAAS;gBAErD,MAAMC,UAAAA,GAAalB,wBAAAA,CAAyBJ,GAAAA,EAAKK,eAAAA,EAAiBS,MAAAA,CAAAA;AAElE,gBAAA,OAAOK,SAAAA,CAAUI,UAAU,CAACD,UAAAA,CAAAA,CAAYtB,GAAAA,EAAKgB,IAAAA,CAAAA;AACnD,YAAA;YAEI,OAAOA,IAAAA,EAAAA;AACX,QAAA,CAAA;IAEAJ,SAAAA,CAAAC,OAAA,CAAAd,cAA6B,GAAGA,cAAAA;IAChCa,SAAAA,CAAAC,OAAA,CAAA9B,yBAAwC,GAAGA,yBAAAA;IAC3C6B,SAAAA,CAAAC,OAAA,CAAAtB,gCAA+C,GAAGA,gCAAAA;IAClDqB,SAAAA,CAAAC,OAAA,CAAAT,wBAAuC,GAAGA,wBAAAA;;;;;;"}
@@ -117,6 +117,9 @@ function requireAuth() {
117
117
  path: '/auth/send-email-confirmation',
118
118
  handler: 'auth.sendEmailConfirmation',
119
119
  config: {
120
+ middlewares: [
121
+ 'plugin::users-permissions.rateLimit'
122
+ ],
120
123
  prefix: ''
121
124
  },
122
125
  request: {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["../../../../server/routes/content-api/auth.js"],"sourcesContent":["'use strict';\n\nconst { UsersPermissionsRouteValidator } = require('./validation');\n\nmodule.exports = (strapi) => {\n const validator = new UsersPermissionsRouteValidator(strapi);\n\n return [\n {\n method: 'GET',\n path: '/connect/(.*)',\n handler: 'auth.connect',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/local',\n handler: 'auth.callback',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.loginBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/local/register',\n handler: 'auth.register',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.registerBodySchema },\n },\n response: validator.authRegisterResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/:provider/callback',\n handler: 'auth.callback',\n config: {\n prefix: '',\n },\n request: {\n params: {\n provider: validator.providerParam,\n },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/forgot-password',\n handler: 'auth.forgotPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.forgotPasswordBodySchema },\n },\n response: validator.forgotPasswordResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/reset-password',\n handler: 'auth.resetPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.resetPasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/email-confirmation',\n handler: 'auth.emailConfirmation',\n config: {\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/send-email-confirmation',\n handler: 'auth.sendEmailConfirmation',\n config: {\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.sendEmailConfirmationBodySchema },\n },\n response: validator.sendEmailConfirmationResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/change-password',\n handler: 'auth.changePassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.changePasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/refresh',\n handler: 'auth.refresh',\n config: { prefix: '' },\n },\n {\n method: 'POST',\n path: '/auth/logout',\n handler: 'auth.logout',\n config: { prefix: '' },\n },\n ];\n};\n"],"names":["UsersPermissionsRouteValidator","require$$0","auth","strapi","validator","method","path","handler","config","middlewares","prefix","request","body","loginBodySchema","response","authResponseSchema","registerBodySchema","authRegisterResponseSchema","params","provider","providerParam","forgotPasswordBodySchema","forgotPasswordResponseSchema","resetPasswordBodySchema","sendEmailConfirmationBodySchema","sendEmailConfirmationResponseSchema","changePasswordBodySchema"],"mappings":";;;;;;;;;IAEA,MAAM,EAAEA,8BAA8B,EAAE,GAAGC,oBAAAA,EAAAA;AAE3CC,IAAAA,IAAAA,GAAiB,CAACC,MAAAA,GAAAA;QAChB,MAAMC,SAAAA,GAAY,IAAIJ,8BAAAA,CAA+BG,MAAAA,CAAAA;QAErD,OAAO;AACL,YAAA;gBACEE,MAAAA,EAAQ,KAAA;gBACRC,IAAAA,EAAM,eAAA;gBACNC,OAAAA,EAAS,cAAA;gBACTC,MAAAA,EAAQ;oBACNC,WAAAA,EAAa;AAAC,wBAAA;AAAsC,qBAAA;oBACpDC,MAAAA,EAAQ;AAChB;AACA,aAAA;AACI,YAAA;gBACEL,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,aAAA;gBACNC,OAAAA,EAAS,eAAA;gBACTC,MAAAA,EAAQ;oBACNC,WAAAA,EAAa;AAAC,wBAAA;AAAsC,qBAAA;oBACpDC,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPC,IAAAA,EAAM;AAAE,wBAAA,kBAAA,EAAoBR,UAAUS;AAAe;AAC7D,iBAAA;AACMC,gBAAAA,QAAAA,EAAUV,UAAUW;AAC1B,aAAA;AACI,YAAA;gBACEV,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,sBAAA;gBACNC,OAAAA,EAAS,eAAA;gBACTC,MAAAA,EAAQ;oBACNC,WAAAA,EAAa;AAAC,wBAAA;AAAsC,qBAAA;oBACpDC,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPC,IAAAA,EAAM;AAAE,wBAAA,kBAAA,EAAoBR,UAAUY;AAAkB;AAChE,iBAAA;AACMF,gBAAAA,QAAAA,EAAUV,UAAUa;AAC1B,aAAA;AACI,YAAA;gBACEZ,MAAAA,EAAQ,KAAA;gBACRC,IAAAA,EAAM,0BAAA;gBACNC,OAAAA,EAAS,eAAA;gBACTC,MAAAA,EAAQ;oBACNE,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPO,MAAAA,EAAQ;AACNC,wBAAAA,QAAAA,EAAUf,UAAUgB;AAC9B;AACA,iBAAA;AACMN,gBAAAA,QAAAA,EAAUV,UAAUW;AAC1B,aAAA;AACI,YAAA;gBACEV,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,uBAAA;gBACNC,OAAAA,EAAS,qBAAA;gBACTC,MAAAA,EAAQ;oBACNC,WAAAA,EAAa;AAAC,wBAAA;AAAsC,qBAAA;oBACpDC,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPC,IAAAA,EAAM;AAAE,wBAAA,kBAAA,EAAoBR,UAAUiB;AAAwB;AACtE,iBAAA;AACMP,gBAAAA,QAAAA,EAAUV,UAAUkB;AAC1B,aAAA;AACI,YAAA;gBACEjB,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,sBAAA;gBACNC,OAAAA,EAAS,oBAAA;gBACTC,MAAAA,EAAQ;oBACNC,WAAAA,EAAa;AAAC,wBAAA;AAAsC,qBAAA;oBACpDC,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPC,IAAAA,EAAM;AAAE,wBAAA,kBAAA,EAAoBR,UAAUmB;AAAuB;AACrE,iBAAA;AACMT,gBAAAA,QAAAA,EAAUV,UAAUW;AAC1B,aAAA;AACI,YAAA;gBACEV,MAAAA,EAAQ,KAAA;gBACRC,IAAAA,EAAM,0BAAA;gBACNC,OAAAA,EAAS,wBAAA;gBACTC,MAAAA,EAAQ;oBACNE,MAAAA,EAAQ;AAChB;AACA,aAAA;AACI,YAAA;gBACEL,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,+BAAA;gBACNC,OAAAA,EAAS,4BAAA;gBACTC,MAAAA,EAAQ;oBACNE,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPC,IAAAA,EAAM;AAAE,wBAAA,kBAAA,EAAoBR,UAAUoB;AAA+B;AAC7E,iBAAA;AACMV,gBAAAA,QAAAA,EAAUV,UAAUqB;AAC1B,aAAA;AACI,YAAA;gBACEpB,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,uBAAA;gBACNC,OAAAA,EAAS,qBAAA;gBACTC,MAAAA,EAAQ;oBACNC,WAAAA,EAAa;AAAC,wBAAA;AAAsC,qBAAA;oBACpDC,MAAAA,EAAQ;AAChB,iBAAA;gBACMC,OAAAA,EAAS;oBACPC,IAAAA,EAAM;AAAE,wBAAA,kBAAA,EAAoBR,UAAUsB;AAAwB;AACtE,iBAAA;AACMZ,gBAAAA,QAAAA,EAAUV,UAAUW;AAC1B,aAAA;AACI,YAAA;gBACEV,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,eAAA;gBACNC,OAAAA,EAAS,cAAA;gBACTC,MAAAA,EAAQ;oBAAEE,MAAAA,EAAQ;AAAE;AAC1B,aAAA;AACI,YAAA;gBACEL,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAM,cAAA;gBACNC,OAAAA,EAAS,aAAA;gBACTC,MAAAA,EAAQ;oBAAEE,MAAAA,EAAQ;AAAE;AAC1B;AACG,SAAA;AACH,IAAA,CAAA;;;;;;"}
1
+ {"version":3,"file":"auth.js","sources":["../../../../server/routes/content-api/auth.js"],"sourcesContent":["'use strict';\n\nconst { UsersPermissionsRouteValidator } = require('./validation');\n\nmodule.exports = (strapi) => {\n const validator = new UsersPermissionsRouteValidator(strapi);\n\n return [\n {\n method: 'GET',\n path: '/connect/(.*)',\n handler: 'auth.connect',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/local',\n handler: 'auth.callback',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.loginBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/local/register',\n handler: 'auth.register',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.registerBodySchema },\n },\n response: validator.authRegisterResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/:provider/callback',\n handler: 'auth.callback',\n config: {\n prefix: '',\n },\n request: {\n params: {\n provider: validator.providerParam,\n },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/forgot-password',\n handler: 'auth.forgotPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.forgotPasswordBodySchema },\n },\n response: validator.forgotPasswordResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/reset-password',\n handler: 'auth.resetPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.resetPasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/email-confirmation',\n handler: 'auth.emailConfirmation',\n config: {\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/send-email-confirmation',\n handler: 'auth.sendEmailConfirmation',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.sendEmailConfirmationBodySchema },\n },\n response: validator.sendEmailConfirmationResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/change-password',\n handler: 'auth.changePassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.changePasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/refresh',\n handler: 'auth.refresh',\n config: { prefix: '' },\n },\n {\n method: 'POST',\n path: '/auth/logout',\n handler: 'auth.logout',\n config: { prefix: '' },\n },\n ];\n};\n"],"names":["UsersPermissionsRouteValidator","require$$0","auth","strapi","validator","method","path","handler","config","middlewares","prefix","request","body","loginBodySchema","response","authResponseSchema","registerBodySchema","authRegisterResponseSchema","params","provider","providerParam","forgotPasswordBodySchema","forgotPasswordResponseSchema","resetPasswordBodySchema","sendEmailConfirmationBodySchema","sendEmailConfirmationResponseSchema","changePasswordBodySchema"],"mappings":"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"}
@@ -115,6 +115,9 @@ function requireAuth() {
115
115
  path: '/auth/send-email-confirmation',
116
116
  handler: 'auth.sendEmailConfirmation',
117
117
  config: {
118
+ middlewares: [
119
+ 'plugin::users-permissions.rateLimit'
120
+ ],
118
121
  prefix: ''
119
122
  },
120
123
  request: {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.mjs","sources":["../../../../server/routes/content-api/auth.js"],"sourcesContent":["'use strict';\n\nconst { UsersPermissionsRouteValidator } = require('./validation');\n\nmodule.exports = (strapi) => {\n const validator = new UsersPermissionsRouteValidator(strapi);\n\n return [\n {\n method: 'GET',\n path: '/connect/(.*)',\n handler: 'auth.connect',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/local',\n handler: 'auth.callback',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.loginBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/local/register',\n handler: 'auth.register',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.registerBodySchema },\n },\n response: validator.authRegisterResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/:provider/callback',\n handler: 'auth.callback',\n config: {\n prefix: '',\n },\n request: {\n params: {\n provider: validator.providerParam,\n },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/forgot-password',\n handler: 'auth.forgotPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.forgotPasswordBodySchema },\n },\n response: validator.forgotPasswordResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/reset-password',\n handler: 'auth.resetPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.resetPasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/email-confirmation',\n handler: 'auth.emailConfirmation',\n config: {\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/send-email-confirmation',\n handler: 'auth.sendEmailConfirmation',\n config: {\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.sendEmailConfirmationBodySchema },\n },\n response: validator.sendEmailConfirmationResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/change-password',\n handler: 'auth.changePassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.changePasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/refresh',\n handler: 'auth.refresh',\n config: { prefix: '' },\n },\n {\n method: 'POST',\n path: '/auth/logout',\n handler: 'auth.logout',\n config: { prefix: '' },\n },\n ];\n};\n"],"names":["UsersPermissionsRouteValidator","require$$0","auth","strapi","validator","method","path","handler","config","middlewares","prefix","request","body","loginBodySchema","response","authResponseSchema","registerBodySchema","authRegisterResponseSchema","params","provider","providerParam","forgotPasswordBodySchema","forgotPasswordResponseSchema","resetPasswordBodySchema","sendEmailConfirmationBodySchema","sendEmailConfirmationResponseSchema","changePasswordBodySchema"],"mappings":"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"}
1
+ {"version":3,"file":"auth.mjs","sources":["../../../../server/routes/content-api/auth.js"],"sourcesContent":["'use strict';\n\nconst { UsersPermissionsRouteValidator } = require('./validation');\n\nmodule.exports = (strapi) => {\n const validator = new UsersPermissionsRouteValidator(strapi);\n\n return [\n {\n method: 'GET',\n path: '/connect/(.*)',\n handler: 'auth.connect',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/local',\n handler: 'auth.callback',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.loginBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/local/register',\n handler: 'auth.register',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.registerBodySchema },\n },\n response: validator.authRegisterResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/:provider/callback',\n handler: 'auth.callback',\n config: {\n prefix: '',\n },\n request: {\n params: {\n provider: validator.providerParam,\n },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/forgot-password',\n handler: 'auth.forgotPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.forgotPasswordBodySchema },\n },\n response: validator.forgotPasswordResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/reset-password',\n handler: 'auth.resetPassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.resetPasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'GET',\n path: '/auth/email-confirmation',\n handler: 'auth.emailConfirmation',\n config: {\n prefix: '',\n },\n },\n {\n method: 'POST',\n path: '/auth/send-email-confirmation',\n handler: 'auth.sendEmailConfirmation',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.sendEmailConfirmationBodySchema },\n },\n response: validator.sendEmailConfirmationResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/change-password',\n handler: 'auth.changePassword',\n config: {\n middlewares: ['plugin::users-permissions.rateLimit'],\n prefix: '',\n },\n request: {\n body: { 'application/json': validator.changePasswordBodySchema },\n },\n response: validator.authResponseSchema,\n },\n {\n method: 'POST',\n path: '/auth/refresh',\n handler: 'auth.refresh',\n config: { prefix: '' },\n },\n {\n method: 'POST',\n path: '/auth/logout',\n handler: 'auth.logout',\n config: { prefix: '' },\n },\n ];\n};\n"],"names":["UsersPermissionsRouteValidator","require$$0","auth","strapi","validator","method","path","handler","config","middlewares","prefix","request","body","loginBodySchema","response","authResponseSchema","registerBodySchema","authRegisterResponseSchema","params","provider","providerParam","forgotPasswordBodySchema","forgotPasswordResponseSchema","resetPasswordBodySchema","sendEmailConfirmationBodySchema","sendEmailConfirmationResponseSchema","changePasswordBodySchema"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/plugin-users-permissions",
3
- "version": "5.44.0",
3
+ "version": "5.45.1",
4
4
  "description": "Protect your API with a full-authentication process based on JWT",
5
5
  "homepage": "https://strapi.io",
6
6
  "bugs": {
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@strapi/design-system": "2.2.0",
57
57
  "@strapi/icons": "2.2.0",
58
- "@strapi/utils": "5.44.0",
58
+ "@strapi/utils": "5.45.1",
59
59
  "bcryptjs": "2.4.3",
60
60
  "formik": "2.4.5",
61
61
  "grant": "5.4.24",
@@ -75,7 +75,7 @@
75
75
  "zod": "3.25.67"
76
76
  },
77
77
  "devDependencies": {
78
- "@strapi/strapi": "5.44.0",
78
+ "@strapi/strapi": "5.45.1",
79
79
  "@testing-library/dom": "10.4.1",
80
80
  "@testing-library/react": "16.3.0",
81
81
  "@testing-library/user-event": "14.6.1",
@@ -6,6 +6,91 @@ const { isString, has, toLower } = require('lodash/fp');
6
6
 
7
7
  const { RateLimitError } = utils.errors;
8
8
 
9
+ /**
10
+ * Routes where the rate-limit key MUST NOT include a user identifier
11
+ * derived from `ctx.request.body.email`.
12
+ *
13
+ * On these routes the request body either has no `email` field
14
+ * (e.g. /auth/local uses `identifier`, /auth/reset-password uses
15
+ * `code`, /auth/change-password uses `currentPassword`) or the
16
+ * field is not part of the route contract. Including the
17
+ * attacker-controlled `body.email` in the rate-limit key on these
18
+ * routes lets a caller obtain a fresh key on every request by
19
+ * varying that field, effectively bypassing per-IP throttling.
20
+ *
21
+ * Comparison uses endsWith so the check is stable under any router
22
+ * mount prefix (e.g. `/api/auth/local`).
23
+ *
24
+ * @see https://github.com/strapi/strapi/security/advisories/GHSA-7mqx-wwh4-f9fw
25
+ *
26
+ * When adding a new `rateLimit`-protected auth route whose body does not
27
+ * use `email` as the real identifier, add its path suffix here (or an
28
+ * equivalent `routeUsesEmailIdentifier` rule) so the key cannot be split
29
+ * with arbitrary `body.email` values.
30
+ */
31
+ const ROUTES_WITHOUT_IDENTIFIER = ['/auth/local', '/auth/reset-password', '/auth/change-password'];
32
+
33
+ const isOAuthCallbackPath = (requestPath) => requestPath.includes('/connect/');
34
+
35
+ const routeUsesEmailIdentifier = (requestPath) => {
36
+ if (isOAuthCallbackPath(requestPath)) {
37
+ return false;
38
+ }
39
+
40
+ return !ROUTES_WITHOUT_IDENTIFIER.some((route) => requestPath.endsWith(route));
41
+ };
42
+
43
+ /**
44
+ * Paths suitable for route matching and prefix keys: POSIX-normalized,
45
+ * lower-cased, trailing slashes removed so `/api/auth/local` and
46
+ * `/api/auth/local/` share one bucket.
47
+ */
48
+ const normalizeRequestPathForRateLimit = (requestPath) => {
49
+ const normalized = path.normalize(requestPath);
50
+ const lower = toLower(normalized);
51
+ return lower.replace(/\/+$/, '') || '/';
52
+ };
53
+
54
+ const getEmailIdentifierForKey = (body) => {
55
+ if (!body || !isString(body.email) || body.email === '') {
56
+ return 'unknownIdentifier';
57
+ }
58
+
59
+ return toLower(body.email);
60
+ };
61
+
62
+ const buildPrefixKey = (ctx) => {
63
+ let requestPath;
64
+ if (!isString(ctx.request.path)) {
65
+ requestPath = 'invalidPath';
66
+ } else {
67
+ requestPath = normalizeRequestPathForRateLimit(ctx.request.path);
68
+ if (requestPath === '.' || requestPath === '..') {
69
+ requestPath = 'invalidPath';
70
+ }
71
+ }
72
+
73
+ if (!routeUsesEmailIdentifier(requestPath)) {
74
+ return `noIdentifier:${requestPath}:${ctx.request.ip}`;
75
+ }
76
+
77
+ const userIdentifier = getEmailIdentifierForKey(ctx.request.body);
78
+ return `${userIdentifier}:${requestPath}:${ctx.request.ip}`;
79
+ };
80
+
81
+ const buildRateLimitLoadConfig = (ctx, rateLimitConfig, routeMiddlewareConfig) => {
82
+ return {
83
+ interval: { min: 5 },
84
+ max: 5,
85
+ ...rateLimitConfig,
86
+ ...routeMiddlewareConfig,
87
+ handler() {
88
+ throw new RateLimitError();
89
+ },
90
+ prefixKey: buildPrefixKey(ctx),
91
+ };
92
+ };
93
+
9
94
  module.exports =
10
95
  (config, { strapi }) =>
11
96
  async (ctx, next) => {
@@ -24,24 +109,15 @@ module.exports =
24
109
  if (rateLimitConfig.enabled === true) {
25
110
  const rateLimit = require('koa2-ratelimit').RateLimit;
26
111
 
27
- const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';
28
- const requestPath = isString(ctx.request.path)
29
- ? toLower(path.normalize(ctx.request.path))
30
- : 'invalidPath';
31
-
32
- const loadConfig = {
33
- interval: { min: 5 },
34
- max: 5,
35
- prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,
36
- handler() {
37
- throw new RateLimitError();
38
- },
39
- ...rateLimitConfig,
40
- ...config,
41
- };
112
+ const loadConfig = buildRateLimitLoadConfig(ctx, rateLimitConfig, config);
42
113
 
43
114
  return rateLimit.middleware(loadConfig)(ctx, next);
44
115
  }
45
116
 
46
117
  return next();
47
118
  };
119
+
120
+ module.exports.buildPrefixKey = buildPrefixKey;
121
+ module.exports.ROUTES_WITHOUT_IDENTIFIER = ROUTES_WITHOUT_IDENTIFIER;
122
+ module.exports.normalizeRequestPathForRateLimit = normalizeRequestPathForRateLimit;
123
+ module.exports.buildRateLimitLoadConfig = buildRateLimitLoadConfig;
@@ -94,6 +94,7 @@ module.exports = (strapi) => {
94
94
  path: '/auth/send-email-confirmation',
95
95
  handler: 'auth.sendEmailConfirmation',
96
96
  config: {
97
+ middlewares: ['plugin::users-permissions.rateLimit'],
97
98
  prefix: '',
98
99
  },
99
100
  request: {