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
JavaScript
/**
* 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
});