// Path-traversal guard for meeting record ids (internal-meetings.js // meetingPath). A caller-supplied :id must never escape the // internal-meetings/ directory. import { test, describe } from "node:test"; import assert from "node:assert/strict"; import path from "node:path"; import { meetingPath } from "../routes/internal-meetings.js"; const DATA = "/data"; const DIR = path.join(DATA, "internal-meetings"); describe("meetingPath", () => { test("a normal UUID id maps into internal-meetings/", () => { const id = "2f1c9b3a-0e4d-4a77-9d2a-abc123def456"; assert.equal(meetingPath(DATA, id), path.join(DIR, `${id}.json`)); }); test("traversal-shaped ids are sanitized and stay inside the dir", () => { for (const id of ["../../etc/passwd", "../../../root/.ssh/id", "..%2f..%2fx", "a/b/c", "....//x"]) { const p = meetingPath(DATA, id); const rel = path.relative(DIR, p); assert.ok(!rel.startsWith(".."), `${id} escaped to ${p}`); assert.ok(!p.includes(".."), `${id} left ".." in ${p}`); assert.ok(p.endsWith(".json")); } }); test("an id that sanitizes to empty throws (load/delete catch → 404 / no-op)", () => { for (const id of ["", null, undefined, "/", "../", "...", "!!!"]) { assert.throws(() => meetingPath(DATA, id), /invalid meeting id/); } }); });