feat: add bounded large-deployment discovery

This commit is contained in:
2026-08-20 01:05:46 +00:00
parent 4a10e6051e
commit 6a8a4cf95a
8 changed files with 449 additions and 24 deletions
+55 -2
View File
@@ -3,7 +3,12 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const { SessionStore } = require('../src/session-store');
const { AltaClient } = require('../src/alta-client');
const {
AltaClient,
DEVICE_MAX_RESPONSE_BYTES,
HIERARCHY_MAX_RESPONSE_BYTES,
MAX_ARRAY_OBJECTS,
} = require('../src/alta-client');
const ORIGIN = 'https://tenant.avasecurity.com';
const SENTINEL = 'HERMES_SENTINEL_SECRET';
@@ -24,17 +29,20 @@ test('uses only stored authority, fixed endpoint paths and hardened transport op
calls.push(options);
if (options.url.endsWith('/devices')) return response([]);
if (options.url.endsWith('/deviceSites')) return response([{ id: 'site-1' }]);
if (options.url.endsWith('/deviceGroups')) return response([{ id: 'group-1' }]);
return response({ user: 'engineer' });
};
const client = new AltaClient({ sessionStore: readyStore(), transport });
assert.deepEqual(await client.getDevices(), []);
assert.deepEqual(await client.getDeviceSites(), [{ id: 'site-1' }]);
assert.deepEqual(await client.getDeviceGroups(), [{ id: 'group-1' }]);
assert.deepEqual(await client.getAuthInfo(), { user: 'engineer' });
assert.deepEqual(calls.map((call) => call.url), [
`${ORIGIN}/api/v1/devices`,
`${ORIGIN}/api/v1/deviceSites`,
`${ORIGIN}/api/v1/deviceGroups`,
`${ORIGIN}/api/v1/auth`,
]);
for (const call of calls) {
@@ -42,11 +50,56 @@ test('uses only stored authority, fixed endpoint paths and hardened transport op
assert.equal(call.proxy, false);
assert.equal(call.maxRedirects, 0);
assert.match(call.headers.Cookie, /^va=HERMES_SENTINEL_SECRET$/);
assert.ok(call.timeout > 0 && call.timeout <= 10_000);
assert.ok(call.timeout > 0 && call.timeout <= 30_000);
assert.ok(call.maxResponseBytes > 0);
}
});
test('uses endpoint-specific 32 MiB/4 MiB limits and a 30 second request deadline', async () => {
const calls = [];
const client = new AltaClient({
sessionStore: readyStore(),
transport: async (options) => { calls.push(options); return response([]); },
});
await client.getDevices();
await client.getDeviceSites();
await client.getDeviceGroups();
assert.deepEqual(calls.map(({ maxResponseBytes }) => maxResponseBytes), [
DEVICE_MAX_RESPONSE_BYTES,
HIERARCHY_MAX_RESPONSE_BYTES,
HIERARCHY_MAX_RESPONSE_BYTES,
]);
assert.ok(calls.every(({ timeout }) => timeout > 0 && timeout <= 30_000));
});
test('accepts device bodies above 2 MiB and rejects bodies above 32 MiB before parsing', async () => {
const acceptedBody = JSON.stringify([{
guid: '550e8400-e29b-41d4-a716-446655440000',
padding: 'x'.repeat((2 * 1024 * 1024) + 1),
}]);
const accepted = new AltaClient({
sessionStore: readyStore(),
transport: async () => response(Buffer.from(acceptedBody)),
});
assert.equal((await accepted.getDevices()).length, 1);
const rejected = new AltaClient({
sessionStore: readyStore(),
transport: async () => response(Buffer.alloc(DEVICE_MAX_RESPONSE_BYTES + 1, 0x20)),
});
await assert.rejects(rejected.getDevices(), { code: 'ALTA_RESPONSE_TOO_LARGE' });
});
test('rejects plain array responses containing more than 10,000 objects', async () => {
const client = new AltaClient({
sessionStore: readyStore(),
transport: async () => response(Array.from({ length: MAX_ARRAY_OBJECTS + 1 }, () => ({}))),
});
await assert.rejects(client.getDevices(), { code: 'ALTA_RESPONSE_TOO_MANY_OBJECTS' });
});
test('rejects renderer-supplied URL/cookie parameters before transport', async () => {
let calls = 0;
const client = new AltaClient({