import { readFileSync, readdirSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { dirname, join } from 'node:path' import { describe, expect, it } from 'vitest' import { createClientHandshake, createHostHandshake } from '../src/handshake.js' import { nobleEd25519Signer, nobleEd25519Verifier } from '../src/ed25519.js' import { createE2ESession } from '../src/session.js' import { MemoryDevicePinStore } from '../src/keystore.js' import { MAX_FRAME_BYTES } from '../src/envelope.js' import { boundProofProvider, fromUtf8, makeHostIdentity, utf8, verifyBoundProof } from './helpers.js' const HERE = dirname(fileURLToPath(import.meta.url)) const SRC = join(HERE, '..', 'src') /** A passthrough "relay" that records every byte payload it forwards (the INV2 spy). */ class RelaySpy { readonly captured: Uint8Array[] = [] forward(payload: Uint8Array): Uint8Array { expect(payload.length).toBeLessThanOrEqual(MAX_FRAME_BYTES) // §4.1 payloadLen guard this.captured.push(payload.slice()) return payload } snapshot(): string { return this.captured.map((b) => Buffer.from(b).toString('latin1')).join('') } } async function establish() { const id = makeHostIdentity() const client = createClientHandshake({ aeadOffer: ['xchacha20-poly1305', 'aes-256-gcm'], deviceAuthProofProvider: boundProofProvider(), verifier: nobleEd25519Verifier(), pinStore: new MemoryDevicePinStore(), hostId: 'h1', }) const host = createHostHandshake({ signer: nobleEd25519Signer(id.privateKey), agentPubkey: id.agentPubkey, supported: ['xchacha20-poly1305', 'aes-256-gcm'], verifyDeviceProof: async (p, b) => verifyBoundProof(p, b), }) const spy = new RelaySpy() const ch = await client.start() const hh = await host.onClientHello(ch) // relay forwards these as opaque DATA const rc = await client.onHostHello(hh, id.agentPubkey) return { spy, clientSession: createE2ESession('client', rc), hostSession: createE2ESession('host', host.result!), } } describe('T12 integration + INV2 tripwire', () => { it('full loop through the relay spy: bidirectional plaintext round-trips', async () => { const { spy, clientSession, hostSession } = await establish() const up = spy.forward(clientSession.seal(utf8('echo test'))) expect(fromUtf8(hostSession.open(up))).toBe('echo test') const down = spy.forward(hostSession.seal(utf8('echo reply'))) expect(fromUtf8(clientSession.open(down))).toBe('echo reply') }) it('INV2 tripwire (merge-blocking): the plaintext canary appears NOWHERE in the relay spy', async () => { const { spy, clientSession, hostSession } = await establish() const canary = `E2E_PLAINTEXT_CANARY_${crypto.randomUUID()}` const wire = spy.forward(clientSession.seal(utf8(canary))) expect(fromUtf8(hostSession.open(wire))).toBe(canary) // The marker must not transit in cleartext anywhere the relay can see. expect(spy.snapshot()).not.toContain(canary) for (const buf of spy.captured) { expect(Buffer.from(buf).toString('latin1')).not.toContain(canary) expect(Buffer.from(buf).toString('utf8')).not.toContain(canary) } }) it('INV11-adjacent isolation: src imports no ws/pg/xterm/DOM-runtime/node builtins', () => { const forbidden = /from\s+['"](ws|pg|xterm|jsdom|node:[a-z]+|@xterm)/ for (const file of readdirSync(SRC).filter((f) => f.endsWith('.ts'))) { const text = readFileSync(join(SRC, file), 'utf8') expect(text, `${file} must stay a pure isomorphic crypto core`).not.toMatch(forbidden) } }) it('no console.* in src (coding-style)', () => { for (const file of readdirSync(SRC).filter((f) => f.endsWith('.ts'))) { expect(readFileSync(join(SRC, file), 'utf8')).not.toMatch(/console\.\w+/) } }) it('vector freeze: all vector files parse to their expected shape (agent↔browser parity anchor)', () => { const dir = join(HERE, 'vectors') const files = readdirSync(dir).filter((f) => f.endsWith('.json')) expect(files.sort()).toEqual(['aead.json', 'envelope.json', 'fingerprint.json', 'hkdf.json']) const aead = JSON.parse(readFileSync(join(dir, 'aead.json'), 'utf8')) as unknown[] expect(aead.length).toBe(2) const fpr = JSON.parse(readFileSync(join(dir, 'fingerprint.json'), 'utf8')) as { enrollFpr: string } expect(fpr.enrollFpr.startsWith('sha256:')).toBe(true) }) })