tests / security.test.ts
tests / security.test.ts
import { describe, it, expect } from "vitest";
import {
sanitizeFilePath,
validateWritePath,
sanitizeCommand,
expandPath,
} from "../src/securityEnhanced";
describe("sanitizeFilePath", () => {
it("should accept a valid relative path", () => {
const result = sanitizeFilePath("foo/bar.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should accept a valid absolute path", () => {
const result = sanitizeFilePath("/tmp/test/file.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should reject empty string", () => {
const result = sanitizeFilePath("");
expect(result.valid).toBe(false);
expect(result.error).toContain("empty");
});
it("should reject whitespace-only string", () => {
const result = sanitizeFilePath(" ");
expect(result.valid).toBe(false);
expect(result.error).toContain("empty");
});
it("should reject null", () => {
const result = sanitizeFilePath(null as any);
expect(result.valid).toBe(false);
});
it("should reject undefined", () => {
const result = sanitizeFilePath(undefined as any);
expect(result.valid).toBe(false);
});
it("should reject path with null byte", () => {
const result = sanitizeFilePath("foo\u0000bar.txt");
expect(result.valid).toBe(false);
expect(result.error).toContain("null");
});
it("should reject path traversal with ..", () => {
const result = sanitizeFilePath("../../etc/passwd");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should reject path traversal with .. in middle", () => {
const result = sanitizeFilePath("foo/../../etc/passwd");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should reject absolute path traversal", () => {
const result = sanitizeFilePath("/foo/../bar");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should accept path with dots that are not traversal", () => {
const result = sanitizeFilePath("foo.bar/file.txt");
expect(result.valid).toBe(true);
});
it("should accept path with single dot segment", () => {
const result = sanitizeFilePath("foo/./bar.txt");
expect(result.valid).toBe(true);
});
});
describe("validateWritePath", () => {
it("should allow writing to a normal path", () => {
const result = validateWritePath("output/file.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should allow writing to home directory", () => {
const result = validateWritePath("~/output/file.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should reject empty path", () => {
const result = validateWritePath("");
expect(result.valid).toBe(false);
});
it("should reject path traversal", () => {
const result = validateWritePath("../../etc/passwd");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should reject null byte in path", () => {
const result = validateWritePath("foo\u0000bar");
expect(result.valid).toBe(false);
expect(result.error).toContain("null");
});
});
describe("sanitizeCommand", () => {
it("should accept a valid command", () => {
const result = sanitizeCommand("echo hello");
expect(result.valid).toBe(true);
expect(result.sanitized).toBe("echo hello");
});
it("should accept command with arguments", () => {
const result = sanitizeCommand("git status");
expect(result.valid).toBe(true);
});
it("should accept command with pipe to safe command", () => {
const result = sanitizeCommand("cat file.txt | grep hello");
expect(result.valid).toBe(true);
});
it("should reject empty command", () => {
const result = sanitizeCommand("");
expect(result.valid).toBe(false);
});
it("should reject whitespace-only command", () => {
const result = sanitizeCommand(" ");
expect(result.valid).toBe(false);
});
it("should reject command starting with rm", () => {
const result = sanitizeCommand("rm -rf /");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with del", () => {
const result = sanitizeCommand("del /f /q file.txt");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with format", () => {
const result = sanitizeCommand("format C:");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with shutdown", () => {
const result = sanitizeCommand("shutdown /s /t 0");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with reboot", () => {
const result = sanitizeCommand("reboot");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after semicolon", () => {
const result = sanitizeCommand("echo hello; rm -rf /");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after pipe", () => {
const result = sanitizeCommand("cat file | del /f /q");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after &&", () => {
const result = sanitizeCommand("echo hello && format C:");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after redirection", () => {
const result = sanitizeCommand("echo hello > rm -rf /");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with eval", () => {
const result = sanitizeCommand("eval something");
expect(result.valid).toBe(false);
expect(result.error).toContain("risky");
});
it("should reject command with source keyword", () => {
const result = sanitizeCommand("source ./script.sh");
expect(result.valid).toBe(false);
expect(result.error).toContain("risky");
});
it("should accept command with redirection to file", () => {
const result = sanitizeCommand("echo hello > output.txt");
expect(result.valid).toBe(true);
});
it("should accept command with redirection append", () => {
const result = sanitizeCommand("echo hello >> output.txt");
expect(result.valid).toBe(true);
});
it("should accept command with multiple safe pipes", () => {
const result = sanitizeCommand("cat file.txt | grep pattern | sort");
expect(result.valid).toBe(true);
});
it("should accept command with && between safe commands", () => {
const result = sanitizeCommand("cd /tmp && ls");
expect(result.valid).toBe(true);
});
it("should reject command starting with rmdir", () => {
const result = sanitizeCommand("rmdir /s /q folder");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with rd", () => {
const result = sanitizeCommand("rd /s /q folder");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with mkfs", () => {
const result = sanitizeCommand("mkfs.ext4 /dev/sda1");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
});
describe("expandPath", () => {
it("should expand ~ to home directory", () => {
const result = expandPath("~/test");
expect(result).not.toContain("~");
expect(result.length).toBeGreaterThan(4);
});
it("should expand environment variables", () => {
process.env.TEST_EXPAND_VAR = "/test/path";
const result = expandPath("%TEST_EXPAND_VAR%/file.txt");
expect(result).toBe("/test/path/file.txt");
delete process.env.TEST_EXPAND_VAR;
});
it("should leave unknown env vars as-is", () => {
const result = expandPath("%UNKNOWN_VAR_XYZ%/file.txt");
expect(result).toBe("%UNKNOWN_VAR_XYZ%/file.txt");
});
it("should trim whitespace", () => {
const result = expandPath(" ~/test ");
expect(result).not.toMatch(/^\s/);
expect(result).not.toMatch(/\s$/);
});
it("should return empty string for empty input", () => {
const result = expandPath("");
expect(result).toBe("");
});
it("should return non-string input as-is", () => {
const result = expandPath(null as any);
expect(result).toBe(null);
});
});
import { describe, it, expect } from "vitest";
import {
sanitizeFilePath,
validateWritePath,
sanitizeCommand,
expandPath,
} from "../src/securityEnhanced";
describe("sanitizeFilePath", () => {
it("should accept a valid relative path", () => {
const result = sanitizeFilePath("foo/bar.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should accept a valid absolute path", () => {
const result = sanitizeFilePath("/tmp/test/file.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should reject empty string", () => {
const result = sanitizeFilePath("");
expect(result.valid).toBe(false);
expect(result.error).toContain("empty");
});
it("should reject whitespace-only string", () => {
const result = sanitizeFilePath(" ");
expect(result.valid).toBe(false);
expect(result.error).toContain("empty");
});
it("should reject null", () => {
const result = sanitizeFilePath(null as any);
expect(result.valid).toBe(false);
});
it("should reject undefined", () => {
const result = sanitizeFilePath(undefined as any);
expect(result.valid).toBe(false);
});
it("should reject path with null byte", () => {
const result = sanitizeFilePath("foo\u0000bar.txt");
expect(result.valid).toBe(false);
expect(result.error).toContain("null");
});
it("should reject path traversal with ..", () => {
const result = sanitizeFilePath("../../etc/passwd");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should reject path traversal with .. in middle", () => {
const result = sanitizeFilePath("foo/../../etc/passwd");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should reject absolute path traversal", () => {
const result = sanitizeFilePath("/foo/../bar");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should accept path with dots that are not traversal", () => {
const result = sanitizeFilePath("foo.bar/file.txt");
expect(result.valid).toBe(true);
});
it("should accept path with single dot segment", () => {
const result = sanitizeFilePath("foo/./bar.txt");
expect(result.valid).toBe(true);
});
});
describe("validateWritePath", () => {
it("should allow writing to a normal path", () => {
const result = validateWritePath("output/file.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should allow writing to home directory", () => {
const result = validateWritePath("~/output/file.txt");
expect(result.valid).toBe(true);
expect(result.sanitized).toBeDefined();
});
it("should reject empty path", () => {
const result = validateWritePath("");
expect(result.valid).toBe(false);
});
it("should reject path traversal", () => {
const result = validateWritePath("../../etc/passwd");
expect(result.valid).toBe(false);
expect(result.error).toContain("traversal");
});
it("should reject null byte in path", () => {
const result = validateWritePath("foo\u0000bar");
expect(result.valid).toBe(false);
expect(result.error).toContain("null");
});
});
describe("sanitizeCommand", () => {
it("should accept a valid command", () => {
const result = sanitizeCommand("echo hello");
expect(result.valid).toBe(true);
expect(result.sanitized).toBe("echo hello");
});
it("should accept command with arguments", () => {
const result = sanitizeCommand("git status");
expect(result.valid).toBe(true);
});
it("should accept command with pipe to safe command", () => {
const result = sanitizeCommand("cat file.txt | grep hello");
expect(result.valid).toBe(true);
});
it("should reject empty command", () => {
const result = sanitizeCommand("");
expect(result.valid).toBe(false);
});
it("should reject whitespace-only command", () => {
const result = sanitizeCommand(" ");
expect(result.valid).toBe(false);
});
it("should reject command starting with rm", () => {
const result = sanitizeCommand("rm -rf /");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with del", () => {
const result = sanitizeCommand("del /f /q file.txt");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with format", () => {
const result = sanitizeCommand("format C:");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with shutdown", () => {
const result = sanitizeCommand("shutdown /s /t 0");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with reboot", () => {
const result = sanitizeCommand("reboot");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after semicolon", () => {
const result = sanitizeCommand("echo hello; rm -rf /");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after pipe", () => {
const result = sanitizeCommand("cat file | del /f /q");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after &&", () => {
const result = sanitizeCommand("echo hello && format C:");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with dangerous keyword after redirection", () => {
const result = sanitizeCommand("echo hello > rm -rf /");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command with eval", () => {
const result = sanitizeCommand("eval something");
expect(result.valid).toBe(false);
expect(result.error).toContain("risky");
});
it("should reject command with source keyword", () => {
const result = sanitizeCommand("source ./script.sh");
expect(result.valid).toBe(false);
expect(result.error).toContain("risky");
});
it("should accept command with redirection to file", () => {
const result = sanitizeCommand("echo hello > output.txt");
expect(result.valid).toBe(true);
});
it("should accept command with redirection append", () => {
const result = sanitizeCommand("echo hello >> output.txt");
expect(result.valid).toBe(true);
});
it("should accept command with multiple safe pipes", () => {
const result = sanitizeCommand("cat file.txt | grep pattern | sort");
expect(result.valid).toBe(true);
});
it("should accept command with && between safe commands", () => {
const result = sanitizeCommand("cd /tmp && ls");
expect(result.valid).toBe(true);
});
it("should reject command starting with rmdir", () => {
const result = sanitizeCommand("rmdir /s /q folder");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with rd", () => {
const result = sanitizeCommand("rd /s /q folder");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
it("should reject command starting with mkfs", () => {
const result = sanitizeCommand("mkfs.ext4 /dev/sda1");
expect(result.valid).toBe(false);
expect(result.error).toContain("dangerous");
});
});
describe("expandPath", () => {
it("should expand ~ to home directory", () => {
const result = expandPath("~/test");
expect(result).not.toContain("~");
expect(result.length).toBeGreaterThan(4);
});
it("should expand environment variables", () => {
process.env.TEST_EXPAND_VAR = "/test/path";
const result = expandPath("%TEST_EXPAND_VAR%/file.txt");
expect(result).toBe("/test/path/file.txt");
delete process.env.TEST_EXPAND_VAR;
});
it("should leave unknown env vars as-is", () => {
const result = expandPath("%UNKNOWN_VAR_XYZ%/file.txt");
expect(result).toBe("%UNKNOWN_VAR_XYZ%/file.txt");
});
it("should trim whitespace", () => {
const result = expandPath(" ~/test ");
expect(result).not.toMatch(/^\s/);
expect(result).not.toMatch(/\s$/);
});
it("should return empty string for empty input", () => {
const result = expandPath("");
expect(result).toBe("");
});
it("should return non-string input as-is", () => {
const result = expandPath(null as any);
expect(result).toBe(null);
});
});