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.
291 lines (246 loc) ⢠9.99 kB
JavaScript
const { execSync } = require('child_process');
const chalk = require('chalk');
class EnhancedAnalyzer {
constructor() {
this.kubectlAvailable = this.checkKubectl();
this.helmAvailable = this.checkHelm();
}
checkKubectl() {
try {
execSync('kubectl version --client', { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
checkHelm() {
try {
execSync('helm version', { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
analyzeImageVersioning(imageName) {
const [name, tag] = imageName.split(':');
const tagValue = tag || 'latest';
return {
imageName: name,
tag: tagValue,
isLatest: tagValue === 'latest',
isPinned: tagValue !== 'latest' && /^\d+\.\d+/.test(tagValue),
isSemver: /^\d+\.\d+\.\d+/.test(tagValue),
updateFrequency: tagValue === 'latest' ? 'frequent' : 'regular'
};
}
detectHelmDeployments() {
if (this.helmAvailable) {
try {
const output = execSync('helm list --all-namespaces', { encoding: 'utf8' });
return {
isHelmManaged: output.trim().length > 0,
releases: output.split('\n').length - 1
};
} catch {
return { isHelmManaged: false, releases: 0 };
}
}
if (this.kubectlAvailable) {
try {
const output = execSync('kubectl get all --all-namespaces -o json', { encoding: 'utf8' });
const hasHelm = output.includes('helm.sh') || output.includes('Helm');
return { isHelmManaged: hasHelm, releases: hasHelm ? 1 : 0 };
} catch {
return { isHelmManaged: false, releases: 0 };
}
}
return { isHelmManaged: false, releases: 0 };
}
analyzeContainerSecurity(imageName) {
const security = {
hasSecurityContext: false,
runAsNonRoot: false,
readOnlyRootFilesystem: false,
hasResourceLimits: false
};
if (!this.kubectlAvailable) return security;
try {
// Check for security contexts in deployments using this image
const output = execSync('kubectl get deployments --all-namespaces -o json', { encoding: 'utf8' });
const deployments = JSON.parse(output);
deployments.items.forEach(deployment => {
const containers = deployment.spec?.template?.spec?.containers || [];
containers.forEach(container => {
if (container.image && container.image.includes(imageName.split(':')[0])) {
if (container.securityContext) {
security.hasSecurityContext = true;
if (container.securityContext.runAsNonRoot) security.runAsNonRoot = true;
if (container.securityContext.readOnlyRootFilesystem) security.readOnlyRootFilesystem = true;
}
if (container.resources?.limits) security.hasResourceLimits = true;
}
});
});
} catch {
// Unable to analyze security
}
return security;
}
generateReport(imageName) {
const versionAnalysis = this.analyzeImageVersioning(imageName);
const helmDeployment = this.detectHelmDeployments();
const containerSecurity = this.analyzeContainerSecurity(imageName);
const recommendations = [];
if (versionAnalysis.isLatest) {
recommendations.push('ā
GOOD: Using "latest" tag - no breaking changes expected from Broadcom transition');
} else if (versionAnalysis.isPinned) {
recommendations.push('šØ CRITICAL: Pinned version may break - Broadcom removing free Bitnami images');
recommendations.push('š MIGRATION REQUIRED: Switch to AWS managed service or upstream alternative');
} else {
recommendations.push('ā ļø HIGH: Semver tags at risk - may break when Broadcom removes free access');
recommendations.push('š MIGRATION RECOMMENDED: Plan transition to avoid service disruption');
}
if (helmDeployment.isHelmManaged) {
recommendations.push('ā
Good: Helm-managed deployment - easier migration to alternatives');
} else {
recommendations.push('š Consider migration complexity for direct manifest deployments');
}
if (!containerSecurity.hasSecurityContext) {
recommendations.push('š Add security contexts for production readiness');
}
return {
imageName,
timestamp: new Date().toISOString(),
versionAnalysis,
helmDeployment,
containerSecurity,
recommendations,
toolsAvailable: {
kubectl: this.kubectlAvailable,
helm: this.helmAvailable
}
};
}
displayReport(report) {
console.log(chalk.bold.blue(`\nš¦ Enhanced Analysis: ${report.imageName}`));
console.log(chalk.gray(`Analyzed: ${new Date(report.timestamp).toLocaleString()}`));
// Version Analysis
console.log(chalk.bold('\nš·ļø Version Analysis:'));
console.log(` Tag: ${report.versionAnalysis.tag}`);
console.log(` Latest: ${report.versionAnalysis.isLatest ? chalk.red('Yes') : chalk.green('No')}`);
console.log(` Pinned: ${report.versionAnalysis.isPinned ? chalk.green('Yes') : chalk.yellow('No')}`);
console.log(` Semver: ${report.versionAnalysis.isSemver ? chalk.green('Yes') : chalk.gray('No')}`);
console.log(` Update Frequency: ${chalk.blue(report.versionAnalysis.updateFrequency)}`);
// Helm Information
console.log(chalk.bold('\nāļø Helm Deployment:'));
if (report.helmDeployment.isHelmManaged) {
console.log(chalk.green(` ā
Helm-managed (${report.helmDeployment.releases} releases)`));
} else {
console.log(chalk.yellow(' ā ļø Not Helm-managed (direct manifest)'));
}
// Container Security
console.log(chalk.bold('\nš Container Security:'));
console.log(` Security Context: ${report.containerSecurity.hasSecurityContext ? chalk.green('Yes') : chalk.yellow('No')}`);
console.log(` Non-Root User: ${report.containerSecurity.runAsNonRoot ? chalk.green('Yes') : chalk.yellow('No')}`);
console.log(` Read-Only Root FS: ${report.containerSecurity.readOnlyRootFilesystem ? chalk.green('Yes') : chalk.yellow('No')}`);
console.log(` Resource Limits: ${report.containerSecurity.hasResourceLimits ? chalk.green('Yes') : chalk.yellow('No')}`);
// Tools Available
console.log(chalk.bold('\nš§ Tools Available:'));
console.log(` kubectl: ${report.toolsAvailable.kubectl ? chalk.green('Yes') : chalk.red('No')}`);
console.log(` helm: ${report.toolsAvailable.helm ? chalk.green('Yes') : chalk.red('No')}`);
// Recommendations
if (report.recommendations.length > 0) {
console.log(chalk.bold('\nš” Recommendations:'));
report.recommendations.forEach(rec => {
console.log(` ${rec}`);
});
}
console.log(chalk.gray('\n' + 'ā'.repeat(60)));
}
}
// Main execution
if (require.main === module) {
const analyzer = new EnhancedAnalyzer();
const testImages = [
'bitnami/mysql:latest',
'bitnami/mysql:8.0.35',
'bitnami/redis:7.2.4',
'bitnami/postgresql:15.4.0'
];
console.log(chalk.bold.blue('š Enhanced Container Analysis Demo'));
console.log(chalk.gray('Testing container image security and deployment analysis\n'));
testImages.forEach(image => {
const report = analyzer.generateReport(image);
analyzer.displayReport(report);
});
console.log(chalk.bold.green('\nā
Enhanced analysis completed!'));
console.log(chalk.gray('This demonstrates:'));
console.log(chalk.gray('⢠Image versioning analysis (latest vs pinned)'));
console.log(chalk.gray('⢠Helm deployment detection'));
console.log(chalk.gray('⢠Container security assessment'));
console.log(chalk.gray('⢠Tool availability checking'));
console.log(chalk.gray('⢠Intelligent recommendations'));
}
module.exports = { EnhancedAnalyzer };
// Add Bitnami Premium migration analysis
class BitnamiPremiumAnalyzer {
analyzeMigrationComplexity(imageName) {
const [name, tag] = imageName.split(':');
const tagValue = tag || 'latest';
if (!name.includes('bitnami')) {
return { applicable: false };
}
const isLatest = tagValue === 'latest';
const isPinned = tagValue !== 'latest' && /^\d+\.\d+/.test(tagValue);
return {
applicable: true,
currentImage: imageName,
premiumImage: imageName.replace('bitnami/', 'bitnamiprem/'),
migrationComplexity: isLatest ? 'LOW' : 'HIGH',
reason: isLatest ?
'Latest tag - automatic transition to Premium' :
'Pinned version - requires manual registry migration',
authRequired: true,
registryChanges: !isLatest,
helmChartImpact: true
};
}
generateMigrationScript(analysis) {
if (!analysis.applicable) return '';
if (analysis.migrationComplexity === 'LOW') {
return `
# EASY MIGRATION (Latest Tag)
# 1. Setup Docker Hub Premium access
# 2. Configure pull secrets
# 3. Images will automatically pull from Premium registry
kubectl create secret docker-registry regcred \\
--docker-server=https://index.docker.io \\
--docker-username=<your-username> \\
--docker-password=<your-pat> \\
--docker-email=<your-email>
`;
} else {
return `
# COMPLEX MIGRATION (Pinned Version)
# 1. Update image references
# 2. Setup authentication
# 3. Update CI/CD pipelines
# Old image: ${analysis.currentImage}
# New image: ${analysis.premiumImage}
# Update deployments:
kubectl patch deployment <deployment-name> -p '{"spec":{"template":{"spec":{"containers":[{"name":"<container-name>","image":"${analysis.premiumImage}"}]}}}}'
# Create pull secret:
kubectl create secret docker-registry regcred \\
--docker-server=https://index.docker.io \\
--docker-username=<your-username> \\
--docker-password=<your-pat> \\
--docker-email=<your-email>
`;
}
}
}
// Export for use
if (typeof module !== 'undefined') {
module.exports = { EnhancedAnalyzer, BitnamiPremiumAnalyzer };
}