import test from 'node:test'; import assert from 'node:assert/strict'; import { upstream58e2204Operations } from '../../operation-registry/src/upstream-58e2204.ts'; import { collectOne, selectReadonlyOperations, validateReadonlyOperation, validateInstanceOrigin, } from '../scripts/collector.ts'; import { loadInstances } from '../scripts/collect-readonly-fixtures.ts'; import { redact, redactOperationPayload, parseResponseSensitivePath } from '../src/redactor.ts'; const health = upstream58e2204Operations.find((x: any) => x.operationId === 'getHealth')!; const fakeResponse = async () => new Response('{"ok":true}', { headers: { 'content-type': 'application/json' } }); test('operation validator rejects path and registry identity bypasses', async () => { const attacks = [ 'https://example.invalid/api/health', '//example.invalid/api/health', '/api/health?scan=1', '/api/health#x', '/api/%73can', '/api/%252e%252e/health', '/api/a/../health', '/api\\health', '/api//health', ]; for (const pathTemplate of attacks) assert.throws(() => validateReadonlyOperation({ ...health, pathTemplate })); assert.throws(() => validateReadonlyOperation({ ...health })); assert.throws(() => validateReadonlyOperation({ operationId: health.operationId, method: 'GET', riskLevel: 'R0', pathTemplate: health.pathTemplate, }), ); await assert.rejects(() => collectOne({ origin: 'http://192.168.1.2', alias: 'instance-1' }, { ...health }, fakeResponse), ); }); test('all path bypass classes fail before transport', async () => { let calls = 0; const transport = async () => { calls++; return new Response('{}', { headers: { 'content-type': 'application/json' } }); }; const attacks = [ 'https://evil.invalid/api/health', '//evil.invalid/api/health', '/api/health?q=1', '/api/health#x', '/api/%68ealth', '/api/%252e%252e/x', '/api/a/../x', '/api\\health', '/api//health', ]; for (const pathTemplate of attacks) await assert.rejects(() => collectOne( { origin: 'http://192.168.1.2', alias: 'instance-1' }, { ...health, pathTemplate }, transport, ), ); assert.equal(calls, 0); assert.throws(() => selectReadonlyOperations([{ ...health }])); }); test('collectOne validates RFC1918 origin before transport', () => { for (const origin of [ 'http://127.0.0.1', 'http://169.254.1.1', 'http://100.100.100.200', 'http://8.8.8.8', 'http://router.local', 'http://u:p@192.168.1.2', 'http://192.168.1.2/x', 'http://192.168.1.2?q=1', 'ftp://192.168.1.2', ]) assert.throws(() => validateInstanceOrigin(origin)); assert.equal(validateInstanceOrigin('https://172.16.2.3:8443'), 'https://172.16.2.3:8443'); }); test('passive selection explicitly denies connectivity and active scan', () => { const { selected, denied } = selectReadonlyOperations(upstream58e2204Operations); assert.ok(denied.some((x: any) => x.pathTemplate === '/api/connectivity')); assert.ok(denied.some((x: any) => x.pathTemplate === '/api/network/operators/scan')); assert.ok(!selected.some((x: any) => x.pathTemplate === '/api/connectivity')); }); test('config uses formal validation plus collector LAN/auth/origin constraints', () => { const good = { instances: [ { id: 'a', url: 'http://192.168.1.2', auth: { mode: 'none' } }, { id: 'b', url: 'https://10.0.0.2', auth: { mode: 'none' } }, ], }; assert.deepEqual( loadInstances(good).map((x: any) => x.alias), ['instance-1', 'instance-2'], ); const bad = [ 'http://127.0.0.1', 'http://169.254.1.2', 'http://100.100.100.200', 'http://8.8.8.8', 'http://router.local', 'http://u:***@192.168.1.2', 'http://192.168.1.2/path', 'http://192.168.1.2?q=1', 'http://192.168.1.2#x', 'http://[::1]', ]; for (const url of bad) assert.throws(() => loadInstances({ instances: [ { id: 'a', url }, { id: 'b', url: 'http://10.0.0.2' }, ], }), ); assert.throws(() => loadInstances({ instances: [ { id: 'a', url: 'http://192.168.1.2', auth: { password: 'x' } }, { id: 'b', url: 'http://10.0.0.2' }, ], }), ); assert.throws(() => loadInstances({ instances: [{ id: 'a', url: 'http://192.168.1.2' }] })); assert.throws(() => loadInstances({ instances: [ { id: 'a', url: 'http://192.168.1.2' }, { id: 'b', url: 'http://10.0.0.2' }, { id: 'c', url: 'http://10.0.0.3' }, ], }), ); }); test('directional response JSONPaths redact every declared path and preserve shape/types', () => { const ops = upstream58e2204Operations.filter((op: any) => op.sensitiveFields.some((f: any) => f.direction === 'response'), ); assert.ok(ops.length > 0); for (const op of ops) for (const field of op.sensitiveFields.filter((f: any) => f.direction === 'response')) { const parts = parseResponseSensitivePath(field.path); assert.ok(parts.length > 0, `${op.operationId} unsupported response path`); let leaf: any = { s: 'private', n: 42, b: true, z: null, a: ['x', { q: 9 }] }; let payload: any = leaf; for (let i = parts.length - 1; i >= 0; i--) { const p = parts[i]; payload = p === '*' ? [payload] : { [p]: payload }; } const out: any = redactOperationPayload(payload, op); let hit = out; for (const p of parts) hit = p === '*' ? hit[0] : hit[p]; assert.deepEqual(hit, { s: '[REDACTED]', n: 0, b: false, z: null, a: ['[REDACTED]', { q: 0 }], }); } }); test('deep sanitizer handles reviewed key/value bypasses without schema destruction', () => { const input: any = { credential: 'x', api_key: 'x', access_key: 'x', device_key: 'x', private_key: 'x', pin: 12, puk: 34, username: 'x', user: 'x', account: 'x', serial: 'x', revision: 'x', path: '/private/a', template: 'user=x token=abc123 path=/private/a', config: { access_token: 'tiny' }, phone_number_is_manual: true, sms_center_is_manual: false, message: 'API is healthy', nested: '{"username":"x","enabled":true}', host: 'private.internal:8080', short_number: '12345678', split_id: '12-34-56-78-90-12-34', ipv6: '::1%lo0', latitude: 1.234567, lon: 2.345678, cell_id: 99, lac: 2, tac: 3, pci: 4, enb: 5, gnb: 6, uptime: 777, traffic: 888, timestamp: '2026-07-16T12:34:56Z', unknown: 'AKIAIOSFODNN7EXAMPLE', }; const x: any = redact(input); for (const k of [ 'credential', 'api_key', 'access_key', 'device_key', 'private_key', 'username', 'user', 'account', 'serial', 'revision', 'path', 'template', 'host', 'short_number', 'split_id', 'ipv6', 'timestamp', 'unknown', ]) assert.equal(x[k], '[REDACTED]'); for (const k of [ 'pin', 'puk', 'latitude', 'lon', 'cell_id', 'lac', 'tac', 'pci', 'enb', 'gnb', 'uptime', 'traffic', ]) assert.equal(x[k], 0); assert.equal(typeof x.nested, 'string'); assert.deepEqual(JSON.parse(x.nested), { username: '[REDACTED]', enabled: true }); assert.deepEqual(x.config, { access_token: '[REDACTED]' }); assert.equal(x.phone_number_is_manual, true); assert.equal(x.sms_center_is_manual, false); assert.equal(x.message, 'API is healthy'); }); test('schema shapes and primitive types survive conservative redaction', () => { const x: any = redact({ phone_numbers: ['12345678', '87654321'], ip_addresses: [{ v4: '192.168.1.9', active: true }], urls: ['private.example/x'], webhook: { url: '//private.example/h', enabled: true }, config: { s: 'secret', n: 42, b: true, z: null, a: ['x', { n: 9 }] }, }); assert.ok(Array.isArray(x.phone_numbers)); assert.equal(x.phone_numbers.length, 2); assert.ok(Array.isArray(x.ip_addresses)); assert.equal(typeof x.ip_addresses[0], 'object'); assert.ok(Array.isArray(x.urls)); assert.equal(typeof x.webhook, 'object'); assert.deepEqual(x.config, { s: '[REDACTED]', n: 0, b: false, z: null, a: ['[REDACTED]', { n: 0 }], }); }); test('invalid declared JSON and non-JSON bodies are non-json and discarded', async () => { const invalid = await collectOne( { origin: 'http://192.168.1.2', alias: 'instance-1' }, health, async () => new Response('{bad', { status: 200, headers: { 'content-type': 'application/json' } }), ); assert.equal(invalid.statusCategory, 'non-json'); assert.deepEqual(invalid.payload, { error: 'invalid-json' }); const text = await collectOne( { origin: 'http://192.168.1.2', alias: 'instance-1' }, health, async () => new Response('private body', { status: 200, headers: { 'content-type': 'text/plain' } }), ); assert.equal(text.statusCategory, 'non-json'); assert.deepEqual(text.payload, { text: '[REDACTED]' }); }); test('collector errors never echo rejected origin values', async () => { const secret = 'http://user:password@evil.invalid/private?token=x'; try { await collectOne({ origin: secret, alias: 'instance-1' }, health, fakeResponse); assert.fail('expected rejection'); } catch (error: any) { assert.ok(!String(error.message).includes(secret)); assert.ok(!String(error.message).includes('password')); } });