import test from "node:test"; import assert from "node:assert/strict"; import { mkdtemp, writeFile, symlink, rm, mkdir } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { readAlogFromLibrary } from "../server/alog-library.js"; import { setup, signup } from "./helpers.js"; test("server filesystem roast library is restricted to administrators", async () => { const { agent, db } = await setup(); await signup(agent, "library@example.com"); assert.equal((await agent.get("/api/alog/library")).status, 403); assert.equal((await agent.get("/api/alog/library/example.alog")).status, 403); await db.query("UPDATE users SET role='admin' WHERE email=$1", [ "library@example.com", ]); assert.equal((await agent.get("/api/alog/library")).status, 200); }); test("library reader rejects path traversal and symlinks outside its directory", async (t) => { const root = await mkdtemp(path.join(os.tmpdir(), "roast-library-")); const old = process.env.ALOG_DIR; process.env.ALOG_DIR = path.join(root, "library"); await mkdir(process.env.ALOG_DIR); t.after(async () => { if (old === undefined) delete process.env.ALOG_DIR; else process.env.ALOG_DIR = old; await rm(root, { recursive: true, force: true }); }); const content = JSON.stringify({ timex: [0, 60, 120], temp1: [20, 30, 40], temp2: [30, 40, 50], mode: "C", }); await writeFile(path.join(root, "outside.alog"), content); await writeFile(path.join(process.env.ALOG_DIR, "inside.alog"), content); await symlink( path.join(root, "outside.alog"), path.join(process.env.ALOG_DIR, "link.alog"), ); await assert.rejects(readAlogFromLibrary("../outside.alog"), { code: "bad_filename", }); await assert.rejects(readAlogFromLibrary("link.alog"), { code: "not_found" }); assert.ok(await readAlogFromLibrary("inside.alog")); });