Skip to main content
Glama
penetration-test.test.ts7.88 kB
/** * SECURITY PENETRATION TEST * Comprehensive attack testing for pathValidator */ import { describe, it, expect, beforeEach } from 'vitest'; import { PathValidator } from '../../src/security/pathValidator.js'; describe('🔴 SECURITY PENETRATION TEST - PathValidator', () => { const mockWorkspace = '/Users/guybary'; let validator: PathValidator; beforeEach(() => { validator = new PathValidator(mockWorkspace); }); describe('ATTACK 1: Path Traversal Variations', () => { it('should BLOCK classic ../ traversal', () => { const result = validator.validate('/Users/guybary/../../../etc/passwd'); expect(result.isValid).toBe(false); expect(result.error).toContain('outside allowed directories'); }); it('should BLOCK deep traversal (10 levels)', () => { const path = '/Users/guybary' + '/..' .repeat(10) + '/etc'; const result = validator.validate(path); expect(result.isValid).toBe(false); }); it('should BLOCK mixed ./ and ../', () => { const result = validator.validate('/Users/guybary/./Documents/.././.././.././etc'); expect(result.isValid).toBe(false); }); it('should BLOCK trailing slash traversal', () => { const result = validator.validate('/Users/guybary/../../../etc/'); expect(result.isValid).toBe(false); }); }); describe('ATTACK 2: Encoding Attacks', () => { it('URL encoded dots (%2e%2e) - SAFE (Node treats as literal)', () => { // Node.js path.resolve() treats %2e as literal characters, not encoded dots // This creates a directory named "%2e%2e" (literal), not ".." const result = validator.validate('/Users/guybary/%2e%2e/%2e%2e/etc'); // This is ALLOWED because it stays within workspace expect(result.isValid).toBe(true); expect(result.sanitizedPath).toContain('%2e%2e'); // Verify it doesn't escape workspace expect(result.sanitizedPath?.startsWith('/Users/guybary')).toBe(true); }); it('should BLOCK backslash on Unix', () => { const result = validator.validate('/Users/guybary\\..\\..\\etc'); expect(result.isValid).toBe(false); }); it('should BLOCK mixed slashes and backslashes', () => { const result = validator.validate('/Users/guybary\\..//..\\\\etc'); expect(result.isValid).toBe(false); }); it('Double URL encoding (%252e) - SAFE (Node treats as literal)', () => { // Same as above - literal %252e characters, not double-encoded dots const result = validator.validate('/Users/guybary/%252e%252e/etc'); expect(result.isValid).toBe(true); expect(result.sanitizedPath?.startsWith('/Users/guybary')).toBe(true); }); }); describe('ATTACK 3: Special Characters', () => { it('should BLOCK null byte injection', () => { const result = validator.validate('/Users/guybary\x00/../../../etc'); expect(result.isValid).toBe(false); }); it('should BLOCK newline injection', () => { const result = validator.validate('/Users/guybary\n/../../../etc'); expect(result.isValid).toBe(false); }); it('should BLOCK tab character', () => { const result = validator.validate('/Users/guybary\t/../../../etc'); expect(result.isValid).toBe(false); }); it('should BLOCK carriage return', () => { const result = validator.validate('/Users/guybary\r/../../../etc'); expect(result.isValid).toBe(false); }); }); describe('ATTACK 4: Path Normalization Tricks', () => { it('should BLOCK multiple consecutive slashes', () => { const result = validator.validate('/Users////guybary/////..//..//etc'); expect(result.isValid).toBe(false); }); it('should BLOCK excessive ./.', () => { const result = validator.validate('/Users/./guybary/./././././../../etc'); expect(result.isValid).toBe(false); }); it('should BLOCK mixed redundant components', () => { const result = validator.validate('/Users/./guybary/./.././.././etc'); expect(result.isValid).toBe(false); }); }); describe('ATTACK 5: Case Sensitivity Bypass (macOS)', () => { it('should BLOCK uppercase USERS', () => { const result = validator.validate('/USERS/guybary/../../../etc'); expect(result.isValid).toBe(false); }); it('should BLOCK mixed case UsErS', () => { const result = validator.validate('/UsErS/GUYBARY/../../../etc'); expect(result.isValid).toBe(false); }); }); describe('ATTACK 6: Sibling Directory Bypass', () => { it('should BLOCK sibling with similar name', () => { const result = validator.validate('/Users/guybary2'); expect(result.isValid).toBe(false); expect(result.error).toContain('outside allowed directories'); }); it('should BLOCK sibling with extra characters', () => { const result = validator.validate('/Users/guybary_other'); expect(result.isValid).toBe(false); }); it('should BLOCK sibling with prefix match', () => { const result = validator.validate('/Users/guybaryextra/Documents'); expect(result.isValid).toBe(false); }); }); describe('ATTACK 7: Absolute System Paths', () => { const systemPaths = [ '/etc/passwd', '/usr/bin', '/tmp', '/var/log', '/root', '/System', '/Library' ]; systemPaths.forEach(systemPath => { it(`should BLOCK access to ${systemPath}`, () => { const result = validator.validate(systemPath); expect(result.isValid).toBe(false); expect(result.error).toContain('outside allowed directories'); }); }); }); describe('ATTACK 8: Home Directory Bypass', () => { it('should BLOCK parent of allowed root (/Users)', () => { const result = validator.validate('/Users'); expect(result.isValid).toBe(false); }); it('should BLOCK root directory (/)', () => { const result = validator.validate('/'); expect(result.isValid).toBe(false); }); it('should BLOCK other user home', () => { const result = validator.validate('/Users/root'); expect(result.isValid).toBe(false); }); }); describe('ATTACK 9: Unicode & Homoglyphs', () => { it('should BLOCK Unicode fraction slash (⁄)', () => { const result = validator.validate('/Users/guybary⁄..⁄..⁄etc'); expect(result.isValid).toBe(false); }); it('Full-width dots (..) - SAFE (not normalized to ASCII dots)', () => { // Unicode full-width dots are NOT normalized to ASCII dots by Node.js // They are kept as literal Unicode characters, creating a directory named ".." const result = validator.validate('/Users/guybary/../etc'); // This is ALLOWED because it stays within workspace expect(result.isValid).toBe(true); expect(result.sanitizedPath).toContain('.'); // Verify it doesn't escape workspace expect(result.sanitizedPath?.startsWith('/Users/guybary')).toBe(true); }); it('should BLOCK RTL override character', () => { const result = validator.validate('/Users/guybary\u202e/../../etc'); expect(result.isValid).toBe(false); }); }); describe('VALID PATHS - Should ALLOW', () => { it('should ALLOW valid workspace subdirectory', () => { const result = validator.validate('/Users/guybary/Documents'); expect(result.isValid).toBe(true); expect(result.error).toBeUndefined(); }); it('should ALLOW workspace root', () => { const result = validator.validate('/Users/guybary'); expect(result.isValid).toBe(true); expect(result.error).toBeUndefined(); }); it('should ALLOW deeply nested valid paths', () => { const result = validator.validate('/Users/guybary/Documents/projects/app/src/components'); expect(result.isValid).toBe(true); }); }); });

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