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
JavaScript
/**
* 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