Files
automaker/libs/platform/tests/security.test.ts
2025-12-20 23:55:03 -05:00

254 lines
7.9 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import path from "path";
describe("security.ts", () => {
let originalEnv: NodeJS.ProcessEnv;
beforeEach(() => {
// Save original environment
originalEnv = { ...process.env };
// Reset modules to get fresh state
vi.resetModules();
});
afterEach(() => {
// Restore original environment
process.env = originalEnv;
});
describe("initAllowedPaths", () => {
it("should load ALLOWED_ROOT_DIRECTORY if set", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/projects";
delete process.env.DATA_DIR;
const { initAllowedPaths, getAllowedPaths } =
await import("../src/security");
initAllowedPaths();
const allowed = getAllowedPaths();
expect(allowed).toContain(path.resolve("/projects"));
});
it("should load DATA_DIR if set", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
process.env.DATA_DIR = "/data/directory";
const { initAllowedPaths, getAllowedPaths } =
await import("../src/security");
initAllowedPaths();
const allowed = getAllowedPaths();
expect(allowed).toContain(path.resolve("/data/directory"));
});
it("should load both ALLOWED_ROOT_DIRECTORY and DATA_DIR if both set", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/projects";
process.env.DATA_DIR = "/app/data";
const { initAllowedPaths, getAllowedPaths } =
await import("../src/security");
initAllowedPaths();
const allowed = getAllowedPaths();
expect(allowed).toContain(path.resolve("/projects"));
expect(allowed).toContain(path.resolve("/app/data"));
});
it("should handle missing environment variables gracefully", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
delete process.env.DATA_DIR;
const { initAllowedPaths } = await import("../src/security");
expect(() => initAllowedPaths()).not.toThrow();
});
});
describe("isPathAllowed", () => {
it("should allow paths within ALLOWED_ROOT_DIRECTORY", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/allowed";
delete process.env.DATA_DIR;
const { initAllowedPaths, isPathAllowed } =
await import("../src/security");
initAllowedPaths();
expect(isPathAllowed("/allowed/file.txt")).toBe(true);
expect(isPathAllowed("/allowed/subdir/file.txt")).toBe(true);
});
it("should deny paths outside ALLOWED_ROOT_DIRECTORY", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/allowed";
delete process.env.DATA_DIR;
const { initAllowedPaths, isPathAllowed } =
await import("../src/security");
initAllowedPaths();
expect(isPathAllowed("/not-allowed/file.txt")).toBe(false);
expect(isPathAllowed("/etc/passwd")).toBe(false);
});
it("should always allow DATA_DIR paths", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/projects";
process.env.DATA_DIR = "/app/data";
const { initAllowedPaths, isPathAllowed } =
await import("../src/security");
initAllowedPaths();
// DATA_DIR paths are always allowed
expect(isPathAllowed("/app/data/settings.json")).toBe(true);
expect(isPathAllowed("/app/data/credentials.json")).toBe(true);
});
it("should allow all paths when no restrictions configured", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
delete process.env.DATA_DIR;
const { initAllowedPaths, isPathAllowed } =
await import("../src/security");
initAllowedPaths();
expect(isPathAllowed("/any/path")).toBe(true);
expect(isPathAllowed("/etc/passwd")).toBe(true);
});
it("should allow all paths when only DATA_DIR is configured", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
process.env.DATA_DIR = "/data";
const { initAllowedPaths, isPathAllowed } =
await import("../src/security");
initAllowedPaths();
// DATA_DIR should be allowed
expect(isPathAllowed("/data/file.txt")).toBe(true);
// And all other paths should be allowed since no ALLOWED_ROOT_DIRECTORY restriction
expect(isPathAllowed("/any/path")).toBe(true);
});
});
describe("validatePath", () => {
it("should return resolved path for allowed paths", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/allowed";
delete process.env.DATA_DIR;
const { initAllowedPaths, validatePath } =
await import("../src/security");
initAllowedPaths();
const result = validatePath("/allowed/file.txt");
expect(result).toBe(path.resolve("/allowed/file.txt"));
});
it("should throw error for paths outside allowed directories", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/allowed";
delete process.env.DATA_DIR;
const { initAllowedPaths, validatePath, PathNotAllowedError } =
await import("../src/security");
initAllowedPaths();
expect(() => validatePath("/not-allowed/file.txt")).toThrow(
PathNotAllowedError
);
});
it("should resolve relative paths", async () => {
const cwd = process.cwd();
process.env.ALLOWED_ROOT_DIRECTORY = cwd;
delete process.env.DATA_DIR;
const { initAllowedPaths, validatePath } =
await import("../src/security");
initAllowedPaths();
const result = validatePath("./file.txt");
expect(result).toBe(path.resolve(cwd, "./file.txt"));
});
it("should not throw when no restrictions configured", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
delete process.env.DATA_DIR;
const { initAllowedPaths, validatePath } =
await import("../src/security");
initAllowedPaths();
expect(() => validatePath("/any/path")).not.toThrow();
});
});
describe("getAllowedPaths", () => {
it("should return empty array when no paths configured", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
delete process.env.DATA_DIR;
const { initAllowedPaths, getAllowedPaths } =
await import("../src/security");
initAllowedPaths();
const allowed = getAllowedPaths();
expect(Array.isArray(allowed)).toBe(true);
expect(allowed).toHaveLength(0);
});
it("should return configured paths", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/projects";
process.env.DATA_DIR = "/data";
const { initAllowedPaths, getAllowedPaths } =
await import("../src/security");
initAllowedPaths();
const allowed = getAllowedPaths();
expect(allowed).toContain(path.resolve("/projects"));
expect(allowed).toContain(path.resolve("/data"));
});
});
describe("getAllowedRootDirectory", () => {
it("should return the configured root directory", async () => {
process.env.ALLOWED_ROOT_DIRECTORY = "/projects";
const { initAllowedPaths, getAllowedRootDirectory } =
await import("../src/security");
initAllowedPaths();
expect(getAllowedRootDirectory()).toBe(path.resolve("/projects"));
});
it("should return null when not configured", async () => {
delete process.env.ALLOWED_ROOT_DIRECTORY;
const { initAllowedPaths, getAllowedRootDirectory } =
await import("../src/security");
initAllowedPaths();
expect(getAllowedRootDirectory()).toBeNull();
});
});
describe("getDataDirectory", () => {
it("should return the configured data directory", async () => {
process.env.DATA_DIR = "/data";
const { initAllowedPaths, getDataDirectory } =
await import("../src/security");
initAllowedPaths();
expect(getDataDirectory()).toBe(path.resolve("/data"));
});
it("should return null when not configured", async () => {
delete process.env.DATA_DIR;
const { initAllowedPaths, getDataDirectory } =
await import("../src/security");
initAllowedPaths();
expect(getDataDirectory()).toBeNull();
});
});
});