Skip to main content
Glama
validation.ts4.5 kB
/** * Session Validation Utilities * * Security-focused validation for session management including * UUID validation and path traversal protection. */ import { existsSync, realpathSync } from "fs"; import { resolve, normalize } from "path"; import { randomUUID } from "crypto"; import { ConfigurationError, ValidationError } from "../utils/errors.js"; import { getChildLogger } from "../utils/logger.js"; const logger = getChildLogger("session:validation"); /** * UUID v4 regex pattern * Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx * where y is one of [8, 9, a, b] */ const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; /** * Validate UUID v4 format */ export function validateUUID(uuid: string): boolean { return UUID_V4_REGEX.test(uuid); } /** * Validate session ID * Throws ValidationError if invalid */ export function validateSessionId(sessionId: string): void { if (!sessionId) { throw new ValidationError("Session ID cannot be empty"); } if (!validateUUID(sessionId)) { throw new ValidationError( `Invalid session ID format. Expected UUID v4, got: ${sessionId}`, ); } } /** * Enhanced project path validation with security checks */ export function validateSecureProjectPath(projectPath: string): void { if (!projectPath) { throw new ConfigurationError("Project path cannot be empty"); } // Resolve to absolute path const resolved = resolve(projectPath); // Normalize the path to remove . and .. const normalized = normalize(projectPath); // Check for path traversal attempts if (resolved.includes("..") || normalized.includes("..")) { throw new ConfigurationError( `Path contains traversal attempts: ${projectPath}`, ); } // Ensure path exists if (!existsSync(resolved)) { throw new ConfigurationError( `Project path does not exist: ${projectPath}`, ); } // Check for symlinks (warn but allow) try { const realPath = realpathSync(resolved); if (realPath !== resolved) { logger.warn({ original: projectPath, resolved, realPath, note: "Symlinks are allowed but may cause unexpected behavior", }, "Project path contains symlinks"); } } catch (error) { logger.error({ err: error instanceof Error ? error : new Error(String(error)), projectPath, }, "Failed to resolve real path"); } // Check for suspicious path patterns const suspiciousPatterns = [ "/etc/", "/usr/bin/", "/usr/sbin/", "/System/", "/Windows/", "/.ssh/", "/.gnupg/", ]; const lowerPath = resolved.toLowerCase(); for (const pattern of suspiciousPatterns) { if (lowerPath.includes(pattern.toLowerCase())) { throw new ConfigurationError( `Project path appears to be in a system directory: ${projectPath}`, ); } } } /** * Validate team name for safety */ export function validateTeamName(teamName: string): void { if (!teamName) { throw new ValidationError("Team name cannot be empty"); } // Check length if (teamName.length > 100) { throw new ValidationError("Team name exceeds maximum length of 100"); } // Check for dangerous characters const dangerousChars = ["../", "..\\", "\0", "|", "&", ";", "$", "`"]; for (const char of dangerousChars) { if (teamName.includes(char)) { throw new ValidationError( `Team name contains dangerous character: ${char}`, ); } } // Allow alphanumeric, dash, underscore, and basic punctuation const validPattern = /^[a-zA-Z0-9_\-@.]+$/; if (!validPattern.test(teamName)) { throw new ValidationError( `Team name contains invalid characters. Allowed: alphanumeric, dash, underscore, @, and period`, ); } } /** * Generate secure random UUID v4 */ export function generateSecureUUID(): string { const uuid = randomUUID(); // Double-check the generated UUID is valid if (!validateUUID(uuid)) { throw new Error("Failed to generate valid UUID"); } return uuid; } /** * Sanitize file path for safe storage */ export function sanitizePath(path: string): string { // Remove null bytes let sanitized = path.replace(/\0/g, ""); // Remove control characters sanitized = sanitized.replace(/[\x00-\x1f\x7f]/g, ""); // Normalize multiple slashes sanitized = sanitized.replace(/\/+/g, "/"); // Remove trailing slashes sanitized = sanitized.replace(/\/$/, ""); return sanitized; }

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/jenova-marie/iris-mcp'

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