235 lines
16 KiB
TypeScript
235 lines
16 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import { createHash, randomUUID } from "node:crypto"
|
|
import { mkdtemp, readdir, rm, rmdir } from "node:fs/promises"
|
|
import { tmpdir } from "node:os"
|
|
import { join } from "node:path"
|
|
import type { ServerProbeExecutionSource } from "../modules/mcp/diagnostic-cancellation-source.js"
|
|
import { fingerprint } from "../modules/mcp/security.js"
|
|
import type { ServerProbeJournalContext } from "./server-probe-journal.js"
|
|
import type { ServerProbeReceipt } from "./server-probe-execution.js"
|
|
import { createLegacyServerProbeVerifier, createLegacyServerProbeVerifierForTests, type LegacyServerProbeBundle, type LegacyServerProbeVerifierCrypto } from "./server-probe-legacy-verifier.js"
|
|
|
|
const secret = "PRIVATE_LEGACY_PROVIDER_VALUE"
|
|
const stamp = new Date("2020-01-01T00:00:00Z")
|
|
async function fixture(kind: "rest" | "mcp" = "rest", terminal = true) {
|
|
const executionId = randomUUID(), receiptId = randomUUID(), ciphertexts = new Map<string, string>()
|
|
const context: ServerProbeJournalContext = { identity: kind === "rest"
|
|
? { kind, ownerId: "original-owner", executionId, issuedAt: 1, expiresAt: stamp.getTime(), requestId: randomUUID(), authentication: "jwt" }
|
|
: { kind, ownerId: "original-owner", executionId, issuedAt: 1, expiresAt: stamp.getTime(), connectionId: randomUUID(), credentialId: randomUUID(), operationName: "servers.probes.run" }, sourceServerId: 9, tool: "ping" }
|
|
const scope = { ownerId: context.identity.ownerId, receiptId, identityKind: kind, executionId, bindingProof: "a".repeat(64), authorityExpiresAt: stamp.toISOString() }
|
|
const source: ServerProbeExecutionSource = { scope, sourceDigest: "d".repeat(64), sourceState: "unknown", terminal: true,
|
|
serverIds: [9], stageBudget: 2, originalCredentialId: context.identity.kind === "mcp" ? context.identity.credentialId : randomUUID(),
|
|
originalContext: context as ServerProbeExecutionSource["originalContext"] }
|
|
const seenProofs: { domain: string; isString: boolean }[] = []
|
|
const crypto: LegacyServerProbeVerifierCrypto = {
|
|
open: async value => { const opened = ciphertexts.get(value); if (!opened) throw Error(secret); return opened },
|
|
proof: async (domain, value) => { seenProofs.push({ domain, isString: typeof value === "string" }); return createHash("sha256").update(domain + fingerprint(value)).digest("hex") },
|
|
}
|
|
const receipt: ServerProbeReceipt = { receiptId, tool: "ping", serverId: 9, state: "completed", httpStatus: 200,
|
|
output: secret, data: { kind: "ping" }, attemptedStages: 1, completedStages: 1, automaticReplay: false, forwardingVerified: false }
|
|
const id = context.identity
|
|
const anchor: Record<string, unknown> = { receipt_id: receiptId, binding_proof: scope.bindingProof, identity_kind: kind, execution_id: executionId,
|
|
actor_id: id.ownerId, authentication: id.kind === "mcp" ? "mcp" : id.authentication,
|
|
mcp_connection_id: id.kind === "mcp" ? id.connectionId : null, mcp_credential_id: id.kind === "mcp" ? id.credentialId : null,
|
|
request_id: id.kind === "rest" ? id.requestId : null, authority_issued_at: String(id.issuedAt), authority_expires_at: stamp,
|
|
source_server_id: 9, destination_server_id: null, tool: "ping", state: terminal ? "completed" : "claimed",
|
|
attempted_stages: terminal ? 1 : null, completed_stages: terminal ? 1 : null, receipt_ciphertext: null, receipt_proof: null,
|
|
created_at: stamp, finished_at: terminal ? stamp : null }
|
|
const stage: Record<string, unknown> = { receipt_id: receiptId, stage_index: 1, kind: "native-ping", timeout_ms: 1000,
|
|
state: "received", outcome: "received", created_at: stamp, received_at: stamp }
|
|
const bundle: LegacyServerProbeBundle = { identityKind: kind, executionId, anchor, stages: terminal ? [stage] : [] }
|
|
let loads = 0
|
|
const deps = { loadMcpSource: async (operationId: string) => { loads++; assert.equal(operationId, executionId); return source } }
|
|
const verify = createLegacyServerProbeVerifierForTests(deps, crypto)
|
|
const refreshReceipt = async (rawContext: unknown = context) => {
|
|
const plaintext = JSON.stringify({ version: 1, context: rawContext, bindingProof: scope.bindingProof, receipt })
|
|
const cipher = "fixture-cipher-" + randomUUID(); ciphertexts.set(cipher, plaintext)
|
|
anchor.receipt_ciphertext = cipher; anchor.receipt_proof = await crypto.proof("receipt", plaintext)
|
|
}
|
|
const quarantine = async () => {
|
|
bundle.quarantine = { receipt_id: receiptId, binding_proof: scope.bindingProof, identity_kind: kind, execution_id: executionId,
|
|
identity_proof: await crypto.proof("identity", context), error_code: "JOURNAL_UNKNOWN", attempted_stages: terminal ? 1 : 0,
|
|
completed_stages: terminal ? 1 : 0, created_at: stamp }
|
|
}
|
|
if (terminal) await refreshReceipt()
|
|
return { verify, deps, crypto, seenProofs, source, context, scope, bundle, receipt, anchor, stage, ciphertexts, refreshReceipt, quarantine, loads: () => loads }
|
|
}
|
|
let checks = 0
|
|
for (const kind of ["rest", "mcp"] as const) {
|
|
const f = await fixture(kind), result = await f.verify(f.bundle)
|
|
assert.equal(result.status, "verified")
|
|
if (result.status !== "verified") throw Error("Expected authenticated receipt")
|
|
assert.equal(result.authentication, "receipt"); assert.equal(result.bindingAuthenticated, true); assert.equal(result.progressAuthenticated, true)
|
|
assert.equal(result.migrationReady, false); assert.equal(result.stageLedgerAuthenticated, false); assert.equal(result.timestampsAuthenticated, false)
|
|
assert.equal(result.originalContext.identity.expiresAt, stamp.getTime()); assert.equal(result.stageCount, 1)
|
|
assert.equal(result.requiresDatabaseSourceAttestation, true); assert.equal(result.automaticReplay, false)
|
|
assert.equal(JSON.stringify(result).includes(secret), false)
|
|
assert.equal(JSON.stringify(result).includes("fixture-cipher-"), false)
|
|
assert(Object.isFrozen(result) && Object.isFrozen(result.originalContext) && Object.isFrozen(result.originalContext.identity) && Object.isFrozen(result.scope))
|
|
assert(f.seenProofs.filter(value => value.domain === "receipt").every(value => value.isString))
|
|
assert.equal(f.loads(), kind === "mcp" ? 1 : 0); checks++
|
|
}
|
|
{
|
|
const f = await fixture("mcp", false), result = await f.verify(f.bundle)
|
|
assert.equal(result.status, "verified")
|
|
if (result.status === "verified") { assert.equal(result.authentication, "approved-source"); assert.equal(result.progressAuthenticated, false) }
|
|
checks++
|
|
}
|
|
{
|
|
const f = await fixture("rest", false)
|
|
assert.equal((await f.verify(f.bundle)).status, "unresolved")
|
|
await f.quarantine()
|
|
const result = await f.verify(f.bundle)
|
|
assert.equal(result.status, "verified")
|
|
if (result.status === "verified") {
|
|
assert.equal(result.authentication, "identity-context-only"); assert.equal(result.bindingAuthenticated, false)
|
|
assert.equal(result.progressAuthenticated, false); assert.equal(result.quarantined, true)
|
|
}
|
|
assert(f.seenProofs.filter(value => value.domain === "identity").every(value => !value.isString))
|
|
// Identity MAC deliberately does not authenticate the binding or timestamp.
|
|
f.anchor.binding_proof = "b".repeat(64); f.bundle.quarantine!.binding_proof = "b".repeat(64)
|
|
f.bundle.quarantine!.created_at = new Date("2021-01-01T00:00:00Z")
|
|
const changed = await f.verify(f.bundle)
|
|
assert.equal(changed.status, "verified")
|
|
if (changed.status === "verified") { assert.equal(changed.bindingAuthenticated, false); assert.equal(changed.timestampsAuthenticated, false); assert.equal(changed.migrationReady, false) }
|
|
delete f.bundle.anchor; assert.equal((await f.verify(f.bundle)).status, "unresolved"); checks++
|
|
}
|
|
for (const orphan of ["quarantine", "cancellation"] as const) {
|
|
const f = await fixture("mcp", false)
|
|
if (orphan === "quarantine") await f.quarantine()
|
|
else {
|
|
const id = f.context.identity; assert(id.kind === "mcp")
|
|
f.bundle.cancellation = { operation_id: id.executionId, connection_id: id.connectionId, actor_id: id.ownerId }
|
|
}
|
|
delete f.bundle.anchor
|
|
const result = await f.verify(f.bundle); assert.equal(result.status, "verified")
|
|
if (result.status === "verified") { assert.equal(result.hasAnchor, false); assert.equal(result.authentication, "approved-source"); assert.equal(result.migrationReady, false) }
|
|
checks++
|
|
}
|
|
{
|
|
const f = await fixture("mcp", false); await f.quarantine(); delete f.bundle.anchor
|
|
f.bundle.quarantine!.attempted_stages = 1
|
|
// The sealed source authenticates context, but an absent anchor/stage ledger
|
|
// cannot substantiate nonzero orphan progress. Unresolved is insufficient
|
|
// evidence for transfer, not a claim that historical data was corrupted.
|
|
const result = await f.verify(f.bundle)
|
|
assert.deepEqual(result, { status: "unresolved", reasonCode: "QUARANTINE_INVALID", migrationReady: false, automaticReplay: false })
|
|
checks++
|
|
}
|
|
{
|
|
const f = await fixture("rest")
|
|
f.receipt.state = "failed"; f.receipt.httpStatus = 502; f.receipt.errorCode = "CANCELLED"
|
|
delete f.receipt.output; delete f.receipt.data; f.receipt.attemptedStages = 0; f.receipt.completedStages = 0
|
|
Object.assign(f.anchor, { state: "failed", attempted_stages: 0, completed_stages: 0 }); f.bundle.stages = []
|
|
await f.refreshReceipt(); assert.equal((await f.verify(f.bundle)).status, "verified"); checks++
|
|
}
|
|
for (const tool of ["dns", "route", "bandwidth", "mtu"] as const) {
|
|
const f = await fixture()
|
|
f.context.tool = tool; f.receipt.tool = tool; f.receipt.data = { kind: tool }; f.anchor.tool = tool
|
|
if (tool === "bandwidth") {
|
|
f.context.destinationServerId = 12; f.receipt.destinationServerId = 12; f.anchor.destination_server_id = 12
|
|
}
|
|
const kinds = tool === "mtu" ? ["source-dns-A", ...[1500,1492,1480,1476,1472,1468,1400,1280,1024,576].map(size => `native-mtu-${size}`)]
|
|
: [tool === "dns" ? "manager-dns-AAAA" : tool === "route" ? "native-route-read" : "native-bandwidth"]
|
|
f.bundle.stages = kinds.map((kind, index) => ({ ...f.stage, stage_index: index + 1, kind }))
|
|
f.receipt.attemptedStages = kinds.length; f.receipt.completedStages = kinds.length
|
|
f.anchor.attempted_stages = kinds.length; f.anchor.completed_stages = kinds.length
|
|
await f.refreshReceipt(); assert.equal((await f.verify(f.bundle)).status, "verified")
|
|
if (tool === "mtu") {
|
|
f.bundle.stages[1].kind = "native-mtu-1492"
|
|
assert.equal((await f.verify(f.bundle)).status, "unresolved")
|
|
}
|
|
checks++
|
|
}
|
|
{
|
|
const f = await fixture()
|
|
f.bundle.stages = [{ ...f.stage, kind: "source-dns-A", outcome: "source-unavailable" }, { ...f.stage, stage_index: 2 }]
|
|
f.receipt.attemptedStages = 2; f.receipt.completedStages = 2; f.anchor.attempted_stages = 2; f.anchor.completed_stages = 2
|
|
await f.refreshReceipt(); assert.equal((await f.verify(f.bundle)).status, "verified"); checks++
|
|
}
|
|
type Fixture = Awaited<ReturnType<typeof fixture>>
|
|
const mutations: Array<(f: Fixture) => void | Promise<void>> = [
|
|
f => { f.bundle.executionId = randomUUID() }, f => { f.bundle.identityKind = "mcp" },
|
|
f => { f.anchor.actor_id = "foreign" }, f => { f.anchor.authentication = "local" },
|
|
f => { f.anchor.mcp_connection_id = randomUUID() }, f => { f.anchor.mcp_credential_id = randomUUID() },
|
|
f => { f.anchor.request_id = randomUUID() }, f => { f.anchor.authority_issued_at = "2" },
|
|
f => { f.anchor.authority_expires_at = new Date("2030-01-01T00:00:00Z") },
|
|
f => { f.anchor.source_server_id = 10 }, f => { f.anchor.destination_server_id = 11 }, f => { f.anchor.tool = "dns" },
|
|
f => { f.anchor.receipt_id = randomUUID() }, f => { f.anchor.binding_proof = "b".repeat(64) },
|
|
f => { f.anchor.receipt_proof = "b".repeat(64) }, f => { f.anchor.receipt_ciphertext = "missing-private" },
|
|
async f => { f.anchor.receipt_proof = await f.crypto.proof("receipt", JSON.parse(f.ciphertexts.get(String(f.anchor.receipt_ciphertext))!)) },
|
|
f => { f.anchor.receipt_ciphertext = "x".repeat(262145) }, f => { f.anchor.finished_at = null },
|
|
f => { f.anchor.created_at = "invalid" }, f => { f.anchor.state = "cancelled" },
|
|
f => { f.anchor.attempted_stages = 2 }, f => { f.anchor.completed_stages = 0 },
|
|
async f => { f.receipt.serverId = 10; await f.refreshReceipt() },
|
|
async f => { f.receipt.automaticReplay = true as false; await f.refreshReceipt() },
|
|
async f => { f.receipt.state = "unknown"; await f.refreshReceipt() },
|
|
async f => { f.receipt.errorCode = "PRIVATE_UNKNOWN"; await f.refreshReceipt() },
|
|
async f => { await f.refreshReceipt({ ...f.context, extra: true }) },
|
|
f => { f.bundle.stages = [] }, f => { f.stage.stage_index = 2 }, f => { f.stage.receipt_id = randomUUID() },
|
|
f => { f.stage.timeout_ms = 120001 }, f => { f.stage.timeout_ms = 0 }, f => { f.stage.kind = "native-route-read" },
|
|
f => { f.stage.outcome = "source-unavailable" }, f => { f.stage.received_at = null },
|
|
f => { f.stage.state = "intent" }, f => { f.bundle.stages.push({ ...f.stage }) },
|
|
]
|
|
for (const mutate of mutations) {
|
|
const f = await fixture(); await mutate(f)
|
|
const result = await f.verify(f.bundle)
|
|
assert.equal(result.status, "unresolved"); assert.equal(JSON.stringify(result).includes(secret), false); checks++
|
|
}
|
|
const sourceMutations: Array<(f: Fixture) => void> = [
|
|
f => { Object.assign(f.source.scope, { ownerId: "foreign" }) }, f => { Object.assign(f.source.scope, { executionId: randomUUID() }) },
|
|
f => { Object.assign(f.source.scope, { bindingProof: "b".repeat(64) }) }, f => { Object.assign(f.source, { originalCredentialId: randomUUID() }) },
|
|
f => { Object.assign(f.source, { serverIds: [10] }) }, f => { Object.assign(f.source, { stageBudget: 1 }) },
|
|
f => { Object.assign(f.source.scope, { authorityExpiresAt: "2030-01-01T00:00:00Z" }) },
|
|
]
|
|
for (const mutate of sourceMutations) {
|
|
const f = await fixture("mcp"); mutate(f)
|
|
assert.equal((await f.verify(f.bundle)).status, "unresolved"); checks++
|
|
}
|
|
for (const mutate of [
|
|
(row: Record<string, unknown>) => { row.identity_proof = "b".repeat(64) },
|
|
(row: Record<string, unknown>) => { row.binding_proof = "b".repeat(64) },
|
|
(row: Record<string, unknown>) => { row.execution_id = randomUUID() },
|
|
(row: Record<string, unknown>) => { row.identity_kind = "rest" },
|
|
(row: Record<string, unknown>) => { row.attempted_stages = 12 },
|
|
(row: Record<string, unknown>) => { row.completed_stages = 1 },
|
|
]) {
|
|
const f = await fixture("mcp", false); await f.quarantine(); mutate(f.bundle.quarantine!)
|
|
assert.equal((await f.verify(f.bundle)).status, "unresolved"); checks++
|
|
}
|
|
for (const field of ["operation_id", "connection_id", "actor_id"]) {
|
|
const f = await fixture("mcp", false), id = f.context.identity; assert(id.kind === "mcp")
|
|
f.bundle.cancellation = { operation_id: id.executionId, connection_id: id.connectionId, actor_id: id.ownerId, [field]: randomUUID() }
|
|
assert.equal((await f.verify(f.bundle)).status, "unresolved"); checks++
|
|
}
|
|
{
|
|
const f = await fixture("mcp"), verify = createLegacyServerProbeVerifier({ loadMcpSource: async () => { throw Error(secret) } })
|
|
const result = await verify(f.bundle); assert.equal(result.status, "unresolved"); assert.equal(JSON.stringify(result).includes(secret), false); checks++
|
|
}
|
|
{
|
|
const f = await fixture(), verify = createLegacyServerProbeVerifierForTests(f.deps, { ...f.crypto, proof: async () => { throw Error(secret) } })
|
|
const result = await verify(f.bundle); assert.equal(result.status, "unresolved"); assert.equal(JSON.stringify(result).includes(secret), false); checks++
|
|
}
|
|
{
|
|
const state = await mkdtemp(join(tmpdir(), "mm-legacy-verifier-test-"))
|
|
const previousState = process.env.MCP_STATE_DIR
|
|
process.env.MCP_STATE_DIR = state
|
|
try {
|
|
const receipt = await fixture()
|
|
const quarantine = await fixture("rest", false)
|
|
await quarantine.quarantine()
|
|
const verify = createLegacyServerProbeVerifier(receipt.deps)
|
|
for (const bundle of [receipt.bundle, quarantine.bundle]) {
|
|
assert.equal((await verify(bundle)).status, "unresolved")
|
|
assert.deepEqual(await readdir(state), [], "Historical reads must leave missing encryption state untouched")
|
|
checks++
|
|
}
|
|
} finally {
|
|
if (previousState === undefined) delete process.env.MCP_STATE_DIR
|
|
else process.env.MCP_STATE_DIR = previousState
|
|
await rm(join(state, "encryption-key"), { force: true })
|
|
await rmdir(state)
|
|
}
|
|
}
|
|
console.log(`server-probe-legacy-verifier: ${checks} historical authentication checks passed; migration readiness always false`)
|