/** * serve-descriptor — the ONE validator for the untrusted daemon-discovery * artifact `.openlore/serve.json`. * * The descriptor is a repo-local, attacker-writable file that OpenLore reads at * three sites — the `serve` CLI, the serve-client the stdio MCP server delegates * through, and the Pi extension. Each reader then FETCHES the host/port the file * names for a liveness probe and, on a healthy answer, POSTs the project * directory and full tool arguments to it. A poisoned descriptor is therefore an * SSRF / egress vector, a leak of the directory + tool args, and a result- * poisoning channel into the agent's context (mcp-security: Untrusted Artifact * Deserialization). One threat model must not have three postures. * * This module is the one lock, extracted verbatim from `serve.ts`'s existing * reader: loopback-only host, integer port 1–65535, integer pid > 0, token * absent-or-string. No check is invented here beyond the ones `serve` already * applied. A descriptor that fails any check is treated exactly as ABSENT — the * reader returns null and the caller takes its existing no-daemon path (spawn a * fresh daemon or fall back to in-process dispatch). No field of an invalid * descriptor ever becomes a fetch target or request header. * * Dependency-light by contract (mcp-security ServeDescriptorValidatedAtEveryReader * + the MCP↔Pi parity doctrine): it imports only node builtins and the loopback * predicate it shares with the HTTP guard, so the Pi host can import it without * pulling in the analyzer. */ /** * The validated daemon-discovery descriptor. `startedAt` / `version` are * advisory metadata (normalized to '' when absent or ill-typed); the other four * fields are security-critical and are only present on a descriptor that passed * {@link validateServeDescriptor}. */ export interface ServeDescriptor { port: number; pid: number; host: string; token?: string; startedAt: string; version: string; } export interface ServeHealth { ok: true; presetDispatchEnforced: true; root: string; pid: number; preset: string; tools: string[]; tokenProtected: boolean; tokenAuthenticated: boolean; } /** Resolve aliases to the stable filesystem identity used in health proofs. */ export declare function canonicalServeRoot(root: string): string; /** Validate the semantic compatibility and identity fields returned by `/health`. */ export declare function validateServeHealth(parsed: unknown, expectedRoot: string, descriptor?: Pick): ServeHealth | null; /** * Validate an already-parsed value as a {@link ServeDescriptor}. Returns the * normalized descriptor, or null if ANY security-critical field is * missing / ill-typed / out of range, or the host is not a loopback form. */ export declare function validateServeDescriptor(parsed: unknown): ServeDescriptor | null; /** * Read + validate a serve.json at `descriptorPath`. Any failure — missing file, * malformed JSON, or a descriptor that fails {@link validateServeDescriptor} — * resolves to null, so a poisoned descriptor is indistinguishable from an absent * one and no field of it ever reaches a fetch target or request header. */ export declare function readServeDescriptor(descriptorPath: string): Promise; //# sourceMappingURL=serve-descriptor.d.ts.map