import { mkdtemp, rm, stat, symlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { LocalArtifactStore } from "./artifacts.js"; const dirs: string[] = []; const context = { spaceId: "space-1" } as never; afterEach(async () => { await Promise.all(dirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))); }); describe("LocalArtifactStore", () => { it("creates artifact files with owner-only permissions", async () => { const root = await mkdtemp(path.join(tmpdir(), "rakazo-artifacts-")); dirs.push(root); const store = new LocalArtifactStore(root); const stored = await store.put( { name: "private.txt", mimeType: "text/plain", bytes: new TextEncoder().encode("private") }, context, ); const info = await stat(path.join(root, "artifacts", "space-1", stored.id)); expect(info.mode & 0o777).toBe(0o600); }); it("does not follow a replacement symlink when reading an artifact", async () => { const root = await mkdtemp(path.join(tmpdir(), "rakazo-artifacts-")); dirs.push(root); const store = new LocalArtifactStore(root); const stored = await store.put( { name: "private.txt", mimeType: "text/plain", bytes: new TextEncoder().encode("private") }, context, ); const file = path.join(root, "artifacts", "space-1", stored.id); const target = path.join(root, "outside.txt"); await writeFile(target, "outside"); await rm(file); await symlink(target, file); await expect(store.get(stored.id, context)).rejects.toThrow(); }); });