Skip to main content
Glama
security.test.tsβ€’6.21 kB
import * as fs from "node:fs"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { SecurityError, setWorkspaceRoot, validateFilePath } from "../../src/utils/security.js"; // Mock fs operations vi.mock("node:fs", async () => { const actual = await vi.importActual("node:fs"); return { ...actual, realpathSync: vi.fn((path: string) => path), // Just return the path as-is for testing existsSync: vi.fn(() => true), // Always return true for parent directory existence }; }); describe("Security Utilities", () => { beforeEach(() => { // Reset mocks before each test vi.clearAllMocks(); // Set a default workspace root for testing setWorkspaceRoot("/test/workspace"); // Mock realpathSync to return the path as-is by default vi.spyOn(fs, "realpathSync").mockImplementation((path: fs.PathLike) => path as string); // Mock existsSync to return true by default vi.spyOn(fs, "existsSync").mockImplementation(() => true); }); describe("validateFilePath", () => { describe("Basic Validation", () => { it("should block empty paths", () => { expect(() => validateFilePath("", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath(" ", "/test/workspace")).toThrow(SecurityError); }); it("should allow valid relative paths", () => { expect(() => validateFilePath("src/index.ts", "/test/workspace")).not.toThrow(); expect(() => validateFilePath("test/utils/security.test.ts", "/test/workspace")).not.toThrow(); expect(() => validateFilePath("package.json", "/test/workspace")).not.toThrow(); expect(() => validateFilePath("src/utils/security.ts", "/test/workspace")).not.toThrow(); }); it("should block null bytes", () => { expect(() => validateFilePath("test.js\0", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("\0test.js", "/test/workspace")).toThrow(SecurityError); }); it("should block absolute paths outside workspace", () => { // Mock realpathSync to simulate actual path resolution vi.spyOn(fs, "realpathSync").mockImplementation((path: fs.PathLike) => { if (path === "/etc/passwd") { return "/etc/passwd"; } if (path === "/home/user/file.txt") { return "/home/user/file.txt"; } if (path === "/usr/local/bin/script.js") { return "/usr/local/bin/script.js"; } if (path === "/test/workspace") { return "/test/workspace"; } return path as string; }); expect(() => validateFilePath("/etc/passwd", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("/home/user/file.txt", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("/usr/local/bin/script.js", "/test/workspace")).toThrow(SecurityError); }); it("should allow absolute paths within workspace", () => { // Mock realpathSync to simulate actual path resolution vi.spyOn(fs, "realpathSync").mockImplementation((path: fs.PathLike) => { if (path === "/test/workspace/src/index.ts") { return "/test/workspace/src/index.ts"; } if (path === "/test/workspace/package.json") { return "/test/workspace/package.json"; } if (path === "/test/workspace") { return "/test/workspace"; } return path as string; }); expect(() => validateFilePath("/test/workspace/src/index.ts", "/test/workspace")).not.toThrow(); expect(() => validateFilePath("/test/workspace/package.json", "/test/workspace")).not.toThrow(); }); }); describe("Path Traversal Protection", () => { it("should block basic path traversal", () => { expect(() => validateFilePath("../../etc/passwd", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("../config.json", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("../../../etc/passwd", "/test/workspace")).toThrow(SecurityError); }); it("should block encoded traversal patterns", () => { expect(() => validateFilePath("%2e%2e%2fetc/passwd", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("%2e%2e/etc/passwd", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("..%2fetc/passwd", "/test/workspace")).toThrow(SecurityError); }); it("should allow legitimate filenames with double dots", () => { expect(() => validateFilePath("config..json", "/test/workspace")).not.toThrow(); expect(() => validateFilePath("file..txt", "/test/workspace")).not.toThrow(); expect(() => validateFilePath("test.config..js", "/test/workspace")).not.toThrow(); }); }); describe("Windows-Specific Attacks (when on Windows platform)", () => { it("should block UNC paths when on Windows", () => { // Mock process.platform to simulate Windows const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform"); Object.defineProperty(process, "platform", { value: "win32" }); try { expect(() => validateFilePath("\\\\server\\share", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("\\\\192.168.1.1\\share", "/test/workspace")).toThrow(SecurityError); } finally { // Restore original platform if (originalPlatform) { Object.defineProperty(process, "platform", originalPlatform); } else { delete (process as any).platform; } } }); it("should block drive letter paths when on Windows", () => { // Mock process.platform to simulate Windows const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform"); Object.defineProperty(process, "platform", { value: "win32" }); try { expect(() => validateFilePath("C:\\Windows", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("D:\\Data\\file.txt", "/test/workspace")).toThrow(SecurityError); expect(() => validateFilePath("Z:\\System\\config.ini", "/test/workspace")).toThrow(SecurityError); } finally { // Restore original platform if (originalPlatform) { Object.defineProperty(process, "platform", originalPlatform); } else { delete (process as any).platform; } } }); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/snapback-dev/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server