testServeTlsPinning
tcm serve --tls: clients pin the certificate and never send the token to an impostor
import { plainEnv, tcmBin, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness"; import { codebase, startServerInfo } from "#codebase/tcmts/cli/test/e2e/sync"; import assert from "node:assert/strict"; import { spawn, spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import tls from "node:tls"; export const testServeTlsPinning = () => withChildren(async (keep) => { const root = tempDir("sync"); const server = codebase(root, "tls-server"); const client = codebase(root, "tls-client"); client.tcm("add"); const { url, fingerprint } = await startServerInfo(keep, server.dir, "tlstok", "--tls"); assert.match(url, /^https:/); client.tcm("remote", "add", "s", url, "--token", "tlstok", "--fingerprint", fingerprint!); assert.match(client.tcm("run", "square", "7", "--on", "s"), /^49$/m); // an impostor with a different certificate: the handshake completes, but no request bytes may follow const other = codebase(root, "impostor"); spawnSync(process.execPath, [tcmBin(), "serve", "--tls", "--port", "1"], { cwd: other.dir, env: plainEnv(), timeout: 5000 }); // creates other/.tcm/tls const received: Buffer[] = []; const impostor = tls.createServer( { cert: fs.readFileSync(path.join(other.dir, ".tcm/tls/cert.pem")), key: fs.readFileSync(path.join(other.dir, ".tcm/tls/key.pem")) }, (socket) => socket.on("data", (d) => received.push(d)), ); try { await new Promise<void>((r) => impostor.listen(0, "127.0.0.1", r)); const port = (impostor.address() as { port: number }).port; client.tcm("remote", "add", "fake", `https://127.0.0.1:${port}`, "--token", "tlstok", "--fingerprint", fingerprint!); // run the client asynchronously: the impostor lives in this process and must keep serving the handshake const attempt = await new Promise<{ code: number | null; out: string }>((resolve) => { const child = keep(spawn(process.execPath, [tcmBin(), "run", "square", "7", "--on", "fake"], { cwd: client.dir, env: plainEnv() })); let out = ""; child.stdout.on("data", (d) => (out += d)); child.stderr.on("data", (d) => (out += d)); child.on("close", (code) => resolve({ code, out })); }); assert.equal(attempt.code, 1); assert.match(attempt.out, /fingerprint mismatch[\s\S]*refusing to send the token/); await new Promise((r) => setTimeout(r, 200)); } finally { impostor.close(); } assert.equal(Buffer.concat(received).length, 0, "nothing, not even headers, reached the impostor"); assert.throws(() => server.tcm("serve", "--host", "0.0.0.0", "--port", "0"), /without TLS would send the token in clear text/); })