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.
211 lines (170 loc) ⢠7.62 kB
JavaScript
const chalk = require('chalk');
class EnhancedPrivateRegistryDetector {
constructor() {
this.registryPatterns = [
// Public registries
{ pattern: /^bitnami\//, type: 'Docker Hub', public: true },
{ pattern: /^docker\.io\/bitnami\//, type: 'Docker Hub', public: true },
{ pattern: /\/bitnami\//, type: 'Generic Public', public: true },
// AWS ECR Private
{ pattern: /^\d+\.dkr\.ecr\.[^\/]+\.amazonaws\.com\/.*bitnami/, type: 'ECR Private', public: false },
// AWS ECR Public
{ pattern: /^public\.ecr\.aws\/.*bitnami/, type: 'ECR Public', public: true },
// Common private registries
{ pattern: /^[^\/]+\.[^\/]+\/.*bitnami/, type: 'Private Registry', public: false },
{ pattern: /^harbor\.[^\/]+\/.*bitnami/, type: 'Harbor Registry', public: false },
{ pattern: /^artifactory\.[^\/]+\/.*bitnami/, type: 'Artifactory', public: false },
{ pattern: /^nexus\.[^\/]+\/.*bitnami/, type: 'Nexus Registry', public: false },
{ pattern: /^registry\.[^\/]+\/.*bitnami/, type: 'Generic Private', public: false },
// Internal/corporate patterns
{ pattern: /^[^\/]*\.internal[^\/]*\/.*bitnami/, type: 'Internal Registry', public: false },
{ pattern: /^[^\/]*\.corp[^\/]*\/.*bitnami/, type: 'Corporate Registry', public: false },
{ pattern: /^localhost:\d+\/.*bitnami/, type: 'Local Registry', public: false },
{ pattern: /^127\.0\.0\.1:\d+\/.*bitnami/, type: 'Local Registry', public: false }
];
}
detectBitnamiImage(imageName) {
if (!imageName || typeof imageName !== 'string') {
return { isBitnami: false };
}
const normalizedImage = imageName.toLowerCase();
for (const { pattern, type, public: isPublic } of this.registryPatterns) {
if (pattern.test(normalizedImage)) {
return {
isBitnami: true,
imageName,
registryType: type,
isPublic,
isPrivate: !isPublic,
pattern: pattern.toString()
};
}
}
return { isBitnami: false, imageName };
}
analyzeImageList(images) {
console.log(chalk.bold.blue('š Enhanced Private Registry Detection'));
console.log(chalk.gray('Comprehensive Bitnami Image Detection Across All Registry Types\n'));
const results = images.map(img => this.detectBitnamiImage(img));
const bitnamiImages = results.filter(r => r.isBitnami);
const nonBitnamiImages = results.filter(r => !r.isBitnami);
if (bitnamiImages.length === 0) {
console.log(chalk.yellow('No Bitnami images detected in provided list.'));
return;
}
// Group by registry type
const byRegistryType = {};
bitnamiImages.forEach(result => {
if (!byRegistryType[result.registryType]) {
byRegistryType[result.registryType] = [];
}
byRegistryType[result.registryType].push(result);
});
// Display results by registry type
Object.entries(byRegistryType).forEach(([registryType, images]) => {
const isPrivate = images[0].isPrivate;
const icon = isPrivate ? 'š' : 'š';
const color = isPrivate ? chalk.red : chalk.green;
console.log(color.bold(`${icon} ${registryType.toUpperCase()} (${images.length} images)`));
images.forEach(image => {
console.log(` š¦ ${image.imageName}`);
console.log(chalk.gray(` Registry: ${image.registryType}`));
console.log(chalk.gray(` Access: ${image.isPrivate ? 'Private' : 'Public'}`));
});
console.log('');
});
// Summary statistics
const publicCount = bitnamiImages.filter(img => img.isPublic).length;
const privateCount = bitnamiImages.filter(img => img.isPrivate).length;
console.log(chalk.bold('š Detection Summary:'));
console.log(` Total Images Analyzed: ${images.length}`);
console.log(` Bitnami Images Found: ${chalk.blue(bitnamiImages.length)}`);
console.log(` Public Registries: ${chalk.green(publicCount)}`);
console.log(` Private Registries: ${chalk.red(privateCount)}`);
console.log(` Non-Bitnami Images: ${chalk.gray(nonBitnamiImages.length)}`);
// Registry type breakdown
console.log(chalk.bold('\nš·ļø Registry Type Breakdown:'));
Object.entries(byRegistryType)
.sort(([,a], [,b]) => b.length - a.length)
.forEach(([type, images]) => {
const accessType = images[0].isPrivate ? 'š Private' : 'š Public';
console.log(` ${type}: ${images.length} (${accessType})`);
});
// Private registry considerations
if (privateCount > 0) {
console.log(chalk.bold.yellow('\nā ļø Private Registry Considerations:'));
console.log(' ⢠Authentication may be required for image analysis');
console.log(' ⢠Cross-account ECR access needs proper IAM permissions');
console.log(' ⢠Corporate registries may need VPN/network access');
console.log(' ⢠Registry credentials should be configured in scanning environment');
}
}
generateEnhancedDetectionCode() {
return `
// Enhanced Bitnami detection for all registry types
public isBitnamiImage(imageName: string): boolean {
if (!imageName || typeof imageName !== 'string') {
return false;
}
const normalizedImage = imageName.toLowerCase();
// Public registries
if (normalizedImage.includes('/bitnami/') || normalizedImage.startsWith('bitnami/')) {
return true;
}
// AWS ECR Private: 123456789012.dkr.ecr.us-east-1.amazonaws.com/bitnami/mysql
if (/^\\d+\\.dkr\\.ecr\\.[^\\/]+\\.amazonaws\\.com\\/.*bitnami/.test(normalizedImage)) {
return true;
}
// AWS ECR Public: public.ecr.aws/bitnami/mysql
if (normalizedImage.startsWith('public.ecr.aws/') && normalizedImage.includes('bitnami')) {
return true;
}
// Private registries: registry.company.com/bitnami/mysql
if (/^[^\\/]+\\.[^\\/]+\\/.*bitnami/.test(normalizedImage)) {
return true;
}
// Internal registries: registry.internal/bitnami/mysql
if (/^[^\\/]*\\.(internal|corp)[^\\/]*\\/.*bitnami/.test(normalizedImage)) {
return true;
}
// Local registries: localhost:5000/bitnami/mysql
if (/^(localhost|127\\.0\\.0\\.1):\\d+\\/.*bitnami/.test(normalizedImage)) {
return true;
}
return false;
}`;
}
}
// Test with comprehensive image list
if (require.main === module) {
const detector = new EnhancedPrivateRegistryDetector();
const testImages = [
// Public registries
'bitnami/mysql:8.0.35',
'docker.io/bitnami/redis:7.2.4',
'public.ecr.aws/bitnami/nginx:1.25',
// AWS ECR Private
'123456789012.dkr.ecr.us-east-1.amazonaws.com/bitnami/mysql:8.0.35',
'987654321098.dkr.ecr.eu-west-1.amazonaws.com/bitnami/postgresql:15.4.0',
// Private registries
'harbor.company.com/bitnami/kafka:3.5.0',
'artifactory.corp.com/bitnami/elasticsearch:8.8.0',
'nexus.internal.com/bitnami/mongodb:6.0.8',
'registry.company.com/bitnami/redis:7.2.4',
// Internal/corporate
'registry.internal/bitnami/grafana:10.0.3',
'docker.corp.company.com/bitnami/prometheus:2.45.0',
// Local registries
'localhost:5000/bitnami/jenkins:2.414.1',
'127.0.0.1:5000/bitnami/sonarqube:10.1.0',
// Non-Bitnami images
'nginx:1.25',
'mysql:8.0',
'redis:7.2'
];
detector.analyzeImageList(testImages);
console.log(chalk.bold.blue('\nš§ Enhanced Detection Code:'));
console.log(detector.generateEnhancedDetectionCode());
}
module.exports = { EnhancedPrivateRegistryDetector };