Skip to main content
Glama

automator-mcp

security.js8.63 kB
// 🛡️ Security Layer - Because with great power comes great responsibility // We don't want Claude sending emails to your ex at 3am export class SecurityManager { constructor() { this.whitelist = new Set(); this.blacklist = new Set(); this.permissions = new Map(); this.auditLog = []; this.rateLimits = new Map(); } // Initialize security settings async initialize() { // Load from config file if exists try { const configPath = `${process.env.HOME}/.automator-mcp/security.json`; const config = await fs.readFile(configPath, 'utf8'); const settings = JSON.parse(config); this.whitelist = new Set(settings.whitelist || []); this.blacklist = new Set(settings.blacklist || []); this.permissions = new Map(settings.permissions || []); } catch (error) { // Use defaults if no config this.setDefaultPermissions(); } } // Default safe permissions setDefaultPermissions() { this.permissions.set('email', { enabled: true, requireConfirmation: true, allowedDomains: [], maxPerDay: 10 }); this.permissions.set('file_system', { enabled: true, allowedPaths: [ `${process.env.HOME}/Desktop`, `${process.env.HOME}/Documents`, `${process.env.HOME}/Downloads` ], forbiddenPaths: [ `${process.env.HOME}/.ssh`, `${process.env.HOME}/.gnupg`, '/System', '/Library' ] }); this.permissions.set('applications', { enabled: true, allowedApps: [ 'Finder', 'Safari', 'Mail', 'Calendar', 'Notes', 'Preview' ], forbiddenApps: [ 'System Preferences', 'Terminal', 'Activity Monitor' ] }); } // Check if action is allowed async checkPermission(action, details) { // Log all attempts this.auditLog.push({ timestamp: new Date(), action, details, allowed: null }); // Check rate limits if (!this.checkRateLimit(action)) { this.auditLog[this.auditLog.length - 1].allowed = false; throw new Error(`Rate limit exceeded for ${action}`); } // Check specific permissions switch (action) { case 'send_email': return this.checkEmailPermission(details); case 'file_operation': return this.checkFilePermission(details); case 'run_application': return this.checkApplicationPermission(details); case 'execute_script': return this.checkScriptPermission(details); default: return this.checkGenericPermission(action, details); } } // Email-specific security async checkEmailPermission(details) { const emailPerms = this.permissions.get('email'); if (!emailPerms.enabled) { throw new Error('Email sending is disabled'); } // Check blacklist if (this.blacklist.has(details.to)) { throw new Error(`Email to ${details.to} is blocked`); } // Check whitelist (if configured) if (this.whitelist.size > 0 && !this.whitelist.has(details.to)) { throw new Error(`Email to ${details.to} is not whitelisted`); } // Check domain restrictions if (emailPerms.allowedDomains.length > 0) { const domain = details.to.split('@')[1]; if (!emailPerms.allowedDomains.includes(domain)) { throw new Error(`Domain ${domain} is not allowed`); } } // Require confirmation for first-time recipients if (emailPerms.requireConfirmation && !this.hasEmailedBefore(details.to)) { return { allowed: true, requiresConfirmation: true, message: `First time emailing ${details.to}. Please confirm.` }; } return { allowed: true }; } // File system security checkFilePermission(details) { const filePerms = this.permissions.get('file_system'); if (!filePerms.enabled) { throw new Error('File operations are disabled'); } const path = details.path; // Check forbidden paths for (const forbidden of filePerms.forbiddenPaths) { if (path.startsWith(forbidden)) { throw new Error(`Access to ${path} is forbidden`); } } // Check allowed paths const inAllowedPath = filePerms.allowedPaths.some(allowed => path.startsWith(allowed) ); if (!inAllowedPath) { throw new Error(`Path ${path} is not in allowed directories`); } return { allowed: true }; } // Application security checkApplicationPermission(details) { const appPerms = this.permissions.get('applications'); if (!appPerms.enabled) { throw new Error('Application control is disabled'); } const appName = details.application; if (appPerms.forbiddenApps.includes(appName)) { throw new Error(`Application ${appName} is forbidden`); } if (appPerms.allowedApps.length > 0 && !appPerms.allowedApps.includes(appName)) { throw new Error(`Application ${appName} is not whitelisted`); } return { allowed: true }; } // Script execution security checkScriptPermission(details) { const script = details.script; // Dangerous patterns to block const dangerousPatterns = [ /rm\s+-rf\s+\//, // Recursive delete from root /sudo/, // Sudo commands /passwd/, // Password changes /ssh\s+/, // SSH connections /curl.*\|.*sh/, // Curl pipe to shell />\/dev\/sda/, // Direct disk writes /dd\s+if=/, // DD commands /mkfs/, // Format filesystem ]; for (const pattern of dangerousPatterns) { if (pattern.test(script)) { throw new Error('Script contains dangerous commands'); } } return { allowed: true }; } // Rate limiting checkRateLimit(action) { const key = `${action}:${new Date().toISOString().split('T')[0]}`; const current = this.rateLimits.get(key) || 0; const limits = { send_email: 10, file_operation: 100, run_application: 50, execute_script: 30 }; const limit = limits[action] || 50; if (current >= limit) { return false; } this.rateLimits.set(key, current + 1); return true; } // Check if we've emailed this address before hasEmailedBefore(address) { return this.auditLog.some(log => log.action === 'send_email' && log.details.to === address && log.allowed === true ); } // Generic permission check checkGenericPermission(action, details) { // For unknown actions, be conservative return { allowed: true, requiresConfirmation: true, message: `Unknown action ${action}. Please confirm.` }; } // Get audit log getAuditLog(filter = {}) { let logs = [...this.auditLog]; if (filter.action) { logs = logs.filter(log => log.action === filter.action); } if (filter.startDate) { logs = logs.filter(log => log.timestamp >= filter.startDate); } if (filter.allowed !== undefined) { logs = logs.filter(log => log.allowed === filter.allowed); } return logs; } // Add to whitelist addToWhitelist(item) { this.whitelist.add(item); this.saveConfig(); } // Add to blacklist addToBlacklist(item) { this.blacklist.add(item); this.saveConfig(); } // Save configuration async saveConfig() { const config = { whitelist: Array.from(this.whitelist), blacklist: Array.from(this.blacklist), permissions: Array.from(this.permissions.entries()) }; const configPath = `${process.env.HOME}/.automator-mcp/security.json`; await fs.mkdir(path.dirname(configPath), { recursive: true }); await fs.writeFile(configPath, JSON.stringify(config, null, 2)); } } // Export singleton export const security = new SecurityManager(); // 🔐 Security decorators for actions export function requirePermission(action) { return function(target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = async function(...args) { const permission = await security.checkPermission(action, args[0]); if (!permission.allowed) { throw new Error(`Permission denied for ${action}`); } if (permission.requiresConfirmation) { // In real implementation, this would trigger a UI confirmation console.log(`⚠️ ${permission.message}`); } return originalMethod.apply(this, args); }; return descriptor; }; }

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/Szowesgad/automator-mcp'

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