Skip to main content
Glama
path-security.test.ts5.16 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { validatePathSecurity, securePathResolve, validateFileExtension, validateTestFilePath, validateConfigFilePath, createSecureTempPath, sanitizeFileContent } from '../path-security'; // Mock fs operations vi.mock('fs', () => ({ promises: { access: vi.fn(), stat: vi.fn() } })); describe('path-security', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('validatePathSecurity', () => { it('should accept safe paths', () => { expect(() => validatePathSecurity('./src/test.ts')).not.toThrow(); expect(() => validatePathSecurity('src/components/Button.tsx')).not.toThrow(); }); it('should reject path traversal attempts', () => { expect(() => validatePathSecurity('../../../etc/passwd')).toThrow('dangerous path pattern'); expect(() => validatePathSecurity('./src/../../../etc/passwd')).toThrow('dangerous path pattern'); }); it('should reject null byte injection', () => { expect(() => validatePathSecurity('file.txt\0.exe')).toThrow('dangerous path pattern'); }); it('should reject system directories', () => { expect(() => validatePathSecurity('/etc/passwd')).toThrow('system directory forbidden'); expect(() => validatePathSecurity('C:\\Windows\\System32')).toThrow('system directory forbidden'); }); it('should reject paths that are too long', () => { const longPath = 'a'.repeat(5000); expect(() => validatePathSecurity(longPath)).toThrow('Path too long'); }); }); describe('securePathResolve', () => { it('should resolve paths within project boundary', () => { const result = securePathResolve('/project', './src/file.ts'); expect(result).toMatch(/\/project\/src\/file\.ts$/); }); it('should prevent directory traversal escapes', () => { // These paths contain .. patterns so they're caught by validatePathSecurity first expect(() => securePathResolve('/project', '../outside.txt')).toThrow('dangerous path pattern'); expect(() => securePathResolve('/project', '../../etc/passwd')).toThrow('dangerous path pattern'); }); it('should normalize and resolve safe paths', () => { const result = securePathResolve('/project', './src/utils.ts'); expect(result).toMatch(/\/project\/src\/utils\.ts$/); }); }); describe('validateFileExtension', () => { it('should accept allowed extensions', () => { expect(() => validateFileExtension('test.ts', ['.ts', '.js'])).not.toThrow(); expect(() => validateFileExtension('component.tsx', ['.tsx', '.ts'])).not.toThrow(); }); it('should reject disallowed extensions', () => { expect(() => validateFileExtension('script.py', ['.ts', '.js'])).toThrow("extension '.py' not allowed"); expect(() => validateFileExtension('malware.exe', ['.ts', '.js'])).toThrow("extension '.exe' not allowed"); }); }); describe('validateTestFilePath', () => { it('should accept valid test files', () => { expect(() => validateTestFilePath('src/components/Button.test.ts')).not.toThrow(); expect(() => validateTestFilePath('__tests__/utils.spec.js')).not.toThrow(); }); it('should accept source files for coverage', () => { expect(() => validateTestFilePath('src/components/Button.tsx')).not.toThrow(); }); it('should reject invalid file extensions', () => { expect(() => validateTestFilePath('test.py')).toThrow('not allowed'); }); }); describe('validateConfigFilePath', () => { it('should accept valid config files', () => { expect(() => validateConfigFilePath('vitest.config.ts')).not.toThrow(); expect(() => validateConfigFilePath('.vitest-mcp.json')).not.toThrow(); }); it('should reject files with invalid extensions first', () => { // Extension validation happens before config name validation expect(() => validateConfigFilePath('random.txt')).toThrow('not allowed'); }); }); describe('createSecureTempPath', () => { it('should create secure temp paths', () => { const tempPath = createSecureTempPath('/project', 'vitest'); expect(tempPath).toMatch(/\/project\/vitest-[a-f0-9]{64}\.tmp$/); }); it('should sanitize prefixes', () => { const tempPath = createSecureTempPath('/project', 'test../file'); expect(tempPath).toMatch(/\/project\/testfile-[a-f0-9]{64}\.tmp$/); }); }); describe('sanitizeFileContent', () => { it('should remove script tags', () => { const malicious = 'Hello <script>alert("xss")</script> World'; const result = sanitizeFileContent(malicious); expect(result).toBe('Hello World'); }); it('should remove dangerous protocols', () => { const malicious = 'Click javascript:alert("xss") here'; const result = sanitizeFileContent(malicious); expect(result).toBe('Click alert("xss") here'); }); it('should handle non-string input', () => { expect(sanitizeFileContent(null as any)).toBe(''); expect(sanitizeFileContent(undefined as any)).toBe(''); }); }); });

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/djankies/vitest-mcp'

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