Files
suaveplan/packages/api-stack/api/api-sandbox-vm/src/permissions/checker.test.ts
2026-05-27 23:29:53 +01:00

91 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "@suaveplan/testing/runner";
import { createPermissionChecker } from "./checker.js";
describe("createPermissionChecker — fs", () => {
it("allows paths matching ** glob", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: { fs: ["/srv/data/**"] } });
expect(c.checkFs("/srv/data/users.json", "read").allow).toBe(true);
expect(c.checkFs("/srv/data/nested/x.json", "read").allow).toBe(true);
});
it("rejects paths outside the allowlist", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: { fs: ["/srv/data/**"] } });
const d = c.checkFs("/etc/passwd", "read");
expect(d.allow).toBe(false);
if (!d.allow) expect(d.reason).toContain("not matched");
});
it("rejects path-traversal escapes after normalisation", {
timeout: 5000,
}, () => {
const c = createPermissionChecker({ policy: { fs: ["/srv/data/**"] } });
expect(c.checkFs("/srv/data/../../etc/passwd", "read").allow).toBe(
false
);
});
it("supports single-star segments", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: { fs: ["/srv/*.json"] } });
expect(c.checkFs("/srv/users.json", "read").allow).toBe(true);
expect(c.checkFs("/srv/sub/x.json", "read").allow).toBe(false);
});
it("supports ? single-character match", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: { fs: ["/srv/?.json"] } });
expect(c.checkFs("/srv/a.json", "read").allow).toBe(true);
expect(c.checkFs("/srv/ab.json", "read").allow).toBe(false);
});
it("empty allowlist denies everything", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: {} });
expect(c.checkFs("/anything", "write").allow).toBe(false);
});
});
describe("createPermissionChecker — fetch", () => {
it("allows exact host match", { timeout: 5000 }, () => {
const c = createPermissionChecker({
policy: { fetch: ["api.example.com"] },
});
expect(c.checkFetch("https://api.example.com/x").allow).toBe(true);
});
it("allows *.suffix host match", { timeout: 5000 }, () => {
const c = createPermissionChecker({
policy: { fetch: ["*.cdn.example"] },
});
expect(c.checkFetch("https://images.cdn.example/x").allow).toBe(true);
expect(c.checkFetch("https://cdn.example/x").allow).toBe(true);
});
it("rejects host outside allowlist", { timeout: 5000 }, () => {
const c = createPermissionChecker({
policy: { fetch: ["api.example.com"] },
});
const d = c.checkFetch("https://evil.example/x");
expect(d.allow).toBe(false);
if (!d.allow) expect(d.reason).toContain("not matched");
});
it("rejects invalid URL", { timeout: 5000 }, () => {
const c = createPermissionChecker({
policy: { fetch: ["api.example.com"] },
});
const d = c.checkFetch("not-a-url");
expect(d.allow).toBe(false);
if (!d.allow) expect(d.reason).toContain("not a valid URL");
});
});
describe("createPermissionChecker — capability", () => {
it("default allows non-empty capability id", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: {} });
expect(c.checkCapability("db", { method: "query" }).allow).toBe(true);
});
it("default denies empty capability id", { timeout: 5000 }, () => {
const c = createPermissionChecker({ policy: {} });
expect(c.checkCapability("", { method: "q" }).allow).toBe(false);
});
});