/** * Authenticated IPC helpers for sandbox communication. * * Uses a shared random token exchanged out-of-band (env var or temp file) * to authenticate messages between the host and the sandbox process. */ import { randomBytes, timingSafeEqual } from "node:crypto"; import type { IpcMessage, IpcAuthContext } from "./types.ts"; // ── Token generation & validation ────────────────────────────────────────────── /** * Generate a cryptographically random IPC authentication token. */ export function generateIpcToken(byteLength = 32): string { return randomBytes(byteLength).toString("hex"); } /** * Compare two tokens in constant time. */ export function tokensMatch(a: string, b: string): boolean { if (a.length !== b.length) return false; const bufA = Buffer.from(a, "utf-8"); const bufB = Buffer.from(b, "utf-8"); if (bufA.length !== bufB.length) return false; try { return timingSafeEqual(bufA, bufB); } catch { return false; } } /** * Create an authenticated IPC context. */ export function createIpcAuth(): IpcAuthContext { return { token: generateIpcToken(), createdAt: Date.now(), }; } /** * Sign an outgoing IPC message with the auth token. */ export function signMessage(msg: Omit, token: string): IpcMessage { return { ...msg, token }; } /** * Verify an incoming IPC message against the expected token. * Returns true if the message is authentic. */ export function verifyMessage(msg: IpcMessage, expectedToken: string): boolean { if (!msg.token) return false; return tokensMatch(msg.token, expectedToken); } /** * Create a message envelope for a request. */ export function createRequest( type: IpcMessage["type"], requestId: string, payload?: unknown, ): Omit { return { type, requestId, payload }; } /** * Maximum age for an IPC token (5 minutes). * Tokens older than this are rejected. */ export const IPC_TOKEN_MAX_AGE_MS = 5 * 60 * 1000; /** * Check if an IPC auth context is still valid (not expired). */ export function isIpcAuthValid(auth: IpcAuthContext): boolean { return Date.now() - auth.createdAt < IPC_TOKEN_MAX_AGE_MS; }