UNPKG

hayai-db

Version:

⚔ Instantly create and manage local databases with one command

338 lines (326 loc) • 14.5 kB
import { Command } from 'commander'; import chalk from 'chalk'; import inquirer from 'inquirer'; import { getSecurityManager } from '../../core/security.js'; async function initializeSecurity() { const securityManager = getSecurityManager(); console.log(chalk.cyan('\nšŸ”’ Hayai Security Configuration\n')); const answers = await inquirer.prompt([ { type: 'confirm', name: 'enableAudit', message: 'Enable audit logging for all operations?', default: true, }, { type: 'confirm', name: 'enableNetworkIsolation', message: 'Enable network isolation for sensitive operations?', default: false, }, { type: 'confirm', name: 'allowCrossEngine', message: 'Allow cross-engine operations (migrate/merge between different database types)?', default: true, }, { type: 'number', name: 'maxOperationsPerHour', message: 'Maximum operations per hour per user:', default: 50, validate: (input) => input > 0 || 'Must be greater than 0', }, { type: 'checkbox', name: 'allowedOperations', message: 'Select allowed operations:', choices: [ { name: 'Clone databases', value: 'clone', checked: true }, { name: 'Merge databases', value: 'merge', checked: true }, { name: 'Migrate databases', value: 'migrate', checked: true }, { name: 'Backup databases', value: 'backup', checked: true }, { name: 'Restore databases', value: 'restore', checked: true }, ], }, ]); const policy = { requireAuthentication: false, // Start with local development friendly settings allowCrossEngineOperations: answers.allowCrossEngine, enableNetworkIsolation: answers.enableNetworkIsolation, auditOperations: answers.enableAudit, maxOperationsPerHour: answers.maxOperationsPerHour, allowedOperations: answers.allowedOperations, }; await securityManager.saveSecurityPolicy(policy); console.log(chalk.green('\nāœ… Security policy configured successfully!')); console.log(chalk.gray('Policy saved to: .hayai/security.json')); if (policy.auditOperations) { console.log(chalk.gray('Audit logs will be saved to: .hayai/audit.log')); } if (policy.enableNetworkIsolation) { console.log(chalk.yellow('\nāš ļø Network isolation enabled:')); console.log(chalk.gray('• Operations will create isolated Docker networks')); console.log(chalk.gray('• This may slow down operations but increases security')); } } async function showSecurityPolicy() { const securityManager = getSecurityManager(); const policy = await securityManager.getSecurityPolicy(); console.log(chalk.cyan('\nšŸ”’ Current Security Policy\n')); console.log(chalk.bold('Authentication:')); console.log(` Required: ${policy.requireAuthentication ? chalk.red('Yes') : chalk.green('No')}`); console.log(chalk.bold('\nOperations:')); console.log(` Cross-engine operations: ${policy.allowCrossEngineOperations ? chalk.green('Allowed') : chalk.red('Blocked')}`); console.log(` Network isolation: ${policy.enableNetworkIsolation ? chalk.green('Enabled') : chalk.yellow('Disabled')}`); console.log(` Rate limit: ${chalk.cyan(policy.maxOperationsPerHour)} operations/hour`); console.log(chalk.bold('\nAllowed Operations:')); policy.allowedOperations.forEach(op => { console.log(` • ${chalk.green(op)}`); }); console.log(chalk.bold('\nAudit:')); console.log(` Logging: ${policy.auditOperations ? chalk.green('Enabled') : chalk.red('Disabled')}`); console.log(chalk.gray('\nšŸ’” Use `hayai security --init` to reconfigure')); } async function showAuditLogs() { try { const fs = await import('fs/promises'); const auditPath = '.hayai/audit.log'; const auditData = await fs.readFile(auditPath, 'utf8'); const logs = auditData.trim().split('\n').filter(line => line.trim()); console.log(chalk.cyan(`\nšŸ“‹ Audit Logs (${logs.length} entries)\n`)); if (logs.length === 0) { console.log(chalk.gray('No audit entries found')); return; } // Show last 20 entries const recentLogs = logs.slice(-20); recentLogs.forEach(logLine => { try { const log = JSON.parse(logLine); const timestamp = new Date(log.timestamp).toLocaleString(); const status = log.success ? chalk.green('āœ…') : chalk.red('āŒ'); const target = log.target ? ` → ${log.target}` : ''; console.log(`${status} ${chalk.gray(timestamp)} ${chalk.cyan(log.operation)} ${log.source}${target}`); if (log.error) { console.log(` ${chalk.red('Error:')} ${log.error}`); } } catch { console.log(chalk.gray(`Invalid log entry: ${logLine}`)); } }); if (logs.length > 20) { console.log(chalk.gray(`\n... and ${logs.length - 20} more entries`)); } } catch (error) { console.log(chalk.yellow('āš ļø No audit log file found')); console.log(chalk.gray('Enable audit logging with: hayai security --init')); } } async function manageCredentials() { const securityManager = getSecurityManager(); console.log(chalk.cyan('\nšŸ”‘ Credential Management\n')); const action = await inquirer.prompt([ { type: 'list', name: 'action', message: 'What would you like to do?', choices: [ { name: 'Generate secure password', value: 'generate' }, { name: 'View stored credentials (masked)', value: 'view' }, { name: 'Update instance credentials', value: 'update' }, { name: 'Remove instance credentials', value: 'remove' }, ], }, ]); switch (action.action) { case 'generate': { const length = await inquirer.prompt([ { type: 'number', name: 'length', message: 'Password length:', default: 16, validate: (input) => input >= 8 || 'Minimum length is 8 characters', }, ]); const password = securityManager.generateSecurePassword(length.length); console.log(chalk.green('\nšŸ”‘ Generated password:')); console.log(chalk.bold(password)); console.log(chalk.gray('\nšŸ’” Copy this password immediately - it won\'t be shown again')); break; } case 'view': { console.log(chalk.yellow('\nāš ļø This feature requires accessing the encrypted credential store')); console.log(chalk.gray('For security reasons, passwords are never displayed in plain text')); break; } case 'update': { const { instanceName, newPassword } = await inquirer.prompt([ { type: 'input', name: 'instanceName', message: 'Instance name:', validate: (input) => input.trim().length > 0 || 'Instance name is required', }, { type: 'password', name: 'newPassword', message: 'New password (leave empty to generate):', }, ]); const finalPassword = newPassword || securityManager.generateSecurePassword(); try { await securityManager.storeCredentials(instanceName, { username: 'admin', password: finalPassword, }); console.log(chalk.green(`\nāœ… Credentials updated for instance: ${instanceName}`)); if (!newPassword) { console.log(chalk.yellow('Generated password:'), chalk.bold(finalPassword)); } } catch (error) { console.error(chalk.red('āŒ Failed to update credentials:'), error); } break; } case 'remove': { console.log(chalk.red('\nāš ļø Credential removal not yet implemented')); console.log(chalk.gray('This feature will be added in a future version')); break; } } } async function handleSecurity(options) { if (options.init) { await initializeSecurity(); return; } if (options.policy) { await showSecurityPolicy(); return; } if (options.audit) { await showAuditLogs(); return; } if (options.credentials) { await manageCredentials(); return; } if (options.generate) { const securityManager = getSecurityManager(); const password = securityManager.generateSecurePassword(); console.log(chalk.green('\nšŸ”‘ Generated secure password:')); console.log(chalk.bold(password)); return; } // Default: Show security overview console.log(chalk.cyan('\nšŸ”’ Hayai Security Center\n')); const securityManager = getSecurityManager(); const policy = await securityManager.getSecurityPolicy(); console.log(chalk.bold('Security Status:')); // Check security settings const securityScore = calculateSecurityScore(policy); const scoreColor = securityScore >= 80 ? chalk.green : securityScore >= 60 ? chalk.yellow : chalk.red; console.log(` Security Score: ${scoreColor(securityScore)}/100`); console.log(` Audit Logging: ${policy.auditOperations ? chalk.green('āœ… Enabled') : chalk.red('āŒ Disabled')}`); console.log(` Network Isolation: ${policy.enableNetworkIsolation ? chalk.green('āœ… Enabled') : chalk.yellow('āš ļø Optional')}`); console.log(` Rate Limiting: ${chalk.cyan(policy.maxOperationsPerHour)} ops/hour`); console.log(chalk.bold('\nRecommendations:')); if (!policy.auditOperations) { console.log(chalk.red(' • Enable audit logging for compliance')); } if (!policy.enableNetworkIsolation && policy.allowCrossEngineOperations) { console.log(chalk.yellow(' • Consider network isolation for cross-engine operations')); } if (policy.maxOperationsPerHour > 100) { console.log(chalk.yellow(' • Consider lowering rate limits for production use')); } console.log(chalk.bold('\nAvailable Commands:')); console.log(` ${chalk.cyan('hayai security --init')} Configure security policy`); console.log(` ${chalk.cyan('hayai security --policy')} View current policy`); console.log(` ${chalk.cyan('hayai security --audit')} View audit logs`); console.log(` ${chalk.cyan('hayai security --credentials')} Manage credentials`); console.log(` ${chalk.cyan('hayai security --generate')} Generate secure password`); } function calculateSecurityScore(policy) { let score = 0; // Base score score += 20; // Audit logging if (policy.auditOperations) score += 25; // Network isolation if (policy.enableNetworkIsolation) score += 20; // Rate limiting if (policy.maxOperationsPerHour <= 50) score += 15; else if (policy.maxOperationsPerHour <= 100) score += 10; // Operation restrictions if (policy.allowedOperations.length <= 3) score += 10; else if (policy.allowedOperations.length <= 5) score += 5; // Cross-engine restrictions if (!policy.allowCrossEngineOperations) score += 10; return Math.min(score, 100); } export const securityCommand = new Command('security') .description('Configure and manage Hayai security settings') .option('--init', 'Initialize security configuration') .option('--policy', 'Show current security policy') .option('--audit', 'Show audit logs') .option('--credentials', 'Manage database credentials') .option('--generate', 'Generate a secure password') .option('--verbose', 'Enable verbose output') .addHelpText('after', ` ${chalk.bold('Security Features:')} ${chalk.cyan('šŸ” Credential Management:')} • Encrypted credential storage with AES-256-CBC • Secure password generation (16+ characters) • Per-instance credential isolation • No plain-text password exposure ${chalk.cyan('šŸ”’ Network Isolation:')} • Isolated Docker networks for operations • Container-to-container communication control • External network access restrictions ${chalk.cyan('šŸ“‹ Audit Logging:')} • All operations logged with timestamps • Success/failure tracking • User attribution and IP logging • Compliance-ready audit trails ${chalk.cyan('⚔ Rate Limiting:')} • Configurable operation limits per hour • Protection against abuse • User-based rate tracking ${chalk.bold('Examples:')} ${chalk.cyan('# Configure security for the first time')} hayai security --init ${chalk.cyan('# Check current security status')} hayai security ${chalk.cyan('# View recent audit logs')} hayai security --audit ${chalk.cyan('# Generate a secure password')} hayai security --generate ${chalk.cyan('# Manage database credentials')} hayai security --credentials ${chalk.bold('Security Best Practices:')} ${chalk.yellow('āš ļø Enable audit logging for production environments')} ${chalk.yellow('āš ļø Use network isolation for cross-engine operations')} ${chalk.yellow('āš ļø Regularly rotate database passwords')} ${chalk.yellow('āš ļø Monitor audit logs for suspicious activity')} ${chalk.yellow('āš ļø Backup .hayai directory securely (contains encrypted credentials)')} ${chalk.bold('Files & Directories:')} ${chalk.gray('.hayai/credentials.enc')} - Encrypted credentials storage ${chalk.gray('.hayai/security.json')} - Security policy configuration ${chalk.gray('.hayai/audit.log')} - Operation audit log ${chalk.gray('.hayai/.key')} - Encryption key (keep secure!) `) .action(handleSecurity);