import assert from 'node:assert/strict'; import {randomUUID} from 'node:crypto'; import {once} from 'node:events'; import {createServer} from 'node:http'; import test from 'node:test'; import {Server} from 'socket.io'; import {io, type Socket as Client} from 'socket.io-client'; import type {ForwardAck, NodeRelayRequest, RelayClaims, RelayMethod, WorldPayloads, WorldVoxelChunk} from '../types.ts'; import {install_relay_routes} from '../runtime/relay_routes.ts'; import {create_session_store} from '../runtime/session_store.ts'; import {DEFAULT_LIMITS} from '../utils/relay_config.ts'; import {RelayError, check_permissions, success, validate_node_ack, validate_request} from '../utils/relay_validation.ts'; import {encode_token} from '../utils/token_utils.ts'; const input = (): WorldPayloads['world.chunk'] => ({x: -12, z: 7, world_generation: randomUUID(), watch_id: randomUUID()}); const blocks = (index = 0, position = 4095) => { const data = Buffer.alloc(8192); data.writeUInt16LE(index, position * 2); return data.toString('base64'); }; const chunk = (payload = input()): WorldVoxelChunk => ({ x: payload.x, z: payload.z, revision: randomUUID(), world_generation: payload.world_generation, min_y: -64, height: 384, palette: [{name: 'minecraft:air', color: 0, properties: {}, occluding: false}, {name: 'minecraft:stone', color: 0x777777, properties: {}, occluding: true}, {name: 'minecraft:oak_stairs', color: 0xb88754, properties: {facing: 'east', half: 'bottom', waterlogged: true}, occluding: false}], sections: [{y: 0, blocks: blocks(1, 17)}, {y: 1, blocks: blocks(2, 2048)}], }); const request = (payload: unknown) => validate_request({request_id: randomUUID(), method: 'world.chunk', payload}, DEFAULT_LIMITS); const ack = (data: unknown, payload: unknown) => validate_node_ack('world.chunk', success(data), 3, DEFAULT_LIMITS, payload); const rejected = (action: () => unknown) => assert.throws(action, (error: unknown) => error instanceof RelayError && error.code === 'INVALID_REQUEST'); test('world.chunk requests require exact coordinates, generation and watch using existing permissions', () => { const payload = input(); request(payload); request({...payload, x: -1_875_000, z: 1_875_000}); request({...payload, x: 1_875_000, z: -1_875_000}); check_permissions(['bot.snapshot']); rejected(() => check_permissions(['world.chunk'])); for (const key of Object.keys(payload)) { const missing = {...payload} as Record; delete missing[key]; rejected(() => request(missing)); } for (const patch of [{x: -1_875_001}, {z: 1_875_001}, {x: 0.5}, {z: '7'}, {x: NaN}, {z: Infinity}, {world_generation: 'bad'}, {watch_id: null}, {watch_id: 'bad'}, {world_generation: undefined}, {revision: randomUUID()}, {bot_id: 3}, {context_generation: randomUUID()}]) { rejected(() => request({...payload, ...patch})); } for (const value of [null, [] as unknown[], 'chunk']) rejected(() => request(value)); }); test('world.chunk accepts empty sparse sections and preserves buried blocks and state in two real sections', () => { const payload = input(), data = chunk(payload); ack(data, payload); assert.equal(Buffer.from(data.sections[0]!.blocks, 'base64').readUInt16LE(17 * 2), 1); assert.equal(Buffer.from(data.sections[1]!.blocks, 'base64').readUInt16LE(2048 * 2), 2); assert.equal(Buffer.from(data.sections[0]!.blocks, 'base64').readUInt16LE(4095 * 2), 0); assert.equal(data.sections[0]!.blocks.length, 10_924); ack({...data, sections: []}, payload); ack({...data, min_y: -4096, height: 1, sections: [{y: 0, blocks: blocks()}]}, payload); ack({...data, min_y: 4096, height: 4096, sections: [{y: 255, blocks: blocks()}]}, payload); // The wire contract does not require zero padding beyond a partial section's height. ack({...data, height: 17, sections: [{y: 1, blocks: blocks(1)}]}, payload); ack({...data, world_generation: data.world_generation.toUpperCase()}, payload); }); test('world.chunk ACK metadata is exact and matches request coordinates and world generation', () => { const payload = input(), data = chunk(payload); for (const key of Object.keys(data)) { const missing = {...data} as Record; delete missing[key]; rejected(() => ack(missing, payload)); } for (const patch of [{x: payload.x + 1}, {z: payload.z - 1}, {x: 1_875_001}, {z: 0.5}, {world_generation: randomUUID()}, {world_generation: 'bad'}, {revision: 'bad'}, {min_y: -4097}, {min_y: 4097}, {min_y: 0.5}, {height: 0}, {height: 4097}, {height: 1.5}, {watch_id: payload.watch_id}, {stream_id: randomUUID()}, {context_generation: randomUUID()}, {extra: true}]) { rejected(() => ack({...data, ...patch}, payload)); } for (const value of [null, [], {}, undefined]) rejected(() => ack(value, payload)); for (const value of [undefined, {}, {...payload, x: payload.x + 1}, {...payload, world_generation: randomUUID()}]) { rejected(() => ack(data, value)); } validate_node_ack('world.chunk', {status: 'error', data: {code: 'RESYNC_REQUIRED', message: 'Stale watch', retryable: true}}, 3, DEFAULT_LIMITS, payload); }); test('world.chunk palette entries require bounded colors, names, occlusion and exact fields', () => { const payload = input(), data = chunk(payload), block = data.palette[1]!; const accept = (palette: unknown) => ack({...data, palette, sections: []}, payload); accept([{...block, name: 'a', color: 0}]); accept([{...block, name: 'x'.repeat(128), color: 0xffffff}]); for (const palette of [[], null, {}, Array.from({length: 8193}, () => block)]) rejected(() => accept(palette)); for (const key of Object.keys(block)) { const missing = {...block} as Record; delete missing[key]; rejected(() => accept([missing])); } for (const patch of [{name: ''}, {name: 'x'.repeat(129)}, {color: -1}, {color: 0x1000000}, {color: 0.5}, {occluding: 1}, {occluding: 'true'}, {extra: true}, {shape: 'cube'}, {properties: [] as unknown[]}, {properties: null}]) { rejected(() => accept([{...block, ...patch}])); } }); test('world.chunk properties accept canonical state keys and bounded primitives without a semantic whitelist', () => { const payload = input(), data = chunk(payload); const accept = (properties: unknown) => ack({...data, palette: [{...data.palette[0], properties}], sections: []}, payload); accept({custom_block_state: 'unknown_but_valid', rotation: 65_536, min: -65_536, solid: true, empty: '', zero: 0}); accept(Object.fromEntries(Array.from({length: 32}, (_, i) => [`property_${i}`, i % 2 === 0 ? 'x'.repeat(64) : false]))); accept({['k'.repeat(32)]: true}); for (const properties of [ Object.fromEntries(Array.from({length: 33}, (_, i) => [`p${i}`, true])), {'': true}, {'Facing': 'east'}, {'minecraft:axis': 'x'}, {'a-b': true}, {'state\n': true}, {['k'.repeat(33)]: true}, {key: 'x'.repeat(65)}, {key: 65_537}, {key: -65_537}, {key: 0.5}, {key: null}, {key: [] as unknown[]}, {key: {}}, {key: undefined}, {key: NaN}, {key: Infinity}, ]) rejected(() => accept(properties)); const getter = Object.defineProperty({}, 'state', {enumerable: true, get() {throw new Error('must not execute');}}); rejected(() => accept(getter)); rejected(() => accept(Object.defineProperty({}, 'state', {value: true}))); rejected(() => accept({[Symbol('state')]: true})); }); test('world.chunk sections are sparse, unique, ascending and bounded by height', () => { const payload = input(), data = chunk(payload), section = data.sections[0]!; ack({...data, sections: [{...section, y: 3}, {...section, y: 23}]}, payload); for (const sections of [null, {}, [section, section], [{...section, y: 1}, section], [{...section, y: -1}], [{...section, y: 24}], [{...section, y: 0.5}], [{...section, y: '0'}], [{blocks: section.blocks}], [{y: 0}], [{...section, extra: true}], Array.from({length: 257}, (_, y) => ({...section, y}))]) { rejected(() => ack({...data, sections}, payload)); } rejected(() => ack({...data, height: 16, sections: [{...section, y: 1}]}, payload)); rejected(() => ack({...data, sections: new Array(1)}, payload)); }); test('world.chunk section blocks enforce canonical base64, exact UInt16LE byte count and all palette indices', () => { const payload = input(), data = chunk(payload), encoded = blocks(); const accept = (value: unknown) => ack({...data, sections: [{y: 0, blocks: value}]}, payload); const noncanonical = `${encoded.slice(0, -2)}B=`; assert.equal(Buffer.from(noncanonical, 'base64').toString('base64'), encoded); for (const value of ['', encoded.slice(0, -1), `${encoded}\n`, 'A'.repeat(10_924), Buffer.alloc(8191).toString('base64'), Buffer.alloc(8193).toString('base64'), noncanonical, `_${encoded.slice(1)}`, Buffer.alloc(8192), new Uint16Array(4096)]) { rejected(() => accept(value)); } for (const position of [0, 17, 2048, 4095]) { accept(blocks(data.palette.length - 1, position)); rejected(() => accept(blocks(data.palette.length, position))); rejected(() => accept(blocks(65_535, position))); } }); test('world.chunk maximum palette fits dedicated world complexity budget and preserves 8 MiB byte limits', () => { const payload = input(), data = chunk(payload); const properties = Object.fromEntries(Array.from({length: 32}, (_, index) => [`p${index}`, true])); const maximum: WorldVoxelChunk = {...data, height: 4096, palette: Array.from({length: 8192}, (_, index) => ({name: `block_${index}`, color: index, properties, occluding: true})), sections: Array.from({length: 256}, (_, y) => ({y, blocks: blocks(8191)}))}; ack(maximum, payload); assert.ok(Buffer.byteLength(JSON.stringify(success(maximum))) < 8 * 1024 * 1024); const size = Buffer.byteLength(JSON.stringify(success(data))); validate_node_ack('world.chunk', success(data), 3, {...DEFAULT_LIMITS, max_payload_bytes: size}, payload); rejected(() => validate_node_ack('world.chunk', success(data), 3, {...DEFAULT_LIMITS, max_payload_bytes: size - 1}, payload)); const oversized = {...maximum, palette: maximum.palette.map(block => ({...block, properties: Object.fromEntries(Object.keys(properties).map(key => [key, 'x'.repeat(64)]))}))}; rejected(() => ack(oversized, payload)); rejected(() => validate_node_ack('bot.snapshot', success(maximum), 3, DEFAULT_LIMITS)); }); test('world.chunk uses private correlated ACKs, snapshot permission on both roles and fresh validated reads', async t => { const key = 'world-voxel-relay-test-secret'; const scope = {forward_id: 1, forward_session_id: randomUUID(), node_session_id: randomUUID(), node_id: 2, bot_id: 3}; const http = createServer(), server = new Server(http), namespace = server.of('/relay'); const store = create_session_store({forward_id: 1, key, limits: DEFAULT_LIMITS, on_status() {}}); const dispose = install_relay_routes(namespace, store), clients: Client[] = []; t.after(async () => {store.close(); dispose(); for (const client of clients) client.close(); await server.close();}); http.listen(0, '127.0.0.1'); await once(http, 'listening'); store.open({...scope, lease_expires_at: Date.now() + 60_000}); const send = (socket: Client, event: string, value: unknown): Promise> => socket.timeout(3000).emitWithAck(event, value); const connect = async (role: 'web' | 'node', permissions: RelayMethod[] = ['bot.snapshot']) => { const socket = io(`http://127.0.0.1:${(http.address() as {port: number}).port}/relay`, {transports: ['websocket'], forceNew: true, reconnection: false}); clients.push(socket); await new Promise(resolve => socket.once('connect', resolve)); const now = Date.now(), grant_id = randomUUID(), expires_at = now + 50_000; if (role === 'web') store.authorize({forward_session_id: scope.forward_session_id, grant_id, user_id: 4, permissions, expires_at}); const claims: RelayClaims = {...scope, v: 1, role, permissions, issued_at: now, join_before: now + 10_000, expires_at, jti: randomUUID(), ...(role === 'web' ? {grant_id, user_id: 4} : {})}; assert.equal((await send(socket, 'join_room', {token: encode_token(key, claims)})).status, 'success'); if (role === 'node') assert.equal((await send(socket, 'forward_node_ready', {forward_session_id: scope.forward_session_id})).status, 'success'); return socket; }; const node = await connect('node'), web = await connect('web'), observer = await connect('web'); const limited = await connect('web', ['bot.logs']); const payload = input(), command = {request_id: randomUUID(), method: 'world.chunk', payload}; const data = chunk(payload); let response: unknown = success(data), calls = 0; const leaked: string[] = []; observer.onAny(event => leaked.push(event)); node.on('relay_request', (value: NodeRelayRequest, reply: (result: unknown) => void) => { calls++; assert.equal(value.method, 'world.chunk'); assert.deepEqual(value.payload, payload); assert.equal(value.forward_session_id, scope.forward_session_id); assert.notEqual(value.correlation_id, value.request_id); assert.ok(value.deadline_at > Date.now()); reply(response); }); const error = (value: ForwardAck, expected: string) => { assert.equal(value.status, 'error'); if (value.status === 'error') assert.equal(value.data.code, expected); }; store.set_control_available(false); assert.deepEqual(await send(web, 'relay_request', command), response); response = success({...data, revision: randomUUID(), sections: []}); assert.deepEqual(await send(web, 'relay_request', command), response); assert.equal(calls, 2); for (const patch of [{x: data.x + 1}, {z: data.z + 1}, {world_generation: randomUUID()}, {sections: [{y: 0, blocks: blocks(3)}]}]) { response = success({...data, ...patch}); error(await send(web, 'relay_request', command), 'INVALID_REQUEST'); } response = {status: 'error', data: {code: 'RESYNC_REQUIRED', message: 'Stale watch', retryable: true}}; assert.deepEqual(await send(web, 'relay_request', command), response); response = success(data); assert.deepEqual(await send(web, 'relay_request', command), response); const before = calls; error(await send(limited, 'relay_request', command), 'FORBIDDEN'); error(await send(node, 'relay_request', command), 'FORBIDDEN'); assert.equal(calls, before); assert.deepEqual(leaked, []); store.set_control_available(true); await connect('node', ['bot.logs']); error(await send(web, 'relay_request', command), 'FORBIDDEN'); assert.equal(calls, before); });