Skip to main content
Glama
safety.ts3.55 kB
export type RiskLevel = 'high' | 'low'; export interface SafetyValidationResult { isValid: boolean; riskLevel: RiskLevel; warnings: string[]; sanitizedCommand?: string; error?: string; } const DESTRUCTIVE_KEYWORDS = new Set(['delete', 'remove', 'purge', 'destroy', 'drop', 'wipe', 'clear']); const VALID_AZ_PREFIXES = ['az ', 'az.cmd ', 'az.exe ']; const INJECTION_PATTERNS = new Map<RegExp, string>([ [/[;&|`$]/, 'Command chaining detected'], [/\$\(.*\)/, 'Command substitution detected'], [/`.*`/, 'Backtick substitution detected'], [/>\s*\//, 'Redirect to root path detected'], [/\|\s*(?:sh|bash|cmd|powershell)/i, 'Pipe to shell detected'], [/(?:^|\s)(?:rm|del|format|shutdown|reboot)\s/i, 'Dangerous system command'], ]); export function isValidAzureCommand(cmd: string): boolean { const lower = cmd.trim().toLowerCase(); return VALID_AZ_PREFIXES.some(p => lower.startsWith(p)); } export function isDestructiveCommand(cmd: string): boolean { const lower = cmd.toLowerCase(); for (const kw of DESTRUCTIVE_KEYWORDS) { if (new RegExp(`\\b${kw}\\b`, 'i').test(lower)) return true; } return false; } export function assessRiskLevel(cmd: string): RiskLevel { if (isDestructiveCommand(cmd)) return 'high'; return 'low'; } function detectInjection(cmd: string): string[] { const detected: string[] = []; for (const [pattern, msg] of INJECTION_PATTERNS) { if (pattern.test(cmd)) detected.push(msg); } return detected; } export function sanitizeInput(command: string): SafetyValidationResult { const warnings: string[] = []; if (!command?.trim()) { return { isValid: false, riskLevel: 'low', warnings: [], error: 'Command cannot be empty' }; } const cmd = command.trim(); if (!isValidAzureCommand(cmd)) { return { isValid: false, riskLevel: 'high', warnings: [], error: 'Must be a valid Azure CLI command starting with "az"' }; } const injections = detectInjection(cmd); if (injections.length) { return { isValid: false, riskLevel: 'high', warnings: injections, error: 'Command contains dangerous patterns' }; } const riskLevel = assessRiskLevel(cmd); if (riskLevel === 'high') { warnings.push('⚠️ DESTRUCTIVE: Will permanently delete resources'); warnings.push('⚠️ This action cannot be undone'); } if (cmd.includes('--yes') || cmd.includes('-y')) { warnings.push('Auto-confirm flag detected'); } if (cmd.includes('--no-wait')) { warnings.push('Async operation flag detected'); } return { isValid: true, riskLevel, warnings, sanitizedCommand: cmd }; } export function generateCommandSummary(command: string): string { const parts = command.trim().split(/\s+/); if (parts[0]?.toLowerCase().startsWith('az')) parts.shift(); const service = parts[0] || 'unknown'; const action = parts[1] || 'unknown'; const params = new Map<string, string>(); for (let i = 0; i < parts.length; i++) { if ((parts[i] === '--name' || parts[i] === '-n') && parts[i + 1]) { params.set('name', parts[i + 1]); } if ((parts[i] === '--resource-group' || parts[i] === '-g') && parts[i + 1]) { params.set('rg', parts[i + 1]); } } let summary = `${action} ${service} resource`; if (params.get('name')) summary += ` "${params.get('name')}"`; if (params.get('rg')) summary += ` in RG "${params.get('rg')}"`; return summary; }

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/vedantparmar12/Azure-_MCP'

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