UNPKG

vaultace-cli

Version:

AI-powered security scanner that detects vulnerabilities in AI-generated code. Proactive scanning, autonomous fixing, and emergency response for modern development teams.

353 lines (287 loc) • 11.1 kB
/** * API Keys Command - API key management */ const { Command } = require('commander') const chalk = require('chalk') const ora = require('ora') const inquirer = require('inquirer') const { table } = require('table') const logger = require('../utils/logger') const APIClient = require('../services/api-client') const apiKeysCommand = new Command('api-keys') .description('šŸ”‘ Manage API keys for integrations') // List API keys apiKeysCommand .command('list') .description('List all API keys') .option('--show-keys', 'show actual key values (use with caution)') .action(async (options) => { const spinner = ora('Fetching API keys...').start() try { logger.command('api-keys list', { showKeys: options.showKeys }, true) logger.security('API keys list accessed', { show_keys: !!options.showKeys }) const apiClient = new APIClient() const response = await apiClient.get('/api/v1/api-keys') spinner.succeed('API keys retrieved') const keys = response.data.api_keys || [] console.log(chalk.bold.blue('\nšŸ”‘ API Keys\n')) if (keys.length === 0) { console.log(chalk.gray('No API keys found')) console.log(chalk.blue('Create one with: vaultace api-keys create')) return } const keyData = [ ['Name', 'Key', 'Permissions', 'Created', 'Last Used', 'Status'] ] keys.forEach(key => { const keyValue = options.showKeys ? key.key : `${key.key.substring(0, 8)}...` const status = key.is_active ? '🟢 Active' : 'šŸ”“ Inactive' keyData.push([ key.name, keyValue, key.permissions.join(', '), new Date(key.created_at).toLocaleDateString(), key.last_used ? new Date(key.last_used).toLocaleDateString() : 'Never', status ]) }) console.log(table(keyData)) if (options.showKeys) { console.log(chalk.red('\nāš ļø API keys are sensitive - keep them secure!')) } console.log(chalk.gray(`\nTotal: ${keys.length} API keys`)) } catch (error) { spinner.fail('Failed to fetch API keys') logger.error('API keys list error', { error: error.message }) if (error.response?.status === 401) { console.log(chalk.yellow('\nAuthentication required. Run: vaultace auth login')) } else { console.error(chalk.red(`Error: ${error.message}`)) } process.exit(1) } }) // Create new API key apiKeysCommand .command('create') .description('Create new API key') .argument('[name]', 'API key name') .option('--permissions <perms...>', 'key permissions (scan|fix|read|write)', ['scan', 'read']) .option('--expires <days>', 'expiration in days (0 for no expiration)', '0') .action(async (name, options) => { try { logger.command('api-keys create', { name, permissions: options.permissions }, true) let keyName = name if (!keyName) { const { inputName } = await inquirer.prompt([ { type: 'input', name: 'inputName', message: 'API key name:', validate: (input) => input.length > 0 || 'Name is required' } ]) keyName = inputName } const availablePermissions = ['scan', 'fix', 'read', 'write', 'admin'] const { selectedPermissions } = await inquirer.prompt([ { type: 'checkbox', name: 'selectedPermissions', message: 'Select permissions:', choices: availablePermissions, default: options.permissions } ]) const { confirm } = await inquirer.prompt([ { type: 'confirm', name: 'confirm', message: `Create API key "${keyName}" with permissions: ${selectedPermissions.join(', ')}?`, default: true } ]) if (!confirm) { console.log(chalk.yellow('API key creation cancelled')) return } const spinner = ora('Creating API key...').start() const apiClient = new APIClient() const response = await apiClient.post('/api/v1/api-keys', { name: keyName, permissions: selectedPermissions, expires_in_days: parseInt(options.expires) || null }) spinner.succeed('API key created successfully') logger.security('API key created', { name: keyName, permissions: selectedPermissions }) console.log(chalk.green('\nāœ… API Key Created')) console.log(chalk.bold('Name:'), keyName) console.log(chalk.bold('Key:'), chalk.yellow(response.data.api_key)) console.log(chalk.bold('Permissions:'), selectedPermissions.join(', ')) if (response.data.expires_at) { console.log(chalk.bold('Expires:'), new Date(response.data.expires_at).toLocaleDateString()) } console.log(chalk.red('\nāš ļø Save this key securely - it won\'t be shown again!')) } catch (error) { if (error.spinner) {error.spinner.fail('Failed to create API key')} logger.error('API key create error', { error: error.message, name }) console.error(chalk.red(`Error: ${error.message}`)) process.exit(1) } }) // Revoke API key apiKeysCommand .command('revoke') .description('Revoke API key') .argument('<key-id>', 'API key ID or name') .option('--force', 'skip confirmation prompt') .action(async (keyId, options) => { try { logger.command('api-keys revoke', { keyId }, true) if (!options.force) { const { confirm } = await inquirer.prompt([ { type: 'confirm', name: 'confirm', message: `Revoke API key "${keyId}"? This cannot be undone.`, default: false } ]) if (!confirm) { console.log(chalk.yellow('Revocation cancelled')) return } } const spinner = ora('Revoking API key...').start() const apiClient = new APIClient() await apiClient.delete(`/api/v1/api-keys/${keyId}`) spinner.succeed('API key revoked') logger.security('API key revoked', { key_id: keyId }) console.log(chalk.green(`\nāœ… API key "${keyId}" revoked`)) } catch (error) { if (error.spinner) {error.spinner.fail('Failed to revoke API key')} logger.error('API key revoke error', { error: error.message, keyId }) if (error.response?.status === 404) { console.error(chalk.red('API key not found')) } else { console.error(chalk.red(`Error: ${error.message}`)) } process.exit(1) } }) // Update API key apiKeysCommand .command('update') .description('Update API key permissions or name') .argument('<key-id>', 'API key ID or name') .option('--name <name>', 'new name for the key') .option('--permissions <perms...>', 'new permissions') .option('--activate', 'activate the key') .option('--deactivate', 'deactivate the key') .action(async (keyId, options) => { try { logger.command('api-keys update', { keyId, options }, true) const updateData = {} if (options.name) { updateData.name = options.name } if (options.permissions) { updateData.permissions = options.permissions } if (options.activate) { updateData.is_active = true } else if (options.deactivate) { updateData.is_active = false } if (Object.keys(updateData).length === 0) { console.log(chalk.yellow('No updates specified')) console.log('Use --name, --permissions, --activate, or --deactivate') return } const { confirm } = await inquirer.prompt([ { type: 'confirm', name: 'confirm', message: `Update API key "${keyId}"?`, default: true } ]) if (!confirm) { console.log(chalk.yellow('Update cancelled')) return } const spinner = ora('Updating API key...').start() const apiClient = new APIClient() await apiClient.patch(`/api/v1/api-keys/${keyId}`, updateData) spinner.succeed('API key updated') logger.security('API key updated', { key_id: keyId, updates: Object.keys(updateData) }) console.log(chalk.green(`\nāœ… API key "${keyId}" updated`)) } catch (error) { if (error.spinner) {error.spinner.fail('Failed to update API key')} logger.error('API key update error', { error: error.message, keyId }) if (error.response?.status === 404) { console.error(chalk.red('API key not found')) } else { console.error(chalk.red(`Error: ${error.message}`)) } process.exit(1) } }) // Show API key usage apiKeysCommand .command('usage') .description('Show API key usage statistics') .argument('[key-id]', 'API key ID (shows all if not specified)') .option('--days <number>', 'number of days to show', '30') .action(async (keyId, options) => { const spinner = ora('Fetching usage statistics...').start() try { logger.command('api-keys usage', { keyId, days: options.days }, true) const apiClient = new APIClient() const endpoint = keyId ? `/api/v1/api-keys/${keyId}/usage` : '/api/v1/api-keys/usage' const response = await apiClient.get(endpoint, { params: { days: parseInt(options.days) } }) spinner.succeed('Usage statistics retrieved') const usage = response.data console.log(chalk.bold.cyan('\nšŸ“Š API Key Usage\n')) if (keyId) { // Single key usage console.log(chalk.bold('Key:'), usage.key_name) console.log(chalk.bold('Total Requests:'), usage.total_requests || 0) console.log(chalk.bold('Successful:'), usage.successful_requests || 0) console.log(chalk.bold('Failed:'), usage.failed_requests || 0) console.log(chalk.bold('Rate Limited:'), usage.rate_limited || 0) console.log(chalk.bold('Last Used:'), usage.last_used || 'Never') if (usage.daily_usage && usage.daily_usage.length > 0) { console.log(chalk.bold('\nšŸ“ˆ Daily Usage:')) usage.daily_usage.slice(-7).forEach(day => { console.log(` ${day.date}: ${day.requests} requests`) }) } } else { // All keys summary const usageData = [ ['Key Name', 'Requests', 'Success Rate', 'Last Used'] ] usage.keys.forEach(key => { const successRate = key.total_requests > 0 ? Math.round((key.successful_requests / key.total_requests) * 100) + '%' : 'N/A' usageData.push([ key.name, key.total_requests || 0, successRate, key.last_used || 'Never' ]) }) console.log(table(usageData)) } } catch (error) { spinner.fail('Failed to fetch usage statistics') logger.error('API key usage error', { error: error.message, keyId }) console.error(chalk.red(`Error: ${error.message}`)) process.exit(1) } }) module.exports = apiKeysCommand