// Guards for the cross-site request gate on the loopback API server. // // Run: node --test extensions/connector-namespaces/server.test.mjs // // The server binds an ephemeral 127.0.0.1 port and JSON-parses every POST body, // so without a check any web page the user visits could script-drive their ARM // operations (CSRF). isCrossSiteRequest is the gate: it blocks a POST /api/* // only when the request carries an explicit foreign-origin signal, and lets the // panel's own same-origin fetches — and header-less callers like this test // harness — through untouched. Importing server.mjs has no side effects at eval; // the HTTP server only starts when startServer() is called. import { test } from "node:test"; import assert from "node:assert/strict"; import { EventEmitter } from "node:events"; import { Readable } from "node:stream"; import { getServerConfig, hasCapabilityToken, isCanonicalHost, isCrossSiteRequest, listenOnLoopback, parseBody, requiresCapabilityToken, runIdempotentOperation, startServer, stopServer, } from "./server.mjs"; import { isValidConfig } from "./state.mjs"; // Minimal request stub: only headers matter to the gate. function req(headers) { return { headers }; } test("same-origin Origin (our own loopback UI) is allowed", () => { const r = req({ host: "127.0.0.1:54321", origin: "http://127.0.0.1:54321" }); assert.equal(isCrossSiteRequest(r, "http://127.0.0.1:54321"), false); }); test("no headers at all (test harness / non-browser client) is allowed", () => { assert.equal(isCrossSiteRequest(req({}), "http://127.0.0.1:54321"), false); }); test("non-web-scheme Origin (host webview) is allowed", () => { // Some app webviews send Origin like "vscode-webview://..." or "app://..." // custom schemes. Those aren't a browsable web page driving a CSRF, so we // don't block them; only http(s) foreign origins and opaque `null` origins // are treated as hostile. const r = req({ host: "127.0.0.1:54321", origin: "app://obsidian.md" }); assert.equal(isCrossSiteRequest(r, "http://127.0.0.1:54321"), false); }); test("opaque null Origin (sandboxed iframe / data: URI) is blocked", () => { // Browsers send the literal string "null" as Origin from sandboxed iframes // (