express-rate-limit 8.0.0 → 8.1.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/index.cjs +45 -23
- package/dist/index.d.cts +20 -3
- package/dist/index.d.mts +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.mjs +44 -12
- package/package.json +16 -17
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// source/index.ts
|
|
@@ -39,20 +29,18 @@ module.exports = __toCommonJS(index_exports);
|
|
|
39
29
|
|
|
40
30
|
// source/ip-key-generator.ts
|
|
41
31
|
var import_node_net = require("node:net");
|
|
42
|
-
var
|
|
32
|
+
var import_ip_address = require("ip-address");
|
|
43
33
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
44
34
|
if (ipv6Subnet && (0, import_node_net.isIPv6)(ip)) {
|
|
45
|
-
return `${
|
|
46
|
-
ip,
|
|
47
|
-
import_ip.default.fromPrefixLen(ipv6Subnet)
|
|
48
|
-
)}/${ipv6Subnet}`;
|
|
35
|
+
return `${new import_ip_address.Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;
|
|
49
36
|
}
|
|
50
37
|
return ip;
|
|
51
38
|
}
|
|
52
39
|
|
|
53
40
|
// source/memory-store.ts
|
|
54
41
|
var MemoryStore = class {
|
|
55
|
-
constructor() {
|
|
42
|
+
constructor(validations2) {
|
|
43
|
+
this.validations = validations2;
|
|
56
44
|
/**
|
|
57
45
|
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
58
46
|
* addresses or API keys).
|
|
@@ -78,6 +66,7 @@ var MemoryStore = class {
|
|
|
78
66
|
*/
|
|
79
67
|
init(options) {
|
|
80
68
|
this.windowMs = options.windowMs;
|
|
69
|
+
this.validations?.windowMs(this.windowMs);
|
|
81
70
|
if (this.interval) clearInterval(this.interval);
|
|
82
71
|
this.interval = setInterval(() => {
|
|
83
72
|
this.clearExpired();
|
|
@@ -250,7 +239,7 @@ var setDraft6Headers = (response, info, windowMs) => {
|
|
|
250
239
|
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
251
240
|
response.setHeader("RateLimit-Limit", info.limit.toString());
|
|
252
241
|
response.setHeader("RateLimit-Remaining", info.remaining.toString());
|
|
253
|
-
if (resetSeconds)
|
|
242
|
+
if (typeof resetSeconds === "number")
|
|
254
243
|
response.setHeader("RateLimit-Reset", resetSeconds.toString());
|
|
255
244
|
};
|
|
256
245
|
var setDraft7Headers = (response, info, windowMs) => {
|
|
@@ -380,6 +369,21 @@ var validations = {
|
|
|
380
369
|
);
|
|
381
370
|
}
|
|
382
371
|
},
|
|
372
|
+
/**
|
|
373
|
+
* 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)
|
|
374
|
+
*
|
|
375
|
+
* @param request {Request} - The Express request object.
|
|
376
|
+
*
|
|
377
|
+
* @returns {void}
|
|
378
|
+
*/
|
|
379
|
+
forwardedHeader(request) {
|
|
380
|
+
if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
|
|
381
|
+
throw new ValidationError(
|
|
382
|
+
"ERR_ERL_FORWARDED_HEADER",
|
|
383
|
+
`The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
},
|
|
383
387
|
/**
|
|
384
388
|
* Ensures totalHits value from store is a positive integer.
|
|
385
389
|
*
|
|
@@ -448,7 +452,7 @@ var validations = {
|
|
|
448
452
|
if (limit === 0) {
|
|
449
453
|
throw new ChangeWarning(
|
|
450
454
|
"WRN_ERL_MAX_ZERO",
|
|
451
|
-
|
|
455
|
+
"Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"
|
|
452
456
|
);
|
|
453
457
|
}
|
|
454
458
|
},
|
|
@@ -480,7 +484,7 @@ var validations = {
|
|
|
480
484
|
if (onLimitReached) {
|
|
481
485
|
throw new ChangeWarning(
|
|
482
486
|
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
|
|
483
|
-
|
|
487
|
+
"The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."
|
|
484
488
|
);
|
|
485
489
|
}
|
|
486
490
|
},
|
|
@@ -559,7 +563,7 @@ var validations = {
|
|
|
559
563
|
}
|
|
560
564
|
throw new ValidationError(
|
|
561
565
|
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
562
|
-
|
|
566
|
+
"express-rate-limit instance should be created at app initialization, not when responding to a request."
|
|
563
567
|
);
|
|
564
568
|
}
|
|
565
569
|
},
|
|
@@ -590,7 +594,22 @@ var validations = {
|
|
|
590
594
|
if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {
|
|
591
595
|
throw new ValidationError(
|
|
592
596
|
"ERR_ERL_KEY_GEN_IPV6",
|
|
593
|
-
|
|
597
|
+
"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
/**
|
|
602
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
603
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
604
|
+
*
|
|
605
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
606
|
+
*/
|
|
607
|
+
windowMs(windowMs) {
|
|
608
|
+
const SET_TIMEOUT_MAX = 2 ** 31 - 1;
|
|
609
|
+
if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
|
|
610
|
+
throw new ValidationError(
|
|
611
|
+
"ERR_ERL_WINDOW_MS",
|
|
612
|
+
`Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
|
|
594
613
|
);
|
|
595
614
|
}
|
|
596
615
|
}
|
|
@@ -719,6 +738,7 @@ var parseOptions = (passedOptions) => {
|
|
|
719
738
|
validations2.ip(request.ip);
|
|
720
739
|
validations2.trustProxy(request);
|
|
721
740
|
validations2.xForwardedForHeader(request);
|
|
741
|
+
validations2.forwardedHeader(request);
|
|
722
742
|
const ip = request.ip;
|
|
723
743
|
let subnet = 56;
|
|
724
744
|
if ((0, import_node_net3.isIPv6)(ip)) {
|
|
@@ -744,7 +764,9 @@ var parseOptions = (passedOptions) => {
|
|
|
744
764
|
standardHeaders,
|
|
745
765
|
// Note that this field is declared after the user's options are spread in,
|
|
746
766
|
// so that this field doesn't get overridden with an un-promisified store!
|
|
747
|
-
store: promisifyStore(
|
|
767
|
+
store: promisifyStore(
|
|
768
|
+
notUndefinedOptions.store ?? new MemoryStore(validations2)
|
|
769
|
+
),
|
|
748
770
|
// Print an error to the console if a few known misconfigurations are detected.
|
|
749
771
|
validations: validations2
|
|
750
772
|
};
|
|
@@ -891,4 +913,4 @@ var rate_limit_default = rateLimit;
|
|
|
891
913
|
ipKeyGenerator,
|
|
892
914
|
rateLimit
|
|
893
915
|
});
|
|
894
|
-
module.exports =
|
|
916
|
+
module.exports = Object.assign(rateLimit, module.exports);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator
|
|
1
|
+
// Generated by dts-bundle-generator v8.1.2
|
|
2
2
|
|
|
3
|
-
import { NextFunction, Request
|
|
3
|
+
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns the IP address itself for IPv4, or a CIDR-notation subnet for IPv6.
|
|
@@ -8,7 +8,7 @@ import { NextFunction, Request as Request$1, RequestHandler, Response as Respons
|
|
|
8
8
|
* If you write a custom keyGenerator that allows a fallback to IP address for
|
|
9
9
|
* unauthenticated users, return ipKeyGenerator(req.ip) rather than just req.ip.
|
|
10
10
|
*
|
|
11
|
-
* For more
|
|
11
|
+
* For more information, {@see Options.ipv6Subnet}.
|
|
12
12
|
*
|
|
13
13
|
* @param ip {string} - The IP address to process, usually request.ip.
|
|
14
14
|
* @param ipv6Subnet {number | false} - The subnet mask for IPv6 addresses.
|
|
@@ -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,6 +1,6 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator
|
|
1
|
+
// Generated by dts-bundle-generator v8.1.2
|
|
2
2
|
|
|
3
|
-
import { NextFunction, Request
|
|
3
|
+
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns the IP address itself for IPv4, or a CIDR-notation subnet for IPv6.
|
|
@@ -8,7 +8,7 @@ import { NextFunction, Request as Request$1, RequestHandler, Response as Respons
|
|
|
8
8
|
* If you write a custom keyGenerator that allows a fallback to IP address for
|
|
9
9
|
* unauthenticated users, return ipKeyGenerator(req.ip) rather than just req.ip.
|
|
10
10
|
*
|
|
11
|
-
* For more
|
|
11
|
+
* For more information, {@see Options.ipv6Subnet}.
|
|
12
12
|
*
|
|
13
13
|
* @param ip {string} - The IP address to process, usually request.ip.
|
|
14
14
|
* @param ipv6Subnet {number | false} - The subnet mask for IPv6 addresses.
|
|
@@ -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,6 +1,6 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator
|
|
1
|
+
// Generated by dts-bundle-generator v8.1.2
|
|
2
2
|
|
|
3
|
-
import { NextFunction, Request
|
|
3
|
+
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns the IP address itself for IPv4, or a CIDR-notation subnet for IPv6.
|
|
@@ -8,7 +8,7 @@ import { NextFunction, Request as Request$1, RequestHandler, Response as Respons
|
|
|
8
8
|
* If you write a custom keyGenerator that allows a fallback to IP address for
|
|
9
9
|
* unauthenticated users, return ipKeyGenerator(req.ip) rather than just req.ip.
|
|
10
10
|
*
|
|
11
|
-
* For more
|
|
11
|
+
* For more information, {@see Options.ipv6Subnet}.
|
|
12
12
|
*
|
|
13
13
|
* @param ip {string} - The IP address to process, usually request.ip.
|
|
14
14
|
* @param ipv6Subnet {number | false} - The subnet mask for IPv6 addresses.
|
|
@@ -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
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
// source/ip-key-generator.ts
|
|
2
2
|
import { isIPv6 } from "node:net";
|
|
3
|
-
import
|
|
3
|
+
import { Address6 } from "ip-address";
|
|
4
4
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
5
5
|
if (ipv6Subnet && isIPv6(ip)) {
|
|
6
|
-
return `${
|
|
7
|
-
ip,
|
|
8
|
-
iptools.fromPrefixLen(ipv6Subnet)
|
|
9
|
-
)}/${ipv6Subnet}`;
|
|
6
|
+
return `${new Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;
|
|
10
7
|
}
|
|
11
8
|
return ip;
|
|
12
9
|
}
|
|
13
10
|
|
|
14
11
|
// source/memory-store.ts
|
|
15
12
|
var MemoryStore = class {
|
|
16
|
-
constructor() {
|
|
13
|
+
constructor(validations2) {
|
|
14
|
+
this.validations = validations2;
|
|
17
15
|
/**
|
|
18
16
|
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
19
17
|
* addresses or API keys).
|
|
@@ -39,6 +37,7 @@ var MemoryStore = class {
|
|
|
39
37
|
*/
|
|
40
38
|
init(options) {
|
|
41
39
|
this.windowMs = options.windowMs;
|
|
40
|
+
this.validations?.windowMs(this.windowMs);
|
|
42
41
|
if (this.interval) clearInterval(this.interval);
|
|
43
42
|
this.interval = setInterval(() => {
|
|
44
43
|
this.clearExpired();
|
|
@@ -211,7 +210,7 @@ var setDraft6Headers = (response, info, windowMs) => {
|
|
|
211
210
|
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
212
211
|
response.setHeader("RateLimit-Limit", info.limit.toString());
|
|
213
212
|
response.setHeader("RateLimit-Remaining", info.remaining.toString());
|
|
214
|
-
if (resetSeconds)
|
|
213
|
+
if (typeof resetSeconds === "number")
|
|
215
214
|
response.setHeader("RateLimit-Reset", resetSeconds.toString());
|
|
216
215
|
};
|
|
217
216
|
var setDraft7Headers = (response, info, windowMs) => {
|
|
@@ -341,6 +340,21 @@ var validations = {
|
|
|
341
340
|
);
|
|
342
341
|
}
|
|
343
342
|
},
|
|
343
|
+
/**
|
|
344
|
+
* 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)
|
|
345
|
+
*
|
|
346
|
+
* @param request {Request} - The Express request object.
|
|
347
|
+
*
|
|
348
|
+
* @returns {void}
|
|
349
|
+
*/
|
|
350
|
+
forwardedHeader(request) {
|
|
351
|
+
if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
|
|
352
|
+
throw new ValidationError(
|
|
353
|
+
"ERR_ERL_FORWARDED_HEADER",
|
|
354
|
+
`The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
344
358
|
/**
|
|
345
359
|
* Ensures totalHits value from store is a positive integer.
|
|
346
360
|
*
|
|
@@ -409,7 +423,7 @@ var validations = {
|
|
|
409
423
|
if (limit === 0) {
|
|
410
424
|
throw new ChangeWarning(
|
|
411
425
|
"WRN_ERL_MAX_ZERO",
|
|
412
|
-
|
|
426
|
+
"Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"
|
|
413
427
|
);
|
|
414
428
|
}
|
|
415
429
|
},
|
|
@@ -441,7 +455,7 @@ var validations = {
|
|
|
441
455
|
if (onLimitReached) {
|
|
442
456
|
throw new ChangeWarning(
|
|
443
457
|
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
|
|
444
|
-
|
|
458
|
+
"The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."
|
|
445
459
|
);
|
|
446
460
|
}
|
|
447
461
|
},
|
|
@@ -520,7 +534,7 @@ var validations = {
|
|
|
520
534
|
}
|
|
521
535
|
throw new ValidationError(
|
|
522
536
|
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
523
|
-
|
|
537
|
+
"express-rate-limit instance should be created at app initialization, not when responding to a request."
|
|
524
538
|
);
|
|
525
539
|
}
|
|
526
540
|
},
|
|
@@ -551,7 +565,22 @@ var validations = {
|
|
|
551
565
|
if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {
|
|
552
566
|
throw new ValidationError(
|
|
553
567
|
"ERR_ERL_KEY_GEN_IPV6",
|
|
554
|
-
|
|
568
|
+
"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
/**
|
|
573
|
+
* Checks to see if the window duration is greater than 2^32 - 1. This is only
|
|
574
|
+
* called by the default MemoryStore, since it uses Node's setInterval method.
|
|
575
|
+
*
|
|
576
|
+
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
|
|
577
|
+
*/
|
|
578
|
+
windowMs(windowMs) {
|
|
579
|
+
const SET_TIMEOUT_MAX = 2 ** 31 - 1;
|
|
580
|
+
if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
|
|
581
|
+
throw new ValidationError(
|
|
582
|
+
"ERR_ERL_WINDOW_MS",
|
|
583
|
+
`Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
|
|
555
584
|
);
|
|
556
585
|
}
|
|
557
586
|
}
|
|
@@ -680,6 +709,7 @@ var parseOptions = (passedOptions) => {
|
|
|
680
709
|
validations2.ip(request.ip);
|
|
681
710
|
validations2.trustProxy(request);
|
|
682
711
|
validations2.xForwardedForHeader(request);
|
|
712
|
+
validations2.forwardedHeader(request);
|
|
683
713
|
const ip = request.ip;
|
|
684
714
|
let subnet = 56;
|
|
685
715
|
if (isIPv62(ip)) {
|
|
@@ -705,7 +735,9 @@ var parseOptions = (passedOptions) => {
|
|
|
705
735
|
standardHeaders,
|
|
706
736
|
// Note that this field is declared after the user's options are spread in,
|
|
707
737
|
// so that this field doesn't get overridden with an un-promisified store!
|
|
708
|
-
store: promisifyStore(
|
|
738
|
+
store: promisifyStore(
|
|
739
|
+
notUndefinedOptions.store ?? new MemoryStore(validations2)
|
|
740
|
+
),
|
|
709
741
|
// Print an error to the console if a few known misconfigurations are detected.
|
|
710
742
|
validations: validations2
|
|
711
743
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-rate-limit",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.1.0",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
|
|
59
|
-
"build:cjs": "esbuild --packages=external --platform=node --bundle --target=es2022 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports =
|
|
59
|
+
"build:cjs": "esbuild --packages=external --platform=node --bundle --target=es2022 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = Object.assign(rateLimit, module.exports);\" source/index.ts",
|
|
60
60
|
"build:esm": "esbuild --packages=external --platform=node --bundle --target=es2022 --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
61
61
|
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts",
|
|
62
62
|
"compile": "run-s clean build:*",
|
|
@@ -73,41 +73,40 @@
|
|
|
73
73
|
"pre-commit": "lint-staged",
|
|
74
74
|
"prepare": "run-s compile && husky"
|
|
75
75
|
},
|
|
76
|
-
"dependencies": {
|
|
77
|
-
"ip": "2.0.1"
|
|
78
|
-
},
|
|
79
76
|
"peerDependencies": {
|
|
80
77
|
"express": ">= 4.11"
|
|
81
78
|
},
|
|
82
79
|
"devDependencies": {
|
|
83
|
-
"@biomejs/biome": "2.
|
|
80
|
+
"@biomejs/biome": "2.2.2",
|
|
84
81
|
"@express-rate-limit/prettier": "1.1.1",
|
|
85
82
|
"@express-rate-limit/tsconfig": "1.0.2",
|
|
86
|
-
"@jest/globals": "30.
|
|
83
|
+
"@jest/globals": "30.1.2",
|
|
87
84
|
"@types/express": "5.0.3",
|
|
88
|
-
"@types/ip": "1.1.3",
|
|
89
85
|
"@types/jest": "30.0.0",
|
|
90
|
-
"@types/node": "24.0
|
|
86
|
+
"@types/node": "24.3.0",
|
|
91
87
|
"@types/supertest": "6.0.3",
|
|
92
88
|
"del-cli": "6.0.0",
|
|
93
|
-
"dts-bundle-generator": "
|
|
94
|
-
"esbuild": "0.25.
|
|
89
|
+
"dts-bundle-generator": "8.1.2",
|
|
90
|
+
"esbuild": "0.25.9",
|
|
95
91
|
"express": "5.1.0",
|
|
96
92
|
"husky": "9.1.7",
|
|
97
|
-
"jest": "30.
|
|
98
|
-
"lint-staged": "16.1.
|
|
99
|
-
"mintlify": "4.2.
|
|
93
|
+
"jest": "30.1.2",
|
|
94
|
+
"lint-staged": "16.1.6",
|
|
95
|
+
"mintlify": "4.2.94",
|
|
100
96
|
"npm-run-all": "4.1.5",
|
|
101
97
|
"prettier": "3.6.2",
|
|
102
98
|
"ratelimit-header-parser": "0.1.0",
|
|
103
|
-
"supertest": "7.1.
|
|
104
|
-
"ts-jest": "29.4.
|
|
99
|
+
"supertest": "7.1.4",
|
|
100
|
+
"ts-jest": "29.4.1",
|
|
105
101
|
"ts-node": "10.9.2",
|
|
106
|
-
"typescript": "5.
|
|
102
|
+
"typescript": "5.9.2"
|
|
107
103
|
},
|
|
108
104
|
"prettier": "@express-rate-limit/prettier",
|
|
109
105
|
"lint-staged": {
|
|
110
106
|
"*.{js,ts,json}": "biome check --write",
|
|
111
107
|
"*.{md,yaml}": "prettier --write"
|
|
108
|
+
},
|
|
109
|
+
"dependencies": {
|
|
110
|
+
"ip-address": "10.0.1"
|
|
112
111
|
}
|
|
113
112
|
}
|