Launch Week Day 3: Introducing Organization Notifications in Socket.Learn More
Socket
Book a DemoSign in
Socket

express-rate-limit

Package Overview
Dependencies
Maintainers
1
Versions
122
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-rate-limit - npm Package Compare versions

Comparing version
6.8.1
to
6.9.0
+12
-0
changelog.md

@@ -9,2 +9,14 @@ # Changelog

## [6.9.0](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.9.0)
### Added
- New validaion check for double-counted requests
- Added help link to each ValidationError, directing users to the appropriate
wiki page for more info
### Changed
- Miscaleanous documenation improvements
## [6.8.1](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.8.0) & [6.7.2](https://github.com/express-rate-limit/express-rate-limit/releases/tag/v6.8.0)

@@ -11,0 +23,0 @@

+54
-5

@@ -45,13 +45,13 @@ "use strict";

constructor(code, message) {
super(
`express-rate-limit: ${code} - ${message} See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#${code.toLowerCase()} for more information on this error.`
);
const url = `https://express-rate-limit.github.io/${code}/`;
super(`${message} See ${url} for more information on this error.`);
__publicField(this, "name");
__publicField(this, "code");
__publicField(this, "help");
this.name = this.constructor.name;
this.code = code;
this.message = message;
this.help = url;
}
};
var Validations = class {
var _Validations = class _Validations {
constructor(enabled) {

@@ -133,2 +133,33 @@ // eslint-disable-next-line @typescript-eslint/parameter-properties

}
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request, store, key) {
this.wrap(() => {
let storeKeys = _Validations.singleCountKeys.get(request);
if (!storeKeys) {
storeKeys = /* @__PURE__ */ new Map();
_Validations.singleCountKeys.set(request, storeKeys);
}
const storeKey = store.localKeys ? store : store.constructor.name;
let keys = storeKeys.get(storeKey);
if (!keys) {
keys = [];
storeKeys.set(storeKey, keys);
}
if (keys.includes(key)) {
throw new ValidationError(
"ERR_ERL_DOUBLE_COUNT",
`The hit count for ${key} was incremented more than once for a single request.`
);
}
keys.push(key);
});
}
wrap(validation) {

@@ -145,2 +176,14 @@ if (!this.enabled) {

};
/**
* Maps the key used in a store for a certain request, and ensures that the
* same key isn't used more than once per request.
*
* The store can be any one of the following:
* - An instance, for stores like the MemoryStore where two instances do not
* share state.
* - A string (class name), for stores where multiple instances
* typically share state, such as the Redis store.
*/
__publicField(_Validations, "singleCountKeys", /* @__PURE__ */ new WeakMap());
var Validations = _Validations;

@@ -171,2 +214,7 @@ // source/memory-store.ts

__publicField(this, "interval");
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
__publicField(this, "localKeys", true);
}

@@ -373,2 +421,3 @@ /**

const { totalHits, resetTime } = await config.store.increment(key);
config.validations.singleCount(request, config.store, key);
const retrieveQuota = typeof config.max === "function" ? config.max(request, response) : config.max;

@@ -375,0 +424,0 @@ const maxHits = await retrieveQuota;

@@ -133,2 +133,10 @@ // Generated by dts-bundle-generator v7.0.0

shutdown?: () => Promise<void> | void;
/**
* Flag to indicate that keys incremented in one instance of this store can
* not affect other instances. Typically false if a database is used, true for
* MemoryStore.
*
* Used to help detect double-counting misconfigurations.
*/
localKeys?: boolean;
};

@@ -314,2 +322,7 @@ /**

/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
localKeys: boolean;
/**
* Method that initializes the store.

@@ -316,0 +329,0 @@ *

@@ -133,2 +133,10 @@ // Generated by dts-bundle-generator v7.0.0

shutdown?: () => Promise<void> | void;
/**
* Flag to indicate that keys incremented in one instance of this store can
* not affect other instances. Typically false if a database is used, true for
* MemoryStore.
*
* Used to help detect double-counting misconfigurations.
*/
localKeys?: boolean;
};

@@ -314,2 +322,7 @@ /**

/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
localKeys: boolean;
/**
* Method that initializes the store.

@@ -316,0 +329,0 @@ *

@@ -133,2 +133,10 @@ // Generated by dts-bundle-generator v7.0.0

shutdown?: () => Promise<void> | void;
/**
* Flag to indicate that keys incremented in one instance of this store can
* not affect other instances. Typically false if a database is used, true for
* MemoryStore.
*
* Used to help detect double-counting misconfigurations.
*/
localKeys?: boolean;
};

