mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 14:22:02 +00:00
254 lines
7.9 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|