🪤 fix: Avoid express-rate-limit v8 ERR_ERL_KEY_GEN_IPV6 False Positive (#12333)

* fix: avoid express-rate-limit v8 ERR_ERL_KEY_GEN_IPV6 false positive

express-rate-limit v8 calls keyGenerator.toString() and throws
ERR_ERL_KEY_GEN_IPV6 if the source contains the literal substring
"req.ip" without "ipKeyGenerator". When packages/api compiles
req?.ip to older JS targets, the output contains "req.ip",
triggering the heuristic.

Bracket notation (req?.['ip']) produces identical runtime behavior
but never emits the literal "req.ip" substring regardless of
compilation target.

Closes #12321

* fix: add toString regression test and clean up redundant annotation

Add a test that verifies removePorts.toString() does not contain
"req.ip", guarding against reintroduction of the ERR_ERL_KEY_GEN_IPV6
false positive. Fix a misleading test description and remove a
redundant type annotation on a trivially-inferred local.
This commit is contained in:
Danny Avila 2026-03-20 12:32:55 -04:00 committed by GitHub
parent e442984364
commit 594d9470d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 13 additions and 3 deletions

View file

@ -75,7 +75,7 @@ describe('removePorts', () => {
expect(removePorts(req(undefined))).toBeUndefined();
});
test('returns undefined when ip is empty string', () => {
test('returns empty string when ip is empty string', () => {
expect(removePorts({ ip: '' } as Request)).toBe('');
});
@ -90,6 +90,12 @@ describe('removePorts', () => {
});
});
describe('express-rate-limit v8 heuristic guard', () => {
test('function source does not contain "req.ip" (guards against ERR_ERL_KEY_GEN_IPV6)', () => {
expect(removePorts.toString()).not.toContain('req.ip');
});
});
describe('unrecognized formats fall through unchanged', () => {
test('returns garbage input unchanged', () => {
expect(removePorts(req('not-an-ip'))).toBe('not-an-ip');

View file

@ -1,8 +1,12 @@
import type { Request } from 'express';
/** Strips port suffix from req.ip for use as a rate-limiter key (IPv4 and IPv6-safe) */
/**
* Strips port suffix from req.ip for use as a rate-limiter key (IPv4 and IPv6-safe).
* Bracket notation for the ip property avoids express-rate-limit v8's toString()
* heuristic that scans for the literal substring "req.ip" (ERR_ERL_KEY_GEN_IPV6).
*/
export function removePorts(req: Request): string | undefined {
const ip = req?.ip;
const ip = req?.['ip'];
if (!ip) {
return ip;
}