diff --git a/common/ip-math.js b/common/ip-math.js index 80105518..aedf2d0f 100644 --- a/common/ip-math.js +++ b/common/ip-math.js @@ -281,7 +281,10 @@ export const compareIps = (a, b) => { // Children of `cidrStr` at `newPrefix`. `total` is exact; `subnets` stops at // `limit` so a /8 → /32 request never materialises 16M strings. -export const splitCidr = (cidrStr, newPrefix, { limit = 1024 } = {}) => { +export const splitCidr = (cidrStr, newPrefix, options = {}) => { + if (!options || typeof options !== 'object' || Array.isArray(options)) return null; + const { limit = 1024 } = options; + if (!Number.isSafeInteger(limit) || limit < 0) return null; const cidr = parseCidr(cidrStr); if (!cidr || !isPrefix(newPrefix, cidr.family) || newPrefix < cidr.prefix) return null; const { family, network } = cidr; diff --git a/tests/ip-math.test.js b/tests/ip-math.test.js index acff368b..0e4bc4b5 100644 --- a/tests/ip-math.test.js +++ b/tests/ip-math.test.js @@ -293,6 +293,29 @@ describe('containment & ordering', () => { }); describe('splitCidr', () => { + it('rejects invalid limits and options without coercing them', () => { + const limits = [Infinity, -Infinity, NaN, 1.5, -1, Number.MAX_SAFE_INTEGER + 1, + Symbol('limit'), '4', 4n, null, true, {}, []]; + for (const limit of limits) { + assert.equal(splitCidr('10.0.0.0/24', 26, { limit }), null); + assert.equal(splitCidr('2001:db8::/32', 48, { limit }), null); + } + for (const options of [null, false, 5, '4', 4n, Symbol('options'), []]) { + assert.equal(splitCidr('10.0.0.0/24', 26, options), null); + } + }); + it('accepts zero, default and large safe limits without changing the exact total', () => { + const zero = splitCidr('10.0.0.0/24', 26, { limit: 0 }); + assert.deepEqual(zero.subnets, []); + assert.equal(zero.total, 4n); + assert.equal(zero.truncated, true); + for (const limit of [undefined, 4, Number.MAX_SAFE_INTEGER]) { + const result = splitCidr('10.0.0.0/24', 26, { limit }); + assert.equal(result.subnets.length, 4); + assert.equal(result.total, 4n); + assert.equal(result.truncated, false); + } + }); it('splits a /24 into /26s', () => { const r = splitCidr('10.0.0.0/24', 26); assert.deepEqual(r.subnets, ['10.0.0.0/26', '10.0.0.64/26', '10.0.0.128/26', '10.0.0.192/26']);