// The room page's check of the endpoints it takes from its query string and from a relay's // GOAWAY (`node --test web/room/endpoint.test.mjs`): its own origin only, and on a loopback // page (the end-to-end harness) loopback alone. import test from "node:test"; import assert from "node:assert/strict"; import { checkEndpoint, kindOf } from "./endpoint.js"; const PAGE = "https://wai.transaction.science/room/?service=x"; const taken = (v, kind, page = PAGE) => checkEndpoint(v, kind, page); test("the page's own origin is taken, a relative path resolved against it", () => { assert.deepEqual(taken("/room-api", "http"), { ok: true, url: "https://wai.transaction.science/room-api" }); assert.deepEqual(taken("https://wai.transaction.science/room-api", "http"), { ok: true, url: "https://wai.transaction.science/room-api" }); assert.deepEqual(taken("wss://wai.transaction.science/moq", "ws"), { ok: true, url: "wss://wai.transaction.science/moq" }); }); test("another origin is refused, whatever its form", () => { for (const [v, kind] of [ ["https://evil.example/room-api", "http"], ["wss://evil.example/moq", "ws"], ["https://wai.transaction.science.evil.example/moq", "http"], ["https://wai.transaction.science:8443/room-api", "http"], // A scheme-relative URL takes the page's scheme and another host. ["//evil.example/room-api", "http"], ["//evil.example/moq", "ws"], // The page's host as user information: the host is the other one. ["https://wai.transaction.science@evil.example/room-api", "http"], ["wss://wai.transaction.science@evil.example/moq", "ws"], // User information on the page's own host is refused too. ["https://user:pass@wai.transaction.science/room-api", "http"], ]) { const r = taken(v, kind); assert.equal(r.ok, false, `${v} is refused`); assert.ok(r.reason, `${v} has a reason`); } }); test("a scheme that is no endpoint's is refused: javascript:, data:, a plain relay on a secure page", () => { assert.equal(taken("javascript:alert(document.domain)", "http").ok, false); assert.equal(taken("javascript:alert(document.domain)", "ws").ok, false); assert.equal(taken("data:text/html,
x
", "http").ok, false); assert.equal(taken("ws://wai.transaction.science/moq", "ws").ok, false, "a secure page takes wss: alone"); assert.equal(taken("http://wai.transaction.science/room-api", "http").ok, false, "a secure page takes https: alone"); assert.equal(taken("wss://wai.transaction.science/moq", "http").ok, false, "a relay URI is no fetch endpoint"); assert.equal(taken("https://wai.transaction.science/moq", "ws").ok, false, "a web URI is no WebSocket endpoint"); }); test("a page served over plain HTTP from a public host takes nothing", () => { assert.equal(taken("/room-api", "http", "http://wai.transaction.science/room/").ok, false); assert.equal(taken("ws://wai.transaction.science/moq", "ws", "http://wai.transaction.science/room/").ok, false); }); test("a loopback page, as the end-to-end harness serves it, takes loopback endpoints on any port and nothing else", () => { for (const page of ["http://127.0.0.1:41234/room/", "http://localhost:8080/room/", "http://[::1]:9000/room/"]) { assert.equal(taken("ws://127.0.0.1:51001/moq", "ws", page).ok, true, page); assert.equal(taken("https://127.0.0.1:51002/moq", "http", page).ok, true, page); assert.equal(taken("http://localhost:51003/issuer", "http", page).ok, true, page); assert.equal(taken("/fixture.json", "http", page).ok, true, page); assert.equal(taken("wss://evil.example/moq", "ws", page).ok, false, page); assert.equal(taken("//evil.example/room-api", "http", page).ok, false, page); assert.equal(taken("http://127.0.0.1@evil.example/", "http", page).ok, false, page); assert.equal(taken("javascript:alert(1)", "http", page).ok, false, page); } }); test("a GOAWAY's URI is checked by its own scheme", () => { assert.equal(kindOf("wss://wai.transaction.science/moq"), "ws"); assert.equal(kindOf("https://wai.transaction.science/moq"), "http"); assert.equal(taken("wss://relay.evil.example/moq", kindOf("wss://relay.evil.example/moq")).ok, false); assert.equal(taken("wss://wai.transaction.science/moq-b", kindOf("wss://wai.transaction.science/moq-b")).ok, true); });