import test from "node:test"; import assert from "node:assert/strict"; import { isPublicAddress, resolvePublicUrl, fetchPageText, } from "../server/fetch-page.js"; import { setup, signup } from "./helpers.js"; test("prefill rejects loopback, private, mapped IPv6 and reserved IP destinations", async () => { for (const ip of [ "127.0.0.1", "10.0.0.1", "192.168.86.2", "169.254.169.254", "100.64.1.1", "0.0.0.0", "::1", "fc00::1", "fe80::1", "::ffff:127.0.0.1", "2002:7f00:1::", "2001:db8::1", ]) assert.equal(isPublicAddress(ip), false, ip); for (const ip of ["8.8.8.8", "1.1.1.1", "2606:4700:4700::1111"]) assert.equal(isPublicAddress(ip), true, ip); for (const url of [ "https://127.1/", "https://2130706433/", "https://[::1]/", "https://user:pass@example.com/", "https://example.com:8443/", ]) await assert.rejects(fetchPageText(url), { code: "destination_rejected" }); }); test("all DNS answers are validated and selected public address is returned for pinning", async () => { const url = new URL("https://coffee.example/"); await assert.rejects( resolvePublicUrl(url, async () => [ { address: "8.8.8.8", family: 4 }, { address: "10.0.0.1", family: 4 }, ]), { code: "destination_rejected" }, ); assert.deepEqual( await resolvePublicUrl(url, async () => [ { address: "8.8.8.8", family: 4 }, ]), { address: "8.8.8.8", family: 4 }, ); }); test("authenticated prefill route rejects private URL before model invocation", async () => { const { agent } = await setup(); const { csrf } = await signup(agent, "fetch@example.com"); const res = await agent .post("/api/prefill") .set("x-csrf-token", csrf) .send({ url: "https://192.168.86.2/" }); assert.equal(res.status, 422); assert.equal(res.body.code, "destination_rejected"); }); test("redirects to private destinations are rejected before a second network request", async () => { let requests = 0; await assert.rejects( fetchPageText("https://coffee.example/", { resolver: async () => [{ address: "8.8.8.8", family: 4 }], requestPage: async () => { requests++; return new Response(null, { status: 302, headers: { location: "https://127.0.0.1/" }, }); }, }), { code: "destination_rejected" }, ); assert.equal(requests, 1); }); test("deadline stays active while reading a stalled response body", async () => { await assert.rejects( fetchPageText("https://coffee.example/", { timeoutMs: 20, resolver: async () => [{ address: "8.8.8.8", family: 4 }], requestPage: async (_url, signal) => new Response( new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode("partial")); signal.addEventListener( "abort", () => controller.error(new Error("body deadline")), { once: true }, ); }, }), { headers: { "content-type": "text/plain" } }, ), }), /body deadline/, ); });