fix(server): use ipKeyGenerator for IPv6 subnet normalisation#1374
Conversation
…anpatwari#1360) The custom keyGenerator in createRouteLimiter referenced req.ip without passing it through express-rate-limit's ipKeyGenerator helper. This caused ERR_ERL_KEY_GEN_IPV6 on startup when binding to 0.0.0.0, and meant each full IPv6 address got its own rate-limit counter — trivially bypassing the per-IP limit. Wrap the IP through ipKeyGenerator so IPv6 addresses are collapsed to their /56 subnet before keying the counter. The existing fallback chain (req.ip → socket.remoteAddress → 'unknown') is preserved to keep ERR_ERL_UNDEFINED_IP_ADDRESS from firing on abruptly closed connections. Tests: 3 new assertions (construction-time regression guard, source-grep for import and call site).
|
@azizur100389 is attempting to deploy a commit to the NexusCore Team on Vercel. A member of the Team first needs to authorize it. |
|
Nice find! Thank you! |
|
Claude finished @magyargergo's task in 4m 8s —— View job PR #1374 Review — fix(server): use ipKeyGenerator for IPv6 subnet normalisationReviewed by: Senior Principal SWE · Security Reviewer · IPv6/Proxy Reviewer · Test Quality Reviewer · Elegance Reviewer Review Bar for This PR
Current PR State
Understanding the ChangeWhy raw What Why the socket fallback exists: In some connection states (abruptly closed, reverse proxy strip) Why Findings[minor] Construction-time test comment overstates when the error fires
[minor] No direct behavioural assertion on IPv6 key collapse
Implementation AssessmentThe implementation is correct and minimal.
import rateLimit, { type RateLimitRequestHandler, ipKeyGenerator } from 'express-rate-limit';Clean single import; no duplicate or conflicting import remains.
keyGenerator: (req: Request) => {
const ip = req.ip ?? req.socket?.remoteAddress;
return ip ? ipKeyGenerator(ip) : 'unknown';
},Fallback chain is correctly preserved:
Comments: Accurately explain IPv6 normalisation rationale, the ERR_ERL_UNDEFINED_IP_ADDRESS motivation for the socket fallback, and the trust-proxy dependency. No stale, narrated, or excessive comments. IPv6 / Proxy Assessment
Test AssessmentConstruction-time regression test ( it('does not throw ERR_ERL_KEY_GEN_IPV6 on construction (#1360)', () => {
expect(() => createRouteLimiter()).not.toThrow();
});Provides a canary for import errors (e.g., removing the Source-grep tests ( expect(validationSource).toMatch(/import.*ipKeyGenerator.*from\s+'express-rate-limit'/);
expect(validationSource).toMatch(/ipKeyGenerator\(ip\)/);These will fail if Existing behavioural tests — all 16 pre-existing tests remain correct and unchanged:
Tests are deterministic, loopback-only (no external network), cross-platform (OS tmpdir, no POSIX-isms). Hidden Unicode / Hygiene AssessmentBidi controls (U+202A–U+202E, U+2066–U+2069): ✅ None found. No bidirectional override or isolate characters in either changed file. GitHub's Unicode warning is a false alarm here. Non-ASCII characters: Found in comments only — all are U+2014 em dashes ( Scope: Exactly 2 files changed. No Elegance AssessmentThis is an exemplary minimal fix. The diff is 10 effective lines: 1 import line modified, 1 keyGenerator one-liner expanded to 3 lines with the Final Verdictproduction-ready with minor follow-ups The core fix is correct: |
Closes #1360
Summary
createRouteLimiterused a customkeyGeneratorthat referencedreq.ipdirectly without callingipKeyGeneratorfrom express-rate-limit v8. This triggeredERR_ERL_KEY_GEN_IPV6on startup when binding to0.0.0.0, and let each full IPv6 address get its own counter — trivially bypassing the per-IP rate limit.ipKeyGeneratorand pass the resolved IP through it so IPv6 addresses are collapsed to their/56subnet. The existingreq.ip → socket.remoteAddress → 'unknown'fallback chain is preserved.Changes
gitnexus/src/server/validation.tsipKeyGenerator; wrap IP through it inkeyGeneratorgitnexus/test/unit/rate-limit.test.tsTest plan
npx tsc --noEmit— cleannpx vitest run test/unit/rate-limit.test.ts— 19/19 pass (was 16, +3 new)npx prettier --checkon changed files — clean