Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
33 changes: 33 additions & 0 deletions test/ip-security.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import test from "node:test";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
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(${JSON.stringify(limiterRequire.resolve("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"));
});
Loading