UNPKG

container-image-scanner

Version:

๐Ÿšจ EMERGENCY Bitnami Migration Scanner - Critical Timeline Aug 28/Sep 29, 2025. Enterprise scanner for 280+ Bitnami images, 118+ Helm charts with emergency migration automation to AWS alternatives.

411 lines (358 loc) โ€ข 12.9 kB
#!/usr/bin/env node /** * Container Image Scanner (CIS) - Production Post-Install Script * Comprehensive system validation, setup assistance, and getting started guide */ const chalk = require('chalk'); const { execSync, spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const os = require('os'); // Prevent issues in CI/CD environments if (process.env.CI || process.env.NODE_ENV === 'test') { console.log('Skipping post-install in CI/test environment'); process.exit(0); } // Error handling process.on('uncaughtException', (error) => { console.error(chalk.red('Post-install error:'), error.message); process.exit(0); // Don't fail npm install }); async function main() { console.log('\n' + '='.repeat(80)); console.log(chalk.bgGreen.black.bold(' ๐Ÿ” CONTAINER IMAGE SCANNER (CIS) v2.0 INSTALLED ')); console.log('='.repeat(80)); console.log(chalk.blue.bold('\n๐Ÿ“Š ENTERPRISE CONTAINER DEPENDENCY ANALYSIS PLATFORM')); console.log(chalk.gray('Bitnami migration planning โ€ข Security analysis โ€ข Cost optimization\n')); // System information displaySystemInfo(); // Check prerequisites console.log(chalk.yellow.bold('๐Ÿ”ง SYSTEM PREREQUISITES CHECK:\n')); const systemReady = await checkPrerequisites(); // AWS configuration check console.log(chalk.yellow.bold('\nโ˜๏ธ AWS CONFIGURATION CHECK:\n')); const awsReady = await checkAWSConfiguration(); // Display quick start guide displayQuickStart(); // Display use cases and examples displayUseCases(); // Final recommendations displayRecommendations(systemReady, awsReady); // Create sample config if needed await createSampleConfig(); console.log(chalk.green.bold('\nโœ… Installation complete! Run "cis --help" for full documentation\n')); } function displaySystemInfo() { console.log(chalk.cyan.bold('๐Ÿ’ป SYSTEM INFORMATION:\n')); console.log(chalk.gray(`Platform: ${os.platform()} ${os.arch()}`)); console.log(chalk.gray(`Node.js: ${process.version}`)); console.log(chalk.gray(`NPM: ${getNpmVersion()}`)); console.log(chalk.gray(`Install Path: ${__dirname}`)); console.log(chalk.gray(`User: ${os.userInfo().username}`)); console.log(); } function getNpmVersion() { try { return execSync('npm --version', { encoding: 'utf8', stdio: 'pipe' }).trim(); } catch { return 'Unknown'; } } async function checkPrerequisites() { const checks = [ { name: 'AWS CLI', command: 'aws --version', required: true, installHint: 'Install: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html' }, { name: 'kubectl', command: 'kubectl version --client --output=yaml', required: true, installHint: 'Install: https://kubernetes.io/docs/tasks/tools/' }, { name: 'Docker', command: 'docker --version', required: false, installHint: 'Install: https://docs.docker.com/get-docker/ (Required for migration scripts)' }, { name: 'Git', command: 'git --version', required: false, installHint: 'Install: https://git-scm.com/downloads (Required for IaC generation)' } ]; let allGood = true; for (const check of checks) { try { const result = execSync(check.command, { encoding: 'utf8', stdio: 'pipe', timeout: 5000 }); let version = 'Found'; if (check.name === 'AWS CLI') { const match = result.match(/aws-cli\/([^\s]+)/); version = match ? `v${match[1]}` : 'Found'; } else if (check.name === 'kubectl') { const match = result.match(/gitVersion:\s*"?v?([^"\s]+)"?/); version = match ? `v${match[1]}` : 'Found'; } else if (check.name === 'Docker') { const match = result.match(/Docker version ([^,\s]+)/); version = match ? `v${match[1]}` : 'Found'; } else if (check.name === 'Git') { const match = result.match(/git version ([^\s]+)/); version = match ? `v${match[1]}` : 'Found'; } console.log(chalk.green(`โœ… ${check.name}: ${version}`)); } catch (error) { const status = check.required ? chalk.red('โŒ') : chalk.yellow('โš ๏ธ'); const label = check.required ? 'REQUIRED' : 'OPTIONAL'; console.log(`${status} ${check.name}: Not found (${label})`); if (check.installHint) { console.log(chalk.gray(` ${check.installHint}`)); } if (check.required) { allGood = false; } } } return allGood; } async function checkAWSConfiguration() { let awsConfigured = false; try { // Check AWS credentials const identity = execSync('aws sts get-caller-identity', { encoding: 'utf8', stdio: 'pipe', timeout: 10000 }); const identityData = JSON.parse(identity); console.log(chalk.green(`โœ… AWS Identity: ${identityData.Arn || identityData.UserId}`)); console.log(chalk.green(`โœ… Account ID: ${identityData.Account}`)); // Check default region try { const region = execSync('aws configure get region', { encoding: 'utf8', stdio: 'pipe' }).trim(); console.log(chalk.green(`โœ… Default Region: ${region}`)); } catch { console.log(chalk.yellow('โš ๏ธ No default region set (recommended: aws configure set region us-east-1)')); } awsConfigured = true; } catch (error) { console.log(chalk.red('โŒ AWS credentials not configured')); console.log(chalk.gray(' Run: aws configure')); console.log(chalk.gray(' Or set environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY')); } // Check for EKS clusters (if AWS is configured) if (awsConfigured) { try { console.log(chalk.gray('\n๐Ÿ” Checking for EKS clusters...')); const clusters = execSync('aws eks list-clusters --output json', { encoding: 'utf8', stdio: 'pipe', timeout: 15000 }); const clusterData = JSON.parse(clusters); if (clusterData.clusters && clusterData.clusters.length > 0) { console.log(chalk.green(`โœ… Found ${clusterData.clusters.length} EKS cluster(s): ${clusterData.clusters.join(', ')}`)); } else { console.log(chalk.yellow('โš ๏ธ No EKS clusters found in default region')); } } catch (error) { console.log(chalk.yellow('โš ๏ธ Could not list EKS clusters (check permissions)')); } } return awsConfigured; } function displayQuickStart() { console.log(chalk.green.bold('\n๐Ÿš€ QUICK START GUIDE:\n')); const examples = [ { title: 'Scan single AWS account', command: 'cis analyze --accounts 123456789012 --regions us-east-1', description: 'Basic scan of one account in one region' }, { title: 'Scan AWS Organization', command: 'cis analyze --org-scan --role-arn "arn:aws:iam::*:role/ContainerScannerRole"', description: 'Enterprise-wide scan across all org accounts' }, { title: 'Bitnami migration analysis', command: 'cis bitnami-scan --output bitnami-analysis.json', description: 'Focus on Bitnami dependencies and migration planning' }, { title: 'Start web dashboard', command: 'cis ui --port 3000', description: 'Interactive web interface for analysis and reporting' }, { title: 'Setup cross-account access', command: 'cis setup-roles --accounts 123,456,789', description: 'Generate IAM roles for multi-account scanning' } ]; examples.forEach(example => { console.log(chalk.cyan(`# ${example.title}`)); console.log(chalk.white(`${example.command}`)); console.log(chalk.gray(` ${example.description}\n`)); }); } function displayUseCases() { console.log(chalk.blue.bold('๐Ÿ“‹ PRIMARY USE CASES:\n')); const useCases = [ { emoji: '๐Ÿ’ฐ', title: 'Bitnami Cost Avoidance', description: 'Identify $72K/year Bitnami licensing costs before Aug 28, 2025 deadline', action: 'cis bitnami-scan --critical-only' }, { emoji: '๐Ÿ”’', title: 'Security Vulnerability Analysis', description: 'Find containers with known CVEs and outdated base images', action: 'cis security-scan --severity high' }, { emoji: '๐Ÿ“Š', title: 'Dependency Mapping', description: 'Complete container dependency analysis and standardization', action: 'cis dependency-map --cluster my-cluster' }, { emoji: '๐ŸŽฏ', title: 'Migration Planning', description: 'Generate automated migration scripts for container registries', action: 'cis migrate --from bitnami --to ecr' }, { emoji: '๐Ÿ“ˆ', title: 'Cost Optimization', description: 'Analyze container licensing costs across cloud providers', action: 'cis cost-analysis --compare-registries' }, { emoji: '๐Ÿ”', title: 'Compliance Auditing', description: 'License compliance and governance reporting', action: 'cis compliance-report --format pdf' } ]; useCases.forEach(useCase => { console.log(`${useCase.emoji} ${chalk.bold(useCase.title)}`); console.log(` ${useCase.description}`); console.log(chalk.gray(` Example: ${useCase.action}\n`)); }); } function displayRecommendations(systemReady, awsReady) { console.log(chalk.magenta.bold('๐ŸŽฏ RECOMMENDED NEXT STEPS:\n')); if (!systemReady) { console.log(chalk.red('1. ๐Ÿ”ง Install missing prerequisites (AWS CLI, kubectl)')); console.log('2. ๐Ÿ“‹ Configure AWS credentials: aws configure'); console.log('3. ๐Ÿงช Test setup: cis doctor'); } else if (!awsReady) { console.log(chalk.yellow('1. ๐Ÿ“‹ Configure AWS credentials: aws configure')); console.log('2. ๐Ÿงช Test setup: cis doctor'); console.log('3. ๐Ÿ” Run first scan: cis analyze --accounts YOUR_ACCOUNT_ID'); } else { console.log(chalk.green('1. ๐Ÿ” Run first scan: cis analyze --accounts YOUR_ACCOUNT_ID')); console.log('2. ๐Ÿ’ฐ Check Bitnami exposure: cis bitnami-scan'); console.log('3. ๐Ÿ“Š Start web UI: cis ui'); console.log('4. ๐Ÿ“– Read documentation: cis --help'); } // Special Bitnami deadline warning const deadline = new Date('2025-08-28'); const now = new Date(); const daysLeft = Math.ceil((deadline - now) / (1000 * 60 * 60 * 24)); if (daysLeft > 0 && daysLeft < 365) { console.log(chalk.bgRed.white.bold(`\nโš ๏ธ URGENT: ${daysLeft} days until Bitnami free access ends (Aug 28, 2025)!`)); console.log(chalk.red(' Run: cis bitnami-scan --accounts ALL --urgent')); } } async function createSampleConfig() { const configPath = path.join(os.homedir(), '.cis-config.json'); try { // Don't overwrite existing config if (fs.existsSync(configPath)) { return; } const sampleConfig = { version: "2.0.0", defaults: { regions: ["us-east-1", "us-west-2"], output_format: "json", parallel_scans: 5, timeout: 300 }, bitnami: { deadline_warning: true, auto_migration_scripts: true, cost_analysis: true }, security: { vulnerability_scanning: true, cve_database_url: "https://cve.circl.lu/api/", severity_threshold: "medium" }, reporting: { include_charts: true, executive_summary: true, technical_details: true }, ui: { port: 3000, auto_open: true, theme: "dark" } }; await fs.promises.writeFile(configPath, JSON.stringify(sampleConfig, null, 2)); console.log(chalk.gray(`\n๐Ÿ“„ Created sample config: ${configPath}`)); console.log(chalk.gray(' Customize settings with: cis config edit')); } catch (error) { // Ignore config creation errors } } // Additional helper functions for better UX function checkDockerAccess() { try { execSync('docker ps', { stdio: 'pipe' }); return true; } catch { return false; } } function getPackageInfo() { try { const packagePath = path.join(__dirname, '..', 'package.json'); const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8')); return { version: packageData.version, description: packageData.description, homepage: packageData.homepage }; } catch { return { version: '2.0.0', description: 'Container Image Scanner' }; } } // Enhanced error handling for network issues function withTimeout(promise, ms) { return Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error('Operation timed out')), ms) ) ]); } // Run the main function main().catch(error => { console.error(chalk.red('Post-install script failed:'), error.message); process.exit(0); // Don't break npm install });