container-image-scanner
Version:
Enterprise Container Image Scanner with AWS Security Best Practices. Scan EKS clusters for Bitnami container image dependencies and generate migration guidance for AWS ECR 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
});