Skip to main content
Glama
executionContextValidator.test.ts9.75 kB
/** * Tests for execution context validation */ import { describe, it, expect, beforeEach } from 'vitest'; import path from 'path'; import fs from 'fs'; import os from 'os'; import { validateExecutionContext, validateProcessContext, } from '../../src/security/executionContextValidator.js'; describe('executionContextValidator', () => { const workspaceRoot = process.cwd(); const parentDir = path.dirname(workspaceRoot); describe('validateExecutionContext', () => { it('should allow undefined cwd (defaults to safe process.cwd())', () => { const result = validateExecutionContext(undefined); expect(result.isValid).toBe(true); expect(result.error).toBeUndefined(); }); it('should reject empty string cwd', () => { const result = validateExecutionContext(''); expect(result.isValid).toBe(false); expect(result.error).toContain('cannot be empty'); }); it('should reject whitespace-only cwd', () => { const result = validateExecutionContext(' '); expect(result.isValid).toBe(false); expect(result.error).toContain('cannot be empty'); }); it('should allow cwd within workspace (absolute path)', () => { const validPath = path.join(workspaceRoot, 'src'); const result = validateExecutionContext(validPath); expect(result.isValid).toBe(true); expect(result.sanitizedPath).toBe(validPath); }); it('should allow cwd within workspace (relative path)', () => { const result = validateExecutionContext('./src'); expect(result.isValid).toBe(true); expect(result.sanitizedPath).toContain('src'); }); it('should allow workspace root itself', () => { const result = validateExecutionContext(workspaceRoot); expect(result.isValid).toBe(true); expect(result.sanitizedPath).toBe(workspaceRoot); }); it('should reject parent directory', () => { const result = validateExecutionContext(parentDir); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); expect(result.error).toContain(workspaceRoot); }); it('should reject path traversal with ../', () => { const result = validateExecutionContext('../'); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); }); it('should reject multiple path traversals (../../../../)', () => { const result = validateExecutionContext('../../../../'); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); }); it('should reject system directories (/etc)', () => { const result = validateExecutionContext('/etc'); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); }); it('should reject root directory (/)', () => { const result = validateExecutionContext('/'); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); }); it('should reject home directory', () => { const homeDir = os.homedir(); if (homeDir !== workspaceRoot && !homeDir.startsWith(workspaceRoot)) { const result = validateExecutionContext(homeDir); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); } }); it('should handle custom workspace root', () => { const customRoot = path.join(workspaceRoot, 'packages'); const validPath = path.join(customRoot, 'octocode-local-files'); const result = validateExecutionContext(validPath, customRoot); expect(result.isValid).toBe(true); }); it('should reject path outside custom workspace root', () => { const customRoot = path.join(workspaceRoot, 'packages', 'octocode-local-files'); const invalidPath = path.join(workspaceRoot, 'packages'); const result = validateExecutionContext(invalidPath, customRoot); expect(result.isValid).toBe(false); expect(result.error).toContain('Can only execute commands within workspace directory'); }); describe('symlink handling', () => { const tmpDir = path.join(workspaceRoot, 'test-tmp-symlinks'); const targetDir = path.join(tmpDir, 'target'); const symlinkPath = path.join(tmpDir, 'symlink'); const externalTarget = path.join(parentDir, 'external-target'); beforeEach(async () => { // Clean up any existing test directories try { if (fs.existsSync(tmpDir)) { fs.rmSync(tmpDir, { recursive: true, force: true }); } } catch { // Ignore cleanup errors } }); it('should allow symlink that points within workspace', async () => { try { // Create test directories fs.mkdirSync(tmpDir, { recursive: true }); fs.mkdirSync(targetDir, { recursive: true }); // Create symlink pointing to target within workspace fs.symlinkSync(targetDir, symlinkPath, 'dir'); const result = validateExecutionContext(symlinkPath); expect(result.isValid).toBe(true); // Clean up fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (error) { // Skip test if symlink creation fails (e.g., permissions) console.warn('Skipping symlink test:', error); } }); it('should reject symlink that points outside workspace', async () => { try { // Create test directories fs.mkdirSync(tmpDir, { recursive: true }); // Try to create external target (might fail, that's ok for test) try { fs.mkdirSync(externalTarget, { recursive: true }); } catch { // Can't create in parent, skip this test return; } // Create symlink pointing outside workspace fs.symlinkSync(externalTarget, symlinkPath, 'dir'); const result = validateExecutionContext(symlinkPath); expect(result.isValid).toBe(false); expect(result.error).toContain('Symlink target'); expect(result.error).toContain('outside workspace'); // Clean up fs.rmSync(tmpDir, { recursive: true, force: true }); fs.rmSync(externalTarget, { recursive: true, force: true }); } catch (error) { // Skip test if setup fails console.warn('Skipping external symlink test:', error); } }); }); }); describe('validateProcessContext', () => { it('should validate that current process is within workspace', () => { const result = validateProcessContext(); // This should always pass since our tests run within the workspace expect(result.isValid).toBe(true); }); it('should handle custom workspace root', () => { const currentCwd = process.cwd(); // If we set a custom root that contains current cwd, it should pass const result = validateProcessContext(currentCwd); expect(result.isValid).toBe(true); }); it('should fail if workspace root is set to a child directory', () => { const childDir = path.join(process.cwd(), 'src', 'subfolder', 'deep'); const result = validateProcessContext(childDir); expect(result.isValid).toBe(false); expect(result.error).toContain('Process is running outside workspace directory'); }); }); describe('WORKSPACE_ROOT environment variable', () => { it('should respect WORKSPACE_ROOT env var if set', () => { const originalEnv = process.env.WORKSPACE_ROOT; try { // Set custom workspace root process.env.WORKSPACE_ROOT = workspaceRoot; // Should allow path within workspace const validPath = path.join(workspaceRoot, 'src'); const result = validateExecutionContext(validPath); expect(result.isValid).toBe(true); // Should reject parent directory const invalidResult = validateExecutionContext(parentDir); expect(invalidResult.isValid).toBe(false); } finally { // Restore original env if (originalEnv) { process.env.WORKSPACE_ROOT = originalEnv; } else { delete process.env.WORKSPACE_ROOT; } } }); }); describe('edge cases', () => { it('should handle paths with trailing slashes', () => { const pathWithSlash = path.join(workspaceRoot, 'src') + '/'; const result = validateExecutionContext(pathWithSlash); expect(result.isValid).toBe(true); }); it('should handle paths with ./ prefix', () => { const result = validateExecutionContext('./src'); expect(result.isValid).toBe(true); }); it('should handle paths with redundant segments (./src/./lib)', () => { const result = validateExecutionContext('./src/./lib'); expect(result.isValid).toBe(true); }); it('should normalize and validate complex relative paths', () => { // src/../src/lib should resolve to src/lib const result = validateExecutionContext('./src/../src/lib'); expect(result.isValid).toBe(true); }); it('should reject path that escapes via complex traversal', () => { // Try to escape: src/../../.. const result = validateExecutionContext('./src/../../..'); expect(result.isValid).toBe(false); }); }); });

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/bgauryy/local-explorer-mcp'

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