Skip to main content
Glama
race-condition-attack.test.ts3.71 kB
/** * RACE CONDITION (TOCTOU) ATTACK TESTS * Time-Of-Check-Time-Of-Use vulnerabilities */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { validateExecutionContext } from '../../src/security/executionContextValidator.js'; import { PathValidator } from '../../src/security/pathValidator.js'; import fs from 'fs'; import path from 'path'; describe('RACE CONDITION ATTACKS (TOCTOU)', () => { // Use project directory to avoid macOS /private/ path being blocked by ignored patterns const workspace = path.join(__dirname, '.tmp_toctou_test'); let validator: PathValidator; beforeAll(() => { fs.mkdirSync(workspace, { recursive: true }); validator = new PathValidator(workspace); }); afterAll(() => { try { const files = fs.readdirSync(workspace); files.forEach((file) => { const filePath = path.join(workspace, file); try { if (fs.lstatSync(filePath).isSymbolicLink()) { fs.unlinkSync(filePath); } else if (fs.statSync(filePath).isDirectory()) { fs.rmdirSync(filePath); } else { fs.unlinkSync(filePath); } } catch { // Ignore cleanup errors } }); fs.rmdirSync(workspace); } catch { // Ignore cleanup errors } }); it('ATTACK: Swap file after validation (classic TOCTOU)', async () => { const testFile = path.join(workspace, 'toctou_test.txt'); fs.writeFileSync(testFile, 'safe content'); const result1 = validator.validate(testFile); expect(result1.isValid).toBe(true); // Swap with symlink (attacker action) fs.unlinkSync(testFile); fs.symlinkSync('/etc/passwd', testFile); // Re-validation should catch the swap const result2 = validator.validate(testFile); expect(result2.isValid).toBe(false); fs.unlinkSync(testFile); }); it('ATTACK: Race condition with directory swap', async () => { const testDir = path.join(workspace, 'toctou_dir'); fs.mkdirSync(testDir); const result1 = validator.validate(testDir); expect(result1.isValid).toBe(true); // Swap directory with symlink fs.rmdirSync(testDir); fs.symlinkSync('/etc', testDir); const result2 = validator.validate(testDir); expect(result2.isValid).toBe(false); fs.unlinkSync(testDir); }); it('SECURITY TEST: PathValidator uses realpath (safe against TOCTOU)', () => { const source = fs.readFileSync( path.join(__dirname, '../../src/security/pathValidator.ts'), 'utf-8' ); const usesRealpath = source.includes('realpathSync'); expect(usesRealpath).toBe(true); }); it('MITIGATION: Validate just before use (recommended pattern)', () => { // Pattern: validate() -> use file immediately // Pattern: Do NOT store validation results for later use // Pattern: Re-validate if time has passed expect(true).toBe(true); }); it('TOCTOU FIX: validator.validate does not crash if path disappears', () => { const tmp = path.join(workspace, 'maybe-missing'); try { fs.rmSync(tmp, { recursive: true, force: true }); } catch { /* ignore */ } const res = validator.validate(tmp); expect(res.isValid).toBe(true); expect(res.sanitizedPath).toBeDefined(); }); it('TOCTOU FIX: executionContextValidator allows non-existent cwd within workspace', () => { const tmp = path.join(workspace, 'missing-cwd'); try { fs.rmSync(tmp, { recursive: true, force: true }); } catch { /* ignore */ } const res = validateExecutionContext(tmp, workspace); expect(res.isValid).toBe(true); expect(res.sanitizedPath).toBeDefined(); }); });

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/octocode-mcp'

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