From 373fafedf106953d124211686ce5b3c8835a70d1 Mon Sep 17 00:00:00 2001 From: DysektAI <33468668+DysektAI@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:53:14 -0400 Subject: [PATCH 1/2] fix(deps): pin ip-address zone-validation patch --- package-lock.json | 6 +++--- package.json | 3 ++- test/ip-security.test.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 test/ip-security.test.js diff --git a/package-lock.json b/package-lock.json index 0118c0f..f149d7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1002,9 +1002,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ip-address": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", - "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "version": "10.7.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", "license": "MIT", "engines": { "node": ">= 12" diff --git a/package.json b/package.json index d54ba6b..a90bc2f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "overrides": { "undici": "^6.28.0", "ws": "^8.21.0", - "shell-quote": "1.12.0" + "shell-quote": "1.12.0", + "ip-address": "10.7.4" }, "devDependencies": { "concurrently": "^9.2.4" diff --git a/test/ip-security.test.js b/test/ip-security.test.js new file mode 100644 index 0000000..63148b9 --- /dev/null +++ b/test/ip-security.test.js @@ -0,0 +1,29 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import { Address6 } from "ip-address"; +import { ipKeyGenerator } from "express-rate-limit"; + +// Run potentially blocking validation outside the test runner. The vulnerable +// version takes many seconds on this input; the patch rejects it in milliseconds. +test("oversized IPv6 zones with line terminators are rejected without stalling", () => { + const result = spawnSync(process.execPath, ["-e", ` + const assert = require('node:assert/strict'); + const { Address6 } = require('ip-address'); + for (const terminator of ['\\n', '\\r', '\\u2028', '\\u2029']) { + assert.equal(Address6.isValid('%'.repeat(200000) + terminator), false); + } + console.log('rejected all oversized zones'); + `], { cwd: fileURLToPath(new URL("../", import.meta.url)), encoding: "utf8", timeout: 2000 }); + assert.ifError(result.error); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.signal, null); + assert.match(result.stdout, /rejected all oversized zones/); +}); + +test("IPv6 zone parsing and existing limiter address grouping remain compatible", () => { + assert.equal(new Address6("fe80::1%eth0").zone, "%eth0"); + assert.equal(ipKeyGenerator("::ffff:192.0.2.1"), "192.0.2.1"); + assert.equal(ipKeyGenerator("2001:db8:abcd:1200::1"), ipKeyGenerator("2001:db8:abcd:12ff::2")); +}); From ed754933bef91904751f18bdc9513fc6fcd474ed Mon Sep 17 00:00:00 2001 From: DysektAI <33468668+DysektAI@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:49:11 -0400 Subject: [PATCH 2/2] test(deps): resolve IPv6 parsing from the limiter installation --- test/ip-security.test.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/ip-security.test.js b/test/ip-security.test.js index 63148b9..46ab29d 100644 --- a/test/ip-security.test.js +++ b/test/ip-security.test.js @@ -2,15 +2,19 @@ import test from "node:test"; import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; -import { Address6 } from "ip-address"; +import { createRequire } from "node:module"; import { ipKeyGenerator } from "express-rate-limit"; +// Exercise the limiter's actual dependency, including a nested installation. +const limiterRequire = createRequire(createRequire(import.meta.url).resolve("express-rate-limit")); +const { Address6 } = limiterRequire("ip-address"); + // Run potentially blocking validation outside the test runner. The vulnerable // version takes many seconds on this input; the patch rejects it in milliseconds. test("oversized IPv6 zones with line terminators are rejected without stalling", () => { const result = spawnSync(process.execPath, ["-e", ` const assert = require('node:assert/strict'); - const { Address6 } = require('ip-address'); + const { Address6 } = require(${JSON.stringify(limiterRequire.resolve("ip-address"))}); for (const terminator of ['\\n', '\\r', '\\u2028', '\\u2029']) { assert.equal(Address6.isValid('%'.repeat(200000) + terminator), false); }