mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
Added extensive test coverage for previously untested files: Platform package (94.69% coverage, +47 tests): - paths.test.ts: 22 tests for path construction and directory creation - security.test.ts: 25 tests for path validation and security Utils package (94.3% coverage, +109 tests): - logger.test.ts: 23 tests for logging with levels - fs-utils.test.ts: 20 tests for safe file operations - conversation-utils.test.ts: 24 tests for message formatting - image-handler.test.ts: 25 tests for image processing - prompt-builder.test.ts: 17 tests for prompt construction Coverage improvements: - Platform: 63.71% → 94.69% stmts, 40% → 97.14% funcs - Utils: 19.51% → 94.3% stmts, 18.51% → 100% funcs Updated thresholds to enforce high quality: - Platform: 90% lines/stmts, 95% funcs, 75% branches - Utils: 90% lines/stmts, 95% funcs, 85% branches Total new tests: 156 (platform: 47, utils: 109) All tests passing with new coverage thresholds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
239 lines
7.4 KiB
TypeScript
239 lines
7.4 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import path from "path";
|
|
import {
|
|
initAllowedPaths,
|
|
addAllowedPath,
|
|
isPathAllowed,
|
|
validatePath,
|
|
getAllowedPaths,
|
|
} from "../src/security";
|
|
|
|
describe("security.ts", () => {
|
|
let originalEnv: NodeJS.ProcessEnv;
|
|
|
|
beforeEach(() => {
|
|
// Save original environment
|
|
originalEnv = { ...process.env };
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Restore original environment
|
|
process.env = originalEnv;
|
|
});
|
|
|
|
describe("initAllowedPaths", () => {
|
|
it("should initialize from ALLOWED_PROJECT_DIRS environment variable", () => {
|
|
process.env.ALLOWED_PROJECT_DIRS = "/path/one,/path/two,/path/three";
|
|
|
|
initAllowedPaths();
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve("/path/one"));
|
|
expect(allowed).toContain(path.resolve("/path/two"));
|
|
expect(allowed).toContain(path.resolve("/path/three"));
|
|
});
|
|
|
|
it("should trim whitespace from paths", () => {
|
|
process.env.ALLOWED_PROJECT_DIRS = " /path/one , /path/two , /path/three ";
|
|
|
|
initAllowedPaths();
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve("/path/one"));
|
|
expect(allowed).toContain(path.resolve("/path/two"));
|
|
expect(allowed).toContain(path.resolve("/path/three"));
|
|
});
|
|
|
|
it("should skip empty paths", () => {
|
|
process.env.ALLOWED_PROJECT_DIRS = "/path/one,,/path/two, ,/path/three";
|
|
|
|
initAllowedPaths();
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed.length).toBeLessThanOrEqual(3);
|
|
expect(allowed).toContain(path.resolve("/path/one"));
|
|
});
|
|
|
|
it("should initialize from DATA_DIR environment variable", () => {
|
|
process.env.DATA_DIR = "/data/directory";
|
|
|
|
initAllowedPaths();
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve("/data/directory"));
|
|
});
|
|
|
|
it("should initialize from WORKSPACE_DIR environment variable", () => {
|
|
process.env.WORKSPACE_DIR = "/workspace/directory";
|
|
|
|
initAllowedPaths();
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve("/workspace/directory"));
|
|
});
|
|
|
|
it("should handle all environment variables together", () => {
|
|
process.env.ALLOWED_PROJECT_DIRS = "/projects/one,/projects/two";
|
|
process.env.DATA_DIR = "/app/data";
|
|
process.env.WORKSPACE_DIR = "/app/workspace";
|
|
|
|
initAllowedPaths();
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve("/projects/one"));
|
|
expect(allowed).toContain(path.resolve("/projects/two"));
|
|
expect(allowed).toContain(path.resolve("/app/data"));
|
|
expect(allowed).toContain(path.resolve("/app/workspace"));
|
|
});
|
|
|
|
it("should handle missing environment variables gracefully", () => {
|
|
delete process.env.ALLOWED_PROJECT_DIRS;
|
|
delete process.env.DATA_DIR;
|
|
delete process.env.WORKSPACE_DIR;
|
|
|
|
expect(() => initAllowedPaths()).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe("addAllowedPath", () => {
|
|
it("should add a path to allowed list", () => {
|
|
const testPath = "/new/allowed/path";
|
|
|
|
addAllowedPath(testPath);
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve(testPath));
|
|
});
|
|
|
|
it("should resolve relative paths to absolute", () => {
|
|
const relativePath = "relative/path";
|
|
|
|
addAllowedPath(relativePath);
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve(relativePath));
|
|
});
|
|
|
|
it("should handle duplicate paths", () => {
|
|
const testPath = "/duplicate/path";
|
|
|
|
addAllowedPath(testPath);
|
|
addAllowedPath(testPath);
|
|
|
|
const allowed = getAllowedPaths();
|
|
const count = allowed.filter((p) => p === path.resolve(testPath)).length;
|
|
expect(count).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("isPathAllowed", () => {
|
|
it("should always return true (all paths allowed)", () => {
|
|
expect(isPathAllowed("/any/path")).toBe(true);
|
|
expect(isPathAllowed("/another/path")).toBe(true);
|
|
expect(isPathAllowed("relative/path")).toBe(true);
|
|
expect(isPathAllowed("/etc/passwd")).toBe(true);
|
|
expect(isPathAllowed("../../../dangerous/path")).toBe(true);
|
|
});
|
|
|
|
it("should return true even for non-existent paths", () => {
|
|
expect(isPathAllowed("/nonexistent/path/12345")).toBe(true);
|
|
});
|
|
|
|
it("should return true for empty string", () => {
|
|
expect(isPathAllowed("")).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("validatePath", () => {
|
|
it("should resolve absolute paths", () => {
|
|
const absPath = "/absolute/path/to/file.txt";
|
|
const result = validatePath(absPath);
|
|
expect(result).toBe(path.resolve(absPath));
|
|
});
|
|
|
|
it("should resolve relative paths", () => {
|
|
const relPath = "relative/path/file.txt";
|
|
const result = validatePath(relPath);
|
|
expect(result).toBe(path.resolve(relPath));
|
|
});
|
|
|
|
it("should handle current directory", () => {
|
|
const result = validatePath(".");
|
|
expect(result).toBe(path.resolve("."));
|
|
});
|
|
|
|
it("should handle parent directory", () => {
|
|
const result = validatePath("..");
|
|
expect(result).toBe(path.resolve(".."));
|
|
});
|
|
|
|
it("should handle complex relative paths", () => {
|
|
const complexPath = "../../some/nested/../path/./file.txt";
|
|
const result = validatePath(complexPath);
|
|
expect(result).toBe(path.resolve(complexPath));
|
|
});
|
|
|
|
it("should handle paths with spaces", () => {
|
|
const pathWithSpaces = "/path with spaces/file.txt";
|
|
const result = validatePath(pathWithSpaces);
|
|
expect(result).toBe(path.resolve(pathWithSpaces));
|
|
});
|
|
|
|
it("should handle home directory expansion on Unix", () => {
|
|
if (process.platform !== "win32") {
|
|
const homePath = "~/documents/file.txt";
|
|
const result = validatePath(homePath);
|
|
expect(result).toBe(path.resolve(homePath));
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("getAllowedPaths", () => {
|
|
it("should return empty array initially", () => {
|
|
const allowed = getAllowedPaths();
|
|
expect(Array.isArray(allowed)).toBe(true);
|
|
});
|
|
|
|
it("should return array of added paths", () => {
|
|
addAllowedPath("/path/one");
|
|
addAllowedPath("/path/two");
|
|
|
|
const allowed = getAllowedPaths();
|
|
expect(allowed).toContain(path.resolve("/path/one"));
|
|
expect(allowed).toContain(path.resolve("/path/two"));
|
|
});
|
|
|
|
it("should return copy of internal set", () => {
|
|
addAllowedPath("/test/path");
|
|
|
|
const allowed1 = getAllowedPaths();
|
|
const allowed2 = getAllowedPaths();
|
|
|
|
expect(allowed1).not.toBe(allowed2);
|
|
expect(allowed1).toEqual(allowed2);
|
|
});
|
|
});
|
|
|
|
describe("Path security disabled behavior", () => {
|
|
it("should allow unrestricted access despite allowed paths list", () => {
|
|
process.env.ALLOWED_PROJECT_DIRS = "/only/this/path";
|
|
initAllowedPaths();
|
|
|
|
// Should return true even for paths not in allowed list
|
|
expect(isPathAllowed("/some/other/path")).toBe(true);
|
|
expect(isPathAllowed("/completely/different/path")).toBe(true);
|
|
});
|
|
|
|
it("should validate paths without permission checks", () => {
|
|
process.env.ALLOWED_PROJECT_DIRS = "/only/this/path";
|
|
initAllowedPaths();
|
|
|
|
// Should validate any path without throwing
|
|
expect(() => validatePath("/some/other/path")).not.toThrow();
|
|
expect(validatePath("/some/other/path")).toBe(
|
|
path.resolve("/some/other/path")
|
|
);
|
|
});
|
|
});
|
|
});
|