@@ -314,2 +322,7 @@ /**

/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
localKeys: boolean;
/**
* Method that initializes the store.

@@ -316,0 +329,0 @@ *

@@ -19,13 +19,13 @@ var __defProp = Object.defineProperty;

constructor(code, message) {
super(
`express-rate-limit: ${code} - ${message} See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#${code.toLowerCase()} for more information on this error.`
);
const url = `https://express-rate-limit.github.io/${code}/`;
super(`${message} See ${url} for more information on this error.`);
__publicField(this, "name");
__publicField(this, "code");
__publicField(this, "help");
this.name = this.constructor.name;
this.code = code;
this.message = message;
this.help = url;
}
};
var Validations = class {
var _Validations = class _Validations {
constructor(enabled) {

@@ -107,2 +107,33 @@ // eslint-disable-next-line @typescript-eslint/parameter-properties

}
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request, store, key) {
this.wrap(() => {
let storeKeys = _Validations.singleCountKeys.get(request);
if (!storeKeys) {
storeKeys = /* @__PURE__ */ new Map();
_Validations.singleCountKeys.set(request, storeKeys);
}
const storeKey = store.localKeys ? store : store.constructor.name;
let keys = storeKeys.get(storeKey);
if (!keys) {
keys = [];
storeKeys.set(storeKey, keys);
}
if (keys.includes(key)) {
throw new ValidationError(
"ERR_ERL_DOUBLE_COUNT",
`The hit count for ${key} was incremented more than once for a single request.`
);
}
keys.push(key);
});
}
wrap(validation) {

@@ -119,2 +150,14 @@ if (!this.enabled) {

};
/**
* Maps the key used in a store for a certain request, and ensures that the
* same key isn't used more than once per request.
*
* The store can be any one of the following:
* - An instance, for stores like the MemoryStore where two instances do not
* share state.
* - A string (class name), for stores where multiple instances
* typically share state, such as the Redis store.
*/
__publicField(_Validations, "singleCountKeys", /* @__PURE__ */ new WeakMap());
var Validations = _Validations;

@@ -145,2 +188,7 @@ // source/memory-store.ts

__publicField(this, "interval");
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
__publicField(this, "localKeys", true);
}

@@ -347,2 +395,3 @@ /**

const { totalHits, resetTime } = await config.store.increment(key);
config.validations.singleCount(request, config.store, key);
const retrieveQuota = typeof config.max === "function" ? config.max(request, response) : config.max;

@@ -349,0 +398,0 @@ const maxHits = await retrieveQuota;

+5
-5
{
"name": "express-rate-limit",
"version": "6.8.1",
"version": "6.9.0",
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",

@@ -66,8 +66,8 @@ "author": {

"lint": "run-s lint:*",
"autofix:code": "npm run lint:code -- --fix",
"autofix:rest": "npm run lint:rest -- --write .",
"autofix": "run-s autofix:*",
"format:code": "npm run lint:code -- --fix",
"format:rest": "npm run lint:rest -- --write .",
"format": "run-s format:*",
"test:lib": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-vm-modules jest",
"test:ext": "cd test/external/ && bash run-all-tests",
"test": "run-s lint test:*",
"test": "run-s lint test:lib",
"pre-commit": "lint-staged",

@@ -74,0 +74,0 @@ "prepare": "run-s compile && husky install config/husky"

+44
-11

@@ -19,4 +19,5 @@ # <div align="center"> Express Rate Limit </div>

Basic rate-limiting middleware for Express. Use to limit repeated requests to
public APIs and/or endpoints such as password reset. Plays nice with
Basic rate-limiting middleware for [Express](http://expressjs.com/). Use to
limit repeated requests to public APIs and/or endpoints such as password reset.
Plays nice with
[express-slow-down](https://www.npmjs.com/package/express-slow-down).

@@ -26,8 +27,32 @@

## Use Cases
Depending on your use case, you may need to switch to a different
[store](#store).
#### Abuse Prevention
The default `MemoryStore` is probably fine.
#### API Rate Limit Enforcement
You likely want to switch to a different [store](#store). As a performance
optimization, the default `MemoryStore` uses a global time window, so if your
limit is 10 requests per minute, a single user might be able to get an initial
burst of up to 20 requests in a row if they happen to get the first 10 in at the
end of one minute and the next 10 in at the start of the next minute. (After the
initial burst, they will be limited to the expected 10 requests per minute.) All
other stores use per-user time windows, so a user will get exactly 10 requests
regardless.
Additionally, if you have multiple servers or processes (for example, with the
[node:cluster](https://nodejs.org/api/cluster.html) module), you'll likely want
to use an external data store to syhcnronize hits
([redis](https://npmjs.com/package/rate-limit-redis),
[memcached](https://npmjs.org/package/rate-limit-memcached), [etc.](#store))
This will guarentee the expected result even if some requests get handled by
different servers/processes.
### Alternate Rate Limiters
> This module does not share state with other processes/servers by default. If
> you need a more robust solution, I recommend using an external store. See the
> [`stores` section](#store) below for a list of external stores.
This module was designed to only handle the basics and didn't even support

@@ -99,2 +124,3 @@ external stores initially. These other options all are excellent pieces of

legacyHeaders: false, // Disable the `X-RateLimit-*` headers
// store: ... , // Use an external store for more precise rate limiting
})

@@ -118,2 +144,3 @@

legacyHeaders: false, // Disable the `X-RateLimit-*` headers
// store: ... , // Use an external store for more precise rate limiting
})

@@ -135,2 +162,3 @@

legacyHeaders: false, // Disable the `X-RateLimit-*` headers
// store: ... , // Use an external store for more precise rate limiting
})

@@ -157,13 +185,18 @@

```ts
import rateLimit, { MemoryStore } from 'express-rate-limit'
import rateLimit from 'express-rate-limit'
import RedisStore from 'rate-limit-redis'
import RedisClient from 'ioredis'
const apiLimiter = rateLimit({
const redisClient = new RedisClient()
const rateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
store: new MemoryStore(),
store: new RedisStore({
/* ... */
}), // Use the external store
})
// Apply the rate limiting middleware to API calls only
app.use('/api', apiLimiter)
// Apply the rate limiting middleware to all requests
app.use(rateLimiter)
```

@@ -170,0 +203,0 @@