325 lines
26 KiB
TypeScript
325 lines
26 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import { createHash, createHmac, randomUUID } from "node:crypto"
|
|
import { spawn } from "node:child_process"
|
|
import { createServer } from "node:net"
|
|
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
import { resolve, isAbsolute } from "node:path"
|
|
import { SignJWT } from "jose"
|
|
import { writeFile } from "node:fs/promises"
|
|
assert.equal(process.env.OWN_EXTERNAL_PROCESS_PG, "true"); assert.equal(process.env.NODE_ENV, "test")
|
|
assert(process.env.OWN_EXTERNAL_SOURCE_ROOT && isAbsolute(process.env.OWN_EXTERNAL_SOURCE_ROOT))
|
|
assert(process.env.OWN_EXTERNAL_RESULT_FILE && isAbsolute(process.env.OWN_EXTERNAL_RESULT_FILE))
|
|
const source = (path: string) => pathToFileURL(resolve(process.env.OWN_EXTERNAL_SOURCE_ROOT!, "backend/src", path)).href
|
|
const database = new URL(process.env.DATABASE_URL ?? "")
|
|
assert.equal(database.hostname, "127.0.0.1"); assert.equal(database.username, "mcp_test"); assert.equal(database.pathname, "/postgres"); assert.notEqual(database.port, "5432")
|
|
const listener = createServer(); await new Promise<void>(r => listener.listen(0, "127.0.0.1", r)); const port = (listener.address() as { port: number }).port
|
|
await new Promise<void>(r => listener.close(() => r()))
|
|
const origin = `http://127.0.0.1:${port}`, endpoint = origin + "/mcp"
|
|
process.env.MCP_PUBLIC_URL = endpoint; process.env.MCP_UI_URL = origin; process.env.CORS_ORIGIN = origin
|
|
const { pool, closePool } = await import(source("db/index.js"))
|
|
const { applySqlMigrations } = await import(source("db/migrate.js"))
|
|
const { mcpConnectionCreateSchema } = await import("@mmapp/contracts/mcp")
|
|
const { createMcpConnection, rotateConnectionToken, setConnectionStatus, updateMcpSettings } = await import(source("modules/mcp/store.js"))
|
|
const { listActions } = await import(source("modules/mcp/actions.js")) as typeof import("../../src/modules/mcp/actions.js")
|
|
const { env } = await import(source("config.js"))
|
|
const { buildApp } = await import(source("index.js"))
|
|
const actor = { id: "owned-external-process-owner", email: "[email protected]", name: "Owned external SDK fixture", apps: ["mm"],
|
|
permissions: ["mm:settings:admin", "mm:mcp:admin", "mm:servers:admin"], expiresAt: Date.now() + 3600000, issuedAt: Date.now() - 1000 }
|
|
let currentId: string | undefined, app: Awaited<ReturnType<typeof buildApp>> | undefined, child: ReturnType<typeof spawn> | undefined
|
|
const credentials: string[] = []
|
|
const bridgePids = new Set<number>()
|
|
let childExit: Promise<number> | undefined
|
|
let releaseLogRead: (() => void) | undefined
|
|
let logReadCompleted: Promise<void> | undefined
|
|
let revocationOwnerId: string | undefined
|
|
const revokedLogRequests: { connectionId: string; tool: string }[] = []
|
|
const originalConnect = pool.connect
|
|
const delay = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))
|
|
async function deliverPortalRevocation(ownerId: string) {
|
|
const secret = randomUUID() + randomUUID()
|
|
process.env.MCP_PORTAL_WEBHOOK_SECRET = secret
|
|
const occurredAt = Date.now(), timestamp = String(Math.floor(occurredAt / 1000))
|
|
const eventId = randomUUID(), body = JSON.stringify({ version: 2, eventId, userId: ownerId, type: "permissions.changed", eventOccurredAt: occurredAt })
|
|
const signature = "sha256=" + createHmac("sha256", secret).update(timestamp + "." + body).digest("hex")
|
|
const response = await fetch(origin + "/_internal/mcp/portal/revocations", {
|
|
method: "POST", headers: { "content-type": "application/json", "x-mm-timestamp": timestamp, "x-mm-signature": signature },
|
|
body, signal: AbortSignal.timeout(5000),
|
|
})
|
|
assert.equal(response.status, 200)
|
|
assert.deepEqual(await response.json(), { ok: true, duplicate: false })
|
|
assert.equal((await pool.query("SELECT count(*)::int AS count FROM mcp_portal_events WHERE id=$1", [eventId])).rows[0].count, 1)
|
|
return occurredAt
|
|
}
|
|
function alive(pid: number) { try { process.kill(pid, 0); return true } catch (error) { if ((error as NodeJS.ErrnoException).code === "ESRCH") return false; throw error } }
|
|
async function drainChildAndBridges() {
|
|
if (child && child.exitCode === null && child.signalCode === null) { child.kill(); await Promise.race([childExit, delay(5000).then(() => { throw Error("Owned external child failed to exit") })]) }
|
|
const deadline = Date.now() + 5000
|
|
while ([...bridgePids].some(alive) && Date.now() < deadline) await delay(25)
|
|
const remaining = [...bridgePids].filter(alive)
|
|
for (const pid of remaining) process.kill(pid)
|
|
const killDeadline = Date.now() + 5000
|
|
while (remaining.some(alive) && Date.now() < killDeadline) await delay(25)
|
|
assert(!remaining.some(alive), "Owned SDK bridge grandchild failed to exit")
|
|
console.log("Owned client and all observed production bridge processes exited:", JSON.stringify({ childPid: child?.pid, exitCode: child?.exitCode, signalCode: child?.signalCode, bridgePids: [...bridgePids] }))
|
|
}
|
|
try {
|
|
await applySqlMigrations(pool)
|
|
for (const table of ["traffic_settings", "uptime_settings", "traffic_flow_settings", "servers_api_ping_settings", "certificate_renew_settings", "backup_schedule_settings", "geoip_settings", "internet_path_settings", "router_log_settings"]) await pool.query(`UPDATE ${table} SET enabled=false WHERE id=1`)
|
|
const secret = "OWNED_DEVICE_CREDENTIAL_MUST_NOT_ESCAPE"
|
|
const allowed = (await pool.query("INSERT INTO servers(name,host,enabled,password) VALUES('owned allowed','192.0.2.1',false,$1) RETURNING id", [secret])).rows[0].id
|
|
const forbidden = (await pool.query("INSERT INTO servers(name,host,enabled) VALUES('owned forbidden','192.0.2.2',false) RETURNING id")).rows[0].id
|
|
await pool.query(`INSERT INTO router_log_entries(server_id,server_name,host,router_id,raw_time,received_at,effective_at,topics,level,message,generation)
|
|
SELECT id,name,host,'fixture','00:00:01',now(),now()-n*interval '1 second',ARRAY['system'],'info','external-sdk-log-'||n,0
|
|
FROM servers CROSS JOIN generate_series(1,2) AS n WHERE id=ANY($1::int[])`, [[allowed, forbidden]])
|
|
await updateMcpSettings({ enabled: true }, actor)
|
|
app = await buildApp({ startScheduler: false, logger: false }); await app.listen({ port, host: "127.0.0.1" })
|
|
const jwt = await new SignJWT({ sub: actor.id, apps: actor.apps, permissions: actor.permissions }).setProtectedHeader({ alg: "HS256" }).setIssuer(env.authIssuer).setIssuedAt().setExpirationTime("1h").sign(new TextEncoder().encode(env.jwtSecret))
|
|
const artifactOwner = { ...actor, id: actor.id + "-artifact" }
|
|
const signArtifactOwner = () => new SignJWT({ sub: artifactOwner.id, apps: artifactOwner.apps, permissions: artifactOwner.permissions })
|
|
.setProtectedHeader({ alg: "HS256" }).setIssuer(env.authIssuer).setIssuedAt().setExpirationTime("1h").sign(new TextEncoder().encode(env.jwtSecret))
|
|
let artifactConnectionId: string | undefined
|
|
const planOwner = { ...actor, id: actor.id + "-approved-plan", permissions: [...actor.permissions, "mm:mcp:approve"] }
|
|
let planConnectionId: string | undefined
|
|
const portalHeaders = { authorization: `Bearer ${jwt}`, "content-type": "application/json" }
|
|
assert.equal((await pool.query("SELECT count(*)::int AS count FROM mcp_connections WHERE owner_id=$1", [actor.id])).rows[0].count, 0)
|
|
const callback = "http://localhost:45678/callback?registered=1"
|
|
const registration = await fetch(origin + "/api/mcp/oauth/clients", { method: "POST", headers: portalHeaders,
|
|
body: JSON.stringify({ name: "Owned denial fixture", redirectUris: [callback] }) })
|
|
assert.equal(registration.status, 200)
|
|
const { clientId } = await registration.json() as { clientId: string }
|
|
const consent = (payload: Record<string, unknown>, headers = portalHeaders) => fetch(origin + "/api/mcp/oauth/consent", { method: "POST", headers, body: JSON.stringify(payload) })
|
|
const authorize = async (state: string) => {
|
|
const parameters = new URLSearchParams({ client_id: clientId, redirect_uri: callback, response_type: "code", resource: endpoint,
|
|
code_challenge_method: "S256", code_challenge: createHash("sha256").update("v".repeat(64)).digest("base64url"), state })
|
|
const response = await fetch(`${origin}/oauth/authorize?${parameters}`, { redirect: "manual" })
|
|
assert.equal(response.status, 302)
|
|
const requestId = new URL(response.headers.get("location")!).searchParams.get("oauthRequest")
|
|
assert.ok(requestId)
|
|
return requestId
|
|
}
|
|
const requestId = await authorize("deny-without-connection")
|
|
assert.equal((await consent({ requestId, approve: false }, { ...portalHeaders, authorization: "" })).status, 401)
|
|
assert.equal((await consent({ requestId, approve: true })).status, 400)
|
|
const refusals = await Promise.all([consent({ requestId, approve: false }), consent({ requestId, approve: false })])
|
|
assert.deepEqual(refusals.map(response => response.status).sort(), [200, 404])
|
|
const { redirectUrl } = await refusals.find(response => response.status === 200)!.json() as { redirectUrl: string }
|
|
const redirect = new URL(redirectUrl)
|
|
const expected = new URL(callback)
|
|
expected.searchParams.set("state", "deny-without-connection")
|
|
expected.searchParams.set("iss", origin)
|
|
expected.searchParams.set("error", "access_denied")
|
|
assert.equal(redirect.href, expected.href)
|
|
const otherOwner = { ...actor, id: "owned-external-process-other-owner" }
|
|
const unrelated = await createMcpConnection(mcpConnectionCreateSchema.parse({ name: "Unrelated paused connection", mode: "observer",
|
|
expiresAt: new Date(Date.now() + 600000).toISOString(), permissions: ["mm:servers:read"],
|
|
scope: { serverIds: [Number(allowed)], operationIds: ["servers.list"], allowGlobal: false } }), otherOwner)
|
|
await setConnectionStatus(unrelated.connection.id, "paused", otherOwner)
|
|
const legacyRequestId = await authorize("legacy-denial")
|
|
const legacy = await consent({ requestId: legacyRequestId, connectionId: unrelated.connection.id, approve: false })
|
|
assert.equal(legacy.status, 200)
|
|
assert.equal((await consent({ requestId: legacyRequestId, connectionId: unrelated.connection.id, approve: false })).status, 404)
|
|
const expired = await createMcpConnection(mcpConnectionCreateSchema.parse({ name: "Expired connection", mode: "observer",
|
|
expiresAt: new Date(Date.now() + 600000).toISOString(), permissions: ["mm:servers:read"],
|
|
scope: { serverIds: [Number(allowed)], operationIds: ["servers.list"], allowGlobal: false } }), actor)
|
|
await pool.query("UPDATE mcp_connections SET policy=jsonb_set(policy,'{expiresAt}',to_jsonb($2::text)) WHERE id=$1", [expired.connection.id, "2000-01-01T00:00:00Z"])
|
|
const expiredRequestId = await authorize("expired-approval")
|
|
assert.equal((await consent({ requestId: expiredRequestId, connectionId: expired.connection.id, approve: true })).status, 403)
|
|
const expiredRequest = (await pool.query("SELECT consumed_at,code_digest,connection_id FROM mcp_oauth_requests WHERE id=$1", [expiredRequestId])).rows[0]
|
|
assert.deepEqual(expiredRequest, { consumed_at: null, code_digest: null, connection_id: null })
|
|
assert.equal((await consent({ requestId: expiredRequestId, approve: false })).status, 200)
|
|
const deniedRows = (await pool.query("SELECT consumed_at,code_digest,connection_id FROM mcp_oauth_requests WHERE client_id=$1", [clientId])).rows
|
|
assert.equal(deniedRows.length, 3)
|
|
assert(deniedRows.every(row => row.consumed_at instanceof Date && row.code_digest === null && row.connection_id === null))
|
|
assert.equal((await pool.query("SELECT count(*)::int AS count FROM mcp_credentials WHERE client_id=$1", [clientId])).rows[0].count, 0)
|
|
const denialAudit = (await pool.query("SELECT actor_id,connection_id,payload FROM mcp_audit WHERE action='oauth.consent' AND payload->>'clientId'=$1", [clientId])).rows
|
|
assert.equal(denialAudit.length, 3)
|
|
assert(denialAudit.every(row => row.actor_id === actor.id && row.connection_id === null && row.payload.approved === false))
|
|
console.log("OAuth consent: expired approval refused before code issuance, denial uses exact redirect and one-use request without credentials")
|
|
const transcript: unknown[] = []; let failure: unknown, result: unknown
|
|
child = spawn(process.execPath, [fileURLToPath(new URL("./client.mjs", import.meta.url))], { cwd: process.cwd(), windowsHide: true, stdio: ["ignore", "pipe", "pipe", "ipc"],
|
|
env: { ...Object.fromEntries(["SystemRoot", "WINDIR", "TEMP", "TMP", "PATH", "Path"].filter(k => process.env[k]).map(k => [k, process.env[k]!])),
|
|
OWN_EXTERNAL_CLIENT: "true", OWN_MCP_ENDPOINT: endpoint, OWN_SERVER_PID: String(process.pid), OWN_ALLOWED_SERVER: String(allowed), OWN_FORBIDDEN_SERVER: String(forbidden),
|
|
OWN_BRIDGE_FILE: fileURLToPath(new URL(source("scripts/mcp-stdio.ts"))), OWN_PORTAL_JWT: jwt, OWN_SERVER_SECRET: secret, OWN_EXTERNAL_CLEANUP_FAULT: process.env.OWN_EXTERNAL_CLEANUP_FAULT } })
|
|
childExit = new Promise<number>(resolve => { child!.once("exit", status => resolve(status ?? 1)); child!.once("error", () => resolve(1)) })
|
|
let stderr = "", stdout = ""
|
|
child.stderr?.on("data", chunk => { stderr += String(chunk); if (stderr.length > 1048576) child?.kill() })
|
|
child.stdout?.on("data", chunk => { stdout += String(chunk); if (stdout.length > 1048576) child?.kill() })
|
|
child.on("message", async (raw: unknown) => {
|
|
const message = raw as { kind: string; command?: string; id?: number; pid?: number; result?: unknown; phase?: string; errorType?: string }
|
|
if (message.kind === "bridge-pid") { assert(Number.isSafeInteger(message.pid) && message.pid! > 0 && message.pid !== process.pid && message.pid !== child?.pid); bridgePids.add(message.pid!); return }
|
|
if (message.kind === "arm-cleanup-timeout" && process.env.OWN_EXTERNAL_CLEANUP_FAULT === "timeout") { setTimeout(() => { failure = { phase: "owned-deliberate-cleanup-timeout" }; child?.kill() }, 25); return }
|
|
if (message.kind === "result") { result = message.result; return }
|
|
if (message.kind === "failure") { failure = { phase: message.phase, errorType: message.errorType }; return }
|
|
if (message.kind !== "control" || !Number.isSafeInteger(message.id)) return
|
|
try {
|
|
let value: unknown
|
|
if (message.command === "new-revocable-plan-connection") {
|
|
assert(!planConnectionId)
|
|
const issued = await createMcpConnection(mcpConnectionCreateSchema.parse({
|
|
name: "Owned approved plan revocation", mode: "approval", permissions: ["mm:settings:admin"],
|
|
expiresAt: new Date(Date.now() + 600000).toISOString(),
|
|
scope: { serverIds: [Number(allowed), Number(forbidden)], operationIds: ["logs.settings.update", "changes.apply"], allowGlobal: true },
|
|
}), planOwner)
|
|
currentId = planConnectionId = issued.connection.id
|
|
const ownerJwt = await new SignJWT({ sub: planOwner.id, apps: planOwner.apps, permissions: planOwner.permissions })
|
|
.setProtectedHeader({ alg: "HS256" }).setIssuer(env.authIssuer).setIssuedAt().setExpirationTime("1h").sign(new TextEncoder().encode(env.jwtSecret))
|
|
credentials.push(issued.token, ownerJwt)
|
|
value = { token: issued.token, ownerJwt }
|
|
} else if (message.command === "revoke-approved-plan-owner") {
|
|
assert(planConnectionId && currentId === planConnectionId)
|
|
const plans = (await pool.query("SELECT id,state,approved_by FROM mcp_plans WHERE connection_id=$1", [planConnectionId])).rows
|
|
assert.equal(plans.length, 1)
|
|
assert.equal(plans[0].state, "approved")
|
|
assert.equal(plans[0].approved_by, planOwner.id)
|
|
await deliverPortalRevocation(planOwner.id)
|
|
value = { ok: true }
|
|
} else if (message.command === "new-artifact-connection") {
|
|
assert(!artifactConnectionId)
|
|
const issued = await createMcpConnection(mcpConnectionCreateSchema.parse({
|
|
name: "Owned export revocation", mode: "observer", permissions: ["mm:servers:read"],
|
|
expiresAt: new Date(Date.now() + 600000).toISOString(),
|
|
scope: { serverIds: [Number(allowed)], operationIds: ["logs.export"], allowGlobal: false },
|
|
}), artifactOwner)
|
|
currentId = artifactConnectionId = issued.connection.id
|
|
const ownerJwt = await signArtifactOwner()
|
|
credentials.push(issued.token, ownerJwt)
|
|
value = { token: issued.token, ownerJwt }
|
|
} else if (message.command === "revoke-artifact-owner") {
|
|
assert(artifactConnectionId && currentId === artifactConnectionId)
|
|
const occurredAt = await deliverPortalRevocation(artifactOwner.id)
|
|
while (Math.floor(Date.now() / 1000) <= Math.floor(occurredAt / 1000)) await delay(25)
|
|
const ownerJwt = await signArtifactOwner()
|
|
credentials.push(ownerJwt)
|
|
value = { ownerJwt }
|
|
} else if (["arm-log-revocation", "arm-log-revocation-after-commit", "arm-log-follow-revocation"].includes(message.command ?? "")) {
|
|
assert(!releaseLogRead)
|
|
const afterCommit = message.command === "arm-log-revocation-after-commit"
|
|
const follow = message.command === "arm-log-follow-revocation"
|
|
const owner = { ...actor, id: `${actor.id}-${follow ? "follow" : afterCommit ? "postcommit" : "precommit"}` }
|
|
revocationOwnerId = owner.id
|
|
if (follow) {
|
|
await pool.query("UPDATE servers SET enabled=true WHERE id=$1", [allowed])
|
|
await pool.query("UPDATE router_log_settings SET enabled=true WHERE id=1")
|
|
}
|
|
const issued = await createMcpConnection(mcpConnectionCreateSchema.parse({
|
|
name: "Owned portal revocation during log read", mode: "observer", permissions: ["mm:servers:read"],
|
|
expiresAt: new Date(Date.now() + 600000).toISOString(),
|
|
scope: { serverIds: [Number(allowed)], operationIds: [follow ? "logs.follow" : "logs.query"], allowGlobal: false },
|
|
}), owner)
|
|
currentId = issued.connection.id
|
|
revokedLogRequests.push({ connectionId: currentId, tool: follow ? "mm_logs_follow" : "mm_logs_query" })
|
|
credentials.push(issued.token)
|
|
let completed!: () => void
|
|
logReadCompleted = new Promise<void>(resolve => { completed = resolve })
|
|
const hold = new Promise<void>(resolve => { releaseLogRead = resolve })
|
|
pool.connect = ((callback?: unknown) => {
|
|
if (typeof callback === "function") return Reflect.apply(originalConnect, pool, [callback])
|
|
return (async () => {
|
|
const client = await originalConnect.call(pool)
|
|
const query = client.query, release = client.release
|
|
let completingLog = false
|
|
client.query = (async (...args: unknown[]) => {
|
|
const params = args[1]
|
|
if (typeof args[0] === "string" && args[0].startsWith("UPDATE mcp_requests SET status=$2,result=")
|
|
&& Array.isArray(params) && params[1] === "completed" && String(params[2]).includes(follow ? "external-sdk-live-event" : "external-sdk-log-1")) {
|
|
completingLog = true
|
|
if (!afterCommit) { completed(); await hold }
|
|
}
|
|
const result = await Reflect.apply(query, client, args)
|
|
if (afterCommit && completingLog && args[0] === "COMMIT") { completed(); await hold }
|
|
return result
|
|
}) as typeof query
|
|
client.release = error => {
|
|
client.query = query
|
|
client.release = release
|
|
release(error)
|
|
}
|
|
return client
|
|
})()
|
|
}) as typeof originalConnect
|
|
value = { token: issued.token }
|
|
} else if (message.command === "publish-follow-fixture-row") {
|
|
assert(releaseLogRead && currentId && revokedLogRequests.at(-1)?.tool === "mm_logs_follow")
|
|
const deadline = Date.now() + 3000
|
|
for (;;) {
|
|
const live = await pool.query("SELECT count(*)::int AS count FROM mcp_read_leases WHERE connection_id=$1", [currentId])
|
|
if (live.rows[0].count === 1) break
|
|
assert(Date.now() < deadline, "Owned follow did not acquire a live read lease")
|
|
await delay(25)
|
|
}
|
|
await pool.query(`INSERT INTO router_log_entries(server_id,server_name,host,router_id,raw_time,received_at,effective_at,topics,level,message,generation)
|
|
SELECT id,name,host,'fixture','00:00:01',now(),now(),ARRAY['system'],'info','external-sdk-live-event',0
|
|
FROM servers WHERE id=$1`, [allowed])
|
|
value = { ok: true }
|
|
} else if (message.command === "deliver-log-revocation") {
|
|
assert(logReadCompleted && releaseLogRead && currentId && revocationOwnerId)
|
|
try {
|
|
await Promise.race([logReadCompleted, delay(5000).then(() => { throw Error("Owned log read did not reach the barrier") })])
|
|
await deliverPortalRevocation(revocationOwnerId)
|
|
value = { ok: true }
|
|
} finally {
|
|
pool.connect = originalConnect
|
|
releaseLogRead()
|
|
releaseLogRead = undefined
|
|
logReadCompleted = undefined
|
|
revocationOwnerId = undefined
|
|
}
|
|
} else if (message.command === "new-connection") {
|
|
const issued = await createMcpConnection(mcpConnectionCreateSchema.parse({ name: "Owned external SDK process", mode: "observer", expiresAt: new Date(Date.now() + 1200000).toISOString(),
|
|
permissions: ["mm:servers:read"], scope: { serverIds: [Number(allowed)], operationIds: ["servers.list"], allowGlobal: false }, limits: { callsPerMinute: 200 } }), actor)
|
|
currentId = issued.connection.id; credentials.push(issued.token); value = { token: issued.token, connectionId: currentId }
|
|
} else if (message.command === "discovery-connection") {
|
|
const operations = listActions().map(action => action.operation)
|
|
const permissions = [...new Set(operations.flatMap(operation => operation.requiredPermissions))]
|
|
const issued = await createMcpConnection(mcpConnectionCreateSchema.parse({
|
|
name: "Owned full catalogue discovery", mode: "approval", expiresAt: new Date(Date.now() + 600000).toISOString(),
|
|
permissions, scope: { serverIds: [Number(allowed)], operationIds: operations.map(operation => operation.id), allowGlobal: true },
|
|
}), { ...actor, permissions: [...new Set([...actor.permissions, ...permissions])] })
|
|
currentId = issued.connection.id
|
|
credentials.push(issued.token)
|
|
value = { token: issued.token, names: operations.map(operation => operation.toolName) }
|
|
} else if (message.command === "large-result" || message.command === "reset-result") {
|
|
await pool.query("UPDATE servers SET comment=$2 WHERE id=$1", [allowed, message.command === "large-result" ? "Z".repeat(40000) + "OWNED_TRUNCATION_TAIL" : ""])
|
|
value = { ok: true }
|
|
} else {
|
|
assert(currentId)
|
|
if (message.command === "rotate") { const rotated = await rotateConnectionToken(currentId, actor); credentials.push(rotated.token); value = rotated }
|
|
else if (message.command === "pause" || message.command === "resume" || message.command === "revoke") { await setConnectionStatus(currentId, message.command === "pause" ? "paused" : message.command === "resume" ? "active" : "revoked", actor); value = { ok: true } }
|
|
else throw Error("Unsupported exact fixture control")
|
|
}
|
|
transcript.push({ command: message.command, connectionId: currentId })
|
|
child?.send({ kind: "reply", id: message.id, value })
|
|
} catch { child?.send({ kind: "reply", id: message.id, error: true }) }
|
|
})
|
|
const timer = setTimeout(() => { failure = { phase: "whole-external-client-deadline" }; child?.kill() }, 120000)
|
|
const code = await childExit; clearTimeout(timer)
|
|
if (code !== 0 || !result) { console.log("OWNED_EXTERNAL_PROCESS_FAILURE", JSON.stringify({ code, failure, stdout: stdout.slice(0,1000), stderr: stderr.slice(0,1000) })); throw Error("External SDK process did not complete") }
|
|
const logs = JSON.stringify((await pool.query("SELECT tool,input,result,error_code FROM mcp_requests ORDER BY created_at")).rows)
|
|
assert.equal(revokedLogRequests.length, 3)
|
|
assert.ok(planConnectionId)
|
|
const invalidated = (await pool.query("SELECT state FROM mcp_plans WHERE connection_id=$1", [planConnectionId])).rows
|
|
assert.deepEqual(invalidated, [{ state: "invalidated" }])
|
|
assert.equal((await pool.query("SELECT count(*)::int AS count FROM mcp_operations WHERE connection_id=$1", [planConnectionId])).rows[0].count, 0)
|
|
assert.equal((await pool.query("SELECT enabled FROM router_log_settings WHERE id=1")).rows[0].enabled, true)
|
|
assert.equal((await pool.query("SELECT count(*)::int AS count FROM mcp_artifacts WHERE connection_id=$1 AND expires_at>now()", [artifactConnectionId])).rows[0].count, 1)
|
|
for (const { connectionId, tool } of revokedLogRequests) {
|
|
const revokedRead = (await pool.query("SELECT status,error_code,result FROM mcp_requests WHERE connection_id=$1 AND tool=$2 AND status='error'", [connectionId, tool])).rows
|
|
assert.equal(revokedRead.length, 1)
|
|
assert.equal(revokedRead[0].status, "error")
|
|
assert.deepEqual(Object.keys(revokedRead[0].result), ["message"])
|
|
assert.doesNotMatch(JSON.stringify(revokedRead[0].result), /external-sdk-log-1|external-sdk-live-event|"entries"|"nextCursor"|"cursor"/)
|
|
assert.ok(["OWNER_CLAIMS_REVOKED", "UNAUTHORIZED"].includes(revokedRead[0].error_code))
|
|
assert.equal((await pool.query("SELECT count(*)::int AS count FROM mcp_read_leases WHERE connection_id=$1", [connectionId])).rows[0].count, 0)
|
|
}
|
|
for (const token of [...credentials, jwt, secret]) { assert(!logs.includes(token)); assert(!stdout.includes(token)); assert(!stderr.includes(token)) }
|
|
const final = { ...(result as object), actualOwnPg: true, actualProductionHttpServer: true, serverFixturePid: process.pid,
|
|
controlTransitions: transcript, auditCredentialLeakAbsent: true, denialWithoutConnection: true,
|
|
legacyDenialIgnoresPausedForeignConnection: true, nativeRouterCalls: 0, productionDatabaseTouched: false }
|
|
await writeFile(process.env.OWN_EXTERNAL_RESULT_FILE!, JSON.stringify(final, null, 2))
|
|
console.log("Owned external process SDK result:", JSON.stringify(final))
|
|
} finally {
|
|
pool.connect = originalConnect
|
|
releaseLogRead?.()
|
|
try { await drainChildAndBridges() } finally { try { if (app) await app.close() } finally { await closePool() } }
|
|
}
|