express-rate-limit 8.0.2 → 8.1.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.
- package/dist/index.cjs +38 -3
- package/dist/index.d.cts +18 -1
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.mjs +38 -3
- package/package.json +12 -12
package/dist/index.cjs
CHANGED
|
@@ -44,7 +44,8 @@ function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
|
44
44
|
|
|
45
45
|
// source/memory-store.ts
|
|
46
46
|
var MemoryStore = class {
|
|
47
|
-
constructor() {
|
|
47
|
+
constructor(validations2) {
|
|
48
|
+
this.validations = validations2;
|
|
48
49
|
/**
|
|
49
50
|
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
50
51
|
* addresses or API keys).
|
|
@@ -70,6 +71,7 @@ var MemoryStore = class {
|
|
|
70
71
|
*/
|
|
71
72
|
init(options) {
|
|
72
73
|
this.windowMs = options.windowMs;
|
|
74
|
+
this.validations?.windowMs(this.windowMs);
|
|
73
75
|
if (this.interval) clearInterval(this.interval);
|
|
74
76
|
this.interval = setInterval(() => {
|
|
75
77
|
this.clearExpired();
|
|
@@ -242,7 +244,7 @@ var setDraft6Headers = (response, info, windowMs) => {
|
|
|
242
244
|
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
243
245
|
response.setHeader("RateLimit-Limit", info.limit.toString());
|
|
244
246
|
response.setHeader("RateLimit-Remaining", info.remaining.toString());
|
|
245
|
-
if (resetSeconds)
|
|
247
|
+
if (typeof resetSeconds === "number")
|
|
246
248
|
response.setHeader("RateLimit-Reset", resetSeconds.toString());
|
|
247
249
|
};
|
|
248
250
|
var setDraft7Headers = (response, info, windowMs) => {
|
|
@@ -372,6 +374,21 @@ var validations = {
|
|
|
372
374
|
);
|
|
373
375
|
}
|
|
374
376
|
},
|
|
377
|
+
/**
|
|
378
|
+
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
|
|
379
|
+
*
|
|
380
|
+
* @param request {Request} - The Express request object.
|
|
381
|
+
*
|
|
382
|
+
* @returns {void}
|
|
383
|
+
*/
|
|
384
|
+
forwardedHeader(request) {
|
|
385
|
+
if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
|
|
386
|
+
throw new ValidationError(
|
|
387
|
+
"ERR_ERL_FORWARDED_HEADER",
|
|
388
|
+
`The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
},
|
|
375
392
|
/**
|
|
376
393
|
* Ensures totalHits value from store is a positive integer.
|
|
377
394
|
*
|
|
@@ -585,6 +602,21 @@ var validations = {
|
|
|
585
602
|
"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
|
|
586
603
|
);
|
|
587
604
|
}
|
|
605
|
+
},
|
|
606
|
+
/**
|
|
607
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
608
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
609
|
+
*
|
|
610
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
611
|
+
*/
|
|
612
|
+
windowMs(windowMs) {
|
|
613
|
+
const SET_TIMEOUT_MAX = 2 ** 31 - 1;
|
|
614
|
+
if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
|
|
615
|
+
throw new ValidationError(
|
|
616
|
+
"ERR_ERL_WINDOW_MS",
|
|
617
|
+
`Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
|
|
618
|
+
);
|
|
619
|
+
}
|
|
588
620
|
}
|
|
589
621
|
};
|
|
590
622
|
var getValidations = (_enabled) => {
|
|
@@ -711,6 +743,7 @@ var parseOptions = (passedOptions) => {
|
|
|
711
743
|
validations2.ip(request.ip);
|
|
712
744
|
validations2.trustProxy(request);
|
|
713
745
|
validations2.xForwardedForHeader(request);
|
|
746
|
+
validations2.forwardedHeader(request);
|
|
714
747
|
const ip = request.ip;
|
|
715
748
|
let subnet = 56;
|
|
716
749
|
if ((0, import_node_net3.isIPv6)(ip)) {
|
|
@@ -736,7 +769,9 @@ var parseOptions = (passedOptions) => {
|
|
|
736
769
|
standardHeaders,
|
|
737
770
|
// Note that this field is declared after the user's options are spread in,
|
|
738
771
|
// so that this field doesn't get overridden with an un-promisified store!
|
|
739
|
-
store: promisifyStore(
|
|
772
|
+
store: promisifyStore(
|
|
773
|
+
notUndefinedOptions.store ?? new MemoryStore(validations2)
|
|
774
|
+
),
|
|
740
775
|
// Print an error to the console if a few known misconfigurations are detected.
|
|
741
776
|
validations: validations2
|
|
742
777
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v8.
|
|
1
|
+
// Generated by dts-bundle-generator v8.1.2
|
|
2
2
|
|
|
3
3
|
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
@@ -60,6 +60,14 @@ declare const validations: {
|
|
|
60
60
|
* @returns {void}
|
|
61
61
|
*/
|
|
62
62
|
xForwardedForHeader(request: Request): void;
|
|
63
|
+
/**
|
|
64
|
+
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
|
|
65
|
+
*
|
|
66
|
+
* @param request {Request} - The Express request object.
|
|
67
|
+
*
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
70
|
+
forwardedHeader(request: Request): void;
|
|
63
71
|
/**
|
|
64
72
|
* Ensures totalHits value from store is a positive integer.
|
|
65
73
|
*
|
|
@@ -141,6 +149,13 @@ declare const validations: {
|
|
|
141
149
|
ipv6Subnet(ipv6Subnet?: any): void;
|
|
142
150
|
ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
|
|
143
151
|
keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
|
|
152
|
+
/**
|
|
153
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
154
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
155
|
+
*
|
|
156
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
157
|
+
*/
|
|
158
|
+
windowMs(windowMs: number): void;
|
|
144
159
|
};
|
|
145
160
|
export type Validations = typeof validations;
|
|
146
161
|
/**
|
|
@@ -496,6 +511,7 @@ export type Client = {
|
|
|
496
511
|
* @public
|
|
497
512
|
*/
|
|
498
513
|
export declare class MemoryStore implements Store {
|
|
514
|
+
private validations?;
|
|
499
515
|
/**
|
|
500
516
|
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
501
517
|
*/
|
|
@@ -521,6 +537,7 @@ export declare class MemoryStore implements Store {
|
|
|
521
537
|
* cannot affect other instances.
|
|
522
538
|
*/
|
|
523
539
|
localKeys: boolean;
|
|
540
|
+
constructor(validations?: Validations | undefined);
|
|
524
541
|
/**
|
|
525
542
|
* Method that initializes the store.
|
|
526
543
|
*
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v8.
|
|
1
|
+
// Generated by dts-bundle-generator v8.1.2
|
|
2
2
|
|
|
3
3
|
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
@@ -60,6 +60,14 @@ declare const validations: {
|
|
|
60
60
|
* @returns {void}
|
|
61
61
|
*/
|
|
62
62
|
xForwardedForHeader(request: Request): void;
|
|
63
|
+
/**
|
|
64
|
+
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
|
|
65
|
+
*
|
|
66
|
+
* @param request {Request} - The Express request object.
|
|
67
|
+
*
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
70
|
+
forwardedHeader(request: Request): void;
|
|
63
71
|
/**
|
|
64
72
|
* Ensures totalHits value from store is a positive integer.
|
|
65
73
|
*
|
|
@@ -141,6 +149,13 @@ declare const validations: {
|
|
|
141
149
|
ipv6Subnet(ipv6Subnet?: any): void;
|
|
142
150
|
ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
|
|
143
151
|
keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
|
|
152
|
+
/**
|
|
153
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
154
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
155
|
+
*
|
|
156
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
157
|
+
*/
|
|
158
|
+
windowMs(windowMs: number): void;
|
|
144
159
|
};
|
|
145
160
|
export type Validations = typeof validations;
|
|
146
161
|
/**
|
|
@@ -496,6 +511,7 @@ export type Client = {
|
|
|
496
511
|
* @public
|
|
497
512
|
*/
|
|
498
513
|
export declare class MemoryStore implements Store {
|
|
514
|
+
private validations?;
|
|
499
515
|
/**
|
|
500
516
|
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
501
517
|
*/
|
|
@@ -521,6 +537,7 @@ export declare class MemoryStore implements Store {
|
|
|
521
537
|
* cannot affect other instances.
|
|
522
538
|
*/
|
|
523
539
|
localKeys: boolean;
|
|
540
|
+
constructor(validations?: Validations | undefined);
|
|
524
541
|
/**
|
|
525
542
|
* Method that initializes the store.
|
|
526
543
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v8.
|
|
1
|
+
// Generated by dts-bundle-generator v8.1.2
|
|
2
2
|
|
|
3
3
|
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
@@ -60,6 +60,14 @@ declare const validations: {
|
|
|
60
60
|
* @returns {void}
|
|
61
61
|
*/
|
|
62
62
|
xForwardedForHeader(request: Request): void;
|
|
63
|
+
/**
|
|
64
|
+
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
|
|
65
|
+
*
|
|
66
|
+
* @param request {Request} - The Express request object.
|
|
67
|
+
*
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
70
|
+
forwardedHeader(request: Request): void;
|
|
63
71
|
/**
|
|
64
72
|
* Ensures totalHits value from store is a positive integer.
|
|
65
73
|
*
|
|
@@ -141,6 +149,13 @@ declare const validations: {
|
|
|
141
149
|
ipv6Subnet(ipv6Subnet?: any): void;
|
|
142
150
|
ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
|
|
143
151
|
keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
|
|
152
|
+
/**
|
|
153
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
154
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
155
|
+
*
|
|
156
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
157
|
+
*/
|
|
158
|
+
windowMs(windowMs: number): void;
|
|
144
159
|
};
|
|
145
160
|
export type Validations = typeof validations;
|
|
146
161
|
/**
|
|
@@ -496,6 +511,7 @@ export type Client = {
|
|
|
496
511
|
* @public
|
|
497
512
|
*/
|
|
498
513
|
export declare class MemoryStore implements Store {
|
|
514
|
+
private validations?;
|
|
499
515
|
/**
|
|
500
516
|
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
501
517
|
*/
|
|
@@ -521,6 +537,7 @@ export declare class MemoryStore implements Store {
|
|
|
521
537
|
* cannot affect other instances.
|
|
522
538
|
*/
|
|
523
539
|
localKeys: boolean;
|
|
540
|
+
constructor(validations?: Validations | undefined);
|
|
524
541
|
/**
|
|
525
542
|
* Method that initializes the store.
|
|
526
543
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -15,7 +15,8 @@ function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
|
15
15
|
|
|
16
16
|
// source/memory-store.ts
|
|
17
17
|
var MemoryStore = class {
|
|
18
|
-
constructor() {
|
|
18
|
+
constructor(validations2) {
|
|
19
|
+
this.validations = validations2;
|
|
19
20
|
/**
|
|
20
21
|
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
21
22
|
* addresses or API keys).
|
|
@@ -41,6 +42,7 @@ var MemoryStore = class {
|
|
|
41
42
|
*/
|
|
42
43
|
init(options) {
|
|
43
44
|
this.windowMs = options.windowMs;
|
|
45
|
+
this.validations?.windowMs(this.windowMs);
|
|
44
46
|
if (this.interval) clearInterval(this.interval);
|
|
45
47
|
this.interval = setInterval(() => {
|
|
46
48
|
this.clearExpired();
|
|
@@ -213,7 +215,7 @@ var setDraft6Headers = (response, info, windowMs) => {
|
|
|
213
215
|
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
214
216
|
response.setHeader("RateLimit-Limit", info.limit.toString());
|
|
215
217
|
response.setHeader("RateLimit-Remaining", info.remaining.toString());
|
|
216
|
-
if (resetSeconds)
|
|
218
|
+
if (typeof resetSeconds === "number")
|
|
217
219
|
response.setHeader("RateLimit-Reset", resetSeconds.toString());
|
|
218
220
|
};
|
|
219
221
|
var setDraft7Headers = (response, info, windowMs) => {
|
|
@@ -343,6 +345,21 @@ var validations = {
|
|
|
343
345
|
);
|
|
344
346
|
}
|
|
345
347
|
},
|
|
348
|
+
/**
|
|
349
|
+
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
|
|
350
|
+
*
|
|
351
|
+
* @param request {Request} - The Express request object.
|
|
352
|
+
*
|
|
353
|
+
* @returns {void}
|
|
354
|
+
*/
|
|
355
|
+
forwardedHeader(request) {
|
|
356
|
+
if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
|
|
357
|
+
throw new ValidationError(
|
|
358
|
+
"ERR_ERL_FORWARDED_HEADER",
|
|
359
|
+
`The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
},
|
|
346
363
|
/**
|
|
347
364
|
* Ensures totalHits value from store is a positive integer.
|
|
348
365
|
*
|
|
@@ -556,6 +573,21 @@ var validations = {
|
|
|
556
573
|
"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
|
|
557
574
|
);
|
|
558
575
|
}
|
|
576
|
+
},
|
|
577
|
+
/**
|
|
578
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
579
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
580
|
+
*
|
|
581
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
582
|
+
*/
|
|
583
|
+
windowMs(windowMs) {
|
|
584
|
+
const SET_TIMEOUT_MAX = 2 ** 31 - 1;
|
|
585
|
+
if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
|
|
586
|
+
throw new ValidationError(
|
|
587
|
+
"ERR_ERL_WINDOW_MS",
|
|
588
|
+
`Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
559
591
|
}
|
|
560
592
|
};
|
|
561
593
|
var getValidations = (_enabled) => {
|
|
@@ -682,6 +714,7 @@ var parseOptions = (passedOptions) => {
|
|
|
682
714
|
validations2.ip(request.ip);
|
|
683
715
|
validations2.trustProxy(request);
|
|
684
716
|
validations2.xForwardedForHeader(request);
|
|
717
|
+
validations2.forwardedHeader(request);
|
|
685
718
|
const ip = request.ip;
|
|
686
719
|
let subnet = 56;
|
|
687
720
|
if (isIPv62(ip)) {
|
|
@@ -707,7 +740,9 @@ var parseOptions = (passedOptions) => {
|
|
|
707
740
|
standardHeaders,
|
|
708
741
|
// Note that this field is declared after the user's options are spread in,
|
|
709
742
|
// so that this field doesn't get overridden with an un-promisified store!
|
|
710
|
-
store: promisifyStore(
|
|
743
|
+
store: promisifyStore(
|
|
744
|
+
notUndefinedOptions.store ?? new MemoryStore(validations2)
|
|
745
|
+
),
|
|
711
746
|
// Print an error to the console if a few known misconfigurations are detected.
|
|
712
747
|
validations: validations2
|
|
713
748
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.1.1",
|
|
4
4
|
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nathan Friedly",
|
|
@@ -61,29 +61,29 @@
|
|
|
61
61
|
"express": ">= 4.11"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@biomejs/biome": "2.
|
|
64
|
+
"@biomejs/biome": "2.2.2",
|
|
65
65
|
"@express-rate-limit/prettier": "1.1.1",
|
|
66
66
|
"@express-rate-limit/tsconfig": "1.0.2",
|
|
67
|
-
"@jest/globals": "30.
|
|
67
|
+
"@jest/globals": "30.1.2",
|
|
68
68
|
"@types/express": "5.0.3",
|
|
69
69
|
"@types/jest": "30.0.0",
|
|
70
|
-
"@types/node": "24.0
|
|
70
|
+
"@types/node": "24.3.0",
|
|
71
71
|
"@types/supertest": "6.0.3",
|
|
72
72
|
"del-cli": "6.0.0",
|
|
73
|
-
"dts-bundle-generator": "8.
|
|
74
|
-
"esbuild": "0.25.
|
|
73
|
+
"dts-bundle-generator": "8.1.2",
|
|
74
|
+
"esbuild": "0.25.9",
|
|
75
75
|
"express": "5.1.0",
|
|
76
76
|
"husky": "9.1.7",
|
|
77
|
-
"jest": "30.
|
|
78
|
-
"lint-staged": "16.1.
|
|
79
|
-
"mintlify": "4.2.
|
|
77
|
+
"jest": "30.1.2",
|
|
78
|
+
"lint-staged": "16.1.6",
|
|
79
|
+
"mintlify": "4.2.94",
|
|
80
80
|
"npm-run-all": "4.1.5",
|
|
81
81
|
"prettier": "3.6.2",
|
|
82
82
|
"ratelimit-header-parser": "0.1.0",
|
|
83
|
-
"supertest": "7.1.
|
|
84
|
-
"ts-jest": "29.4.
|
|
83
|
+
"supertest": "7.1.4",
|
|
84
|
+
"ts-jest": "29.4.1",
|
|
85
85
|
"ts-node": "10.9.2",
|
|
86
|
-
"typescript": "5.
|
|
86
|
+
"typescript": "5.9.2"
|
|
87
87
|
},
|
|
88
88
|
"prettier": "@express-rate-limit/prettier",
|
|
89
89
|
"lint-staged": {
|