Skip to main content
Glama
security.test.ts4.67 kB
import { describe, test, expect } from 'vitest'; import { normalizeAndValidatePath } from '../fs-utils.js'; import path from 'path'; describe('Security - Path Traversal Protection', () => { const testRoot = '/test/root'; // Common path traversal attacks const attacks = [ { path: '../../../etc/passwd', name: 'basic ../ traversal' }, { path: '..\\..\\windows\\system32', name: 'Windows backslash traversal' }, { path: '/etc/passwd', name: 'absolute path attack' }, { path: 'C:\\Windows\\System32', name: 'Windows absolute path' }, { path: '%2e%2e%2f', name: 'URL encoded traversal' }, { path: '....///', name: 'multiple dots and slashes' }, { path: '/root/../etc/passwd', name: 'absolute with traversal' }, { path: './././../../../etc/passwd', name: 'complex traversal' }, { path: 'foo/../../../bar', name: 'traversal in middle' }, { path: '..', name: 'simple parent directory' }, { path: '../', name: 'parent directory with slash' }, { path: '\\..\\..\\', name: 'Windows style parent' }, { path: 'test\x00.txt', name: 'null byte injection' }, { path: '...', name: 'triple dots' }, { path: '....//', name: 'four dots with slashes' }, { path: '../.../...', name: 'mixed dots pattern' } ]; attacks.forEach(({ path: attackPath, name }) => { test(`blocks ${name}: ${attackPath}`, () => { expect(() => { normalizeAndValidatePath(testRoot, attackPath); }).toThrow('Path traversal detected'); }); }); // Valid paths that should be allowed const validPaths = [ { path: '.', expected: testRoot }, { path: '', expected: testRoot }, { path: 'file.txt', expected: path.join(testRoot, 'file.txt') }, { path: './file.txt', expected: path.join(testRoot, 'file.txt') }, { path: 'subdir/file.txt', expected: path.join(testRoot, 'subdir/file.txt') }, { path: './subdir/./file.txt', expected: path.join(testRoot, 'subdir/file.txt') }, { path: 'deep/nested/path/file.txt', expected: path.join(testRoot, 'deep/nested/path/file.txt') }, { path: 'file with spaces.txt', expected: path.join(testRoot, 'file with spaces.txt') }, { path: 'файл.txt', expected: path.join(testRoot, 'файл.txt') }, // Unicode { path: '测试.txt', expected: path.join(testRoot, '测试.txt') }, // Chinese { path: '..file.txt', expected: path.join(testRoot, '..file.txt') }, // Starts with dots but not traversal { path: 'dir.with.dots/file.txt', expected: path.join(testRoot, 'dir.with.dots/file.txt') } ]; validPaths.forEach(({ path: validPath, expected }) => { test(`allows valid path: ${validPath}`, () => { const result = normalizeAndValidatePath(testRoot, validPath); expect(result).toBe(expected); }); }); // Edge cases test('handles Windows-style paths on Unix', () => { const windowsPath = 'subdir\\file.txt'; const result = normalizeAndValidatePath(testRoot, windowsPath); // On Unix, backslashes are valid filename characters if (process.platform === 'win32') { expect(result).toBe(path.join(testRoot, 'subdir', 'file.txt')); } else { expect(result).toBe(path.join(testRoot, 'subdir\\file.txt')); } }); test('prevents escape via symlinks', () => { // This is a conceptual test - actual symlink testing would require fs mocking const symlinkPath = 'symlink-to-parent/../secret'; expect(() => { normalizeAndValidatePath(testRoot, symlinkPath); }).toThrow('Path traversal detected'); }); test('handles very long paths', () => { const longPath = 'a/'.repeat(100) + 'file.txt'; const result = normalizeAndValidatePath(testRoot, longPath); expect(result).toContain(testRoot); expect(result).toContain('file.txt'); }); test('handles paths with multiple slashes', () => { const result = normalizeAndValidatePath(testRoot, 'dir///subdir//file.txt'); expect(result).toBe(path.join(testRoot, 'dir/subdir/file.txt')); }); test('handles trailing slashes', () => { const result = normalizeAndValidatePath(testRoot, 'dir/subdir/'); expect(result).toBe(path.join(testRoot, 'dir/subdir')); }); // Additional security considerations test('blocks hidden file escape attempts', () => { expect(() => { normalizeAndValidatePath(testRoot, './../etc/passwd'); }).toThrow('Path traversal detected'); }); test('blocks case variation attacks', () => { const caseAttacks = ['../ETC/PASSWD', '../Etc/Passwd', '../eTc/pAsSwD']; caseAttacks.forEach(attack => { expect(() => { normalizeAndValidatePath(testRoot, attack); }).toThrow('Path traversal detected'); }); }); });

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/davstr1/peekabooMCP'

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