@strapi/plugin-users-permissions 5.44.0 → 5.45.0
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/dist/server/_virtual/rateLimit.js +6 -0
- package/dist/server/_virtual/rateLimit.js.map +1 -0
- package/dist/server/_virtual/rateLimit.mjs +4 -0
- package/dist/server/_virtual/rateLimit.mjs.map +1 -0
- package/dist/server/middlewares/rateLimit.js +87 -18
- package/dist/server/middlewares/rateLimit.js.map +1 -1
- package/dist/server/middlewares/rateLimit.mjs +87 -18
- package/dist/server/middlewares/rateLimit.mjs.map +1 -1
- package/dist/server/routes/content-api/auth.js +3 -0
- package/dist/server/routes/content-api/auth.js.map +1 -1
- package/dist/server/routes/content-api/auth.mjs +3 -0
- package/dist/server/routes/content-api/auth.mjs.map +1 -1
- package/package.json +3 -3
- package/server/middlewares/rateLimit.js +91 -15
- package/server/routes/content-api/auth.js +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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\
|
|
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":"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"}
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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\
|
|
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":"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"}
|
|
@@ -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;
|
|
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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"}
|
|
@@ -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.
|
|
3
|
+
"version": "5.45.0",
|
|
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.
|
|
58
|
+
"@strapi/utils": "5.45.0",
|
|
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.
|
|
78
|
+
"@strapi/strapi": "5.45.0",
|
|
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
|
|
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;
|