import assert from "node:assert/strict"; import { generateKeyPairSync, randomBytes } from "node:crypto"; import { mkdtemp, readFile, writeFile, rm } from "node:fs/promises"; import { createServer } from "node:net"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { test } from "node:test"; import { parse } from "jsonc-parser"; import { unstable_dev } from "wrangler"; import { oauthBindings, oauthConfig } from "./fixtures/oauth-config.mjs"; test( "native domain Workflow reconciles lost replies, checks HTTPS, and revokes before removal", { timeout: 180_000 }, async (t) => { const temporary = await mkdtemp( join(tmpdir(), "flarebot-domain-workflow-"), ); const server = createServer(); await new Promise((done) => server.listen(0, "127.0.0.1", done)); const port = server.address().port; await new Promise((done) => server.close(done)); const origin = `http://127.0.0.1:${port}`; // Production routes make Miniflare rewrite the localhost request origin. const { routes: _productionRoutes, workers_dev: _productionWorkersDev, ...base } = parse(await readFile("wrangler.control-plane.jsonc", "utf8")); const configPath = join(temporary, "wrangler.json"); await writeFile( configPath, JSON.stringify({ ...base, name: "flarebot-domain-flow-test", main: resolve("tests/fixtures/domain-workflow-worker.ts"), assets: undefined, durable_objects: { bindings: [ ...base.durable_objects.bindings, { name: "DOMAIN_PROVIDER", class_name: "DomainProvider" }, ], }, exports: { ...base.exports, DomainProvider: { type: "durable-object", storage: "sqlite" }, }, }), ); const keys = generateKeyPairSync("ed25519"); const config = oauthConfig(origin); config.bridge = { keyId: "fixture", publicKey: keys.publicKey.export({ format: "jwk" }).x, }; config.oauthCapabilities.scopes[2].capabilities.push( "domain-zones", "domain-routing", ); const worker = await unstable_dev( "tests/fixtures/domain-workflow-worker.ts", { config: configPath, vars: { ...oauthBindings(origin), FLAREBOT_CONTROL_PLANE: JSON.stringify(config), FLAREBOT_BRIDGE_SIGNING_KEY: keys.privateKey .export({ format: "der", type: "pkcs8" }) .toString("base64url"), }, local: true, ip: "127.0.0.1", port, inspectorPort: 0, persistTo: temporary, logLevel: "error", experimental: { disableExperimentalWarning: true, watch: false }, }, ); t.after(async () => { await worker.stop(); await rm(temporary, { recursive: true, force: true }); }); const post = (path, body) => fetch(origin + path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const id = randomBytes(16).toString("hex"); const auth = await (await post("/fixture/seed", { id })).json(); const endpoint = `/api/installations/${id}/domain`; const form = (suffix, values, cookie = auth.cookie, csrf = origin) => fetch(origin + endpoint + suffix, { method: "POST", headers: { Cookie: cookie, Origin: csrf, "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams(values), }); const read = () => fetch(origin + endpoint, { headers: { Cookie: auth.cookie } }); const wait = async (status) => { let domain; for (let n = 0; n < 160; n++) { domain = (await (await read()).json()).domain; if (domain?.status === status) return domain; if (domain?.status === "failed" && status !== "failed") assert.fail(JSON.stringify(domain)); await new Promise((done) => setTimeout(done, 250)); } assert.fail(`Expected ${status}, got ${JSON.stringify(domain)}`); }; assert.equal( (await form("", {}, auth.cookie, "https://attacker.example")).status, 403, ); assert.equal((await fetch(origin + endpoint)).status, 401); const other = await ( await post("/fixture/seed", { id: "f".repeat(32), owner: "other-owner" }) ).json(); assert.equal( (await fetch(origin + endpoint, { headers: { Cookie: other.cookie } })) .status, 404, ); await post("/fixture/provider", { losePut: true }); const request = { requestId: randomBytes(16).toString("hex"), zoneId: "c".repeat(32), hostname: "bot.example.com", }; const replies = await Promise.all([form("", request), form("", request)]); const bodies = await Promise.all(replies.map((r) => r.json())); assert.deepEqual( replies.map((r) => r.status), [202, 202], JSON.stringify(bodies), ); assert.equal(bodies[0].domain.operationId, bodies[1].domain.operationId); assert.equal(bodies[0].domain.deadline, bodies[1].domain.deadline); const active = await wait("active"); assert.equal(active.origin, "https://bot.example.com"); let provider = await (await fetch(origin + "/fixture/provider")).json(); assert.equal( provider.trace.filter((row) => row.method === "PUT").length, 1, ); assert.ok(provider.trace.some((row) => row.path === "/auth/domain-health")); assert.equal(JSON.stringify(active).includes("test-token"), false); const removalRequest = { requestId: randomBytes(16).toString("hex") }; assert.equal((await form("/remove", removalRequest)).status, 202); const removed = await wait("removed"); assert.equal(removed.origin, null); provider = await (await fetch(origin + "/fixture/provider")).json(); assert.equal(provider.domain, null); assert.equal(provider.origin, null); const deletion = provider.trace.findIndex((row) => row.method === "DELETE"); assert.equal( provider.trace[deletion - 2]?.path === "/auth/domain-configuration" || provider.trace .slice(0, deletion) .some((row) => row.path === "/auth/domain-configuration"), true, ); assert.equal( (await form("/remove", removalRequest)).status, 200, "completed removal is replayable", ); await post("/fixture/provider", { conflict: true }); const nextRequest = { ...request, requestId: randomBytes(16).toString("hex"), }; assert.equal((await form("", nextRequest)).status, 202); assert.equal((await wait("failed")).errorCode, "resource_conflict"); await post("/fixture/provider", { conflict: false }); assert.equal( (await form("/retry", { requestId: randomBytes(16).toString("hex") })) .status, 202, ); await wait("active"); assert.equal( (await form("/remove", { requestId: randomBytes(16).toString("hex") })) .status, 202, ); await wait("removed"); await post("/fixture/provider", { delayPut: true }); assert.equal( ( await form("", { ...request, requestId: randomBytes(16).toString("hex"), }) ).status, 202, ); const unknown = await wait("failed"); assert.equal(unknown.errorCode, "attachment_outcome_unknown"); assert.equal( (await form("/remove", { requestId: randomBytes(16).toString("hex") })) .status, 409, ); assert.equal( (await (await read()).json()).domain.operationId, unknown.operationId, "rejected cancellation cannot supersede unresolved attachment", ); const countPuts = async () => (await (await fetch(origin + "/fixture/provider")).json()).trace.filter( (row) => row.method === "PUT", ).length; const beforeRetry = await countPuts(); assert.equal( (await form("/retry", { requestId: randomBytes(16).toString("hex") })) .status, 202, ); assert.equal( (await wait("failed")).errorCode, "attachment_outcome_unknown", ); assert.equal( await countPuts(), beforeRetry, "absence cannot authorize a second PUT while the first may still commit", ); await post("/fixture/provider", { commitPending: true }); assert.equal( (await form("/retry", { requestId: randomBytes(16).toString("hex") })) .status, 202, ); await wait("active"); assert.equal( await countPuts(), beforeRetry, "late first write is reconciled, not repeated", ); await post("/fixture/expire", { grantRef: auth.grantRef }); assert.equal( (await read()).status, 200, "status remains readable after grant expiry", ); assert.equal( (await form("/remove", { requestId: randomBytes(16).toString("hex") })) .status, 401, ); }, );