UNPKG

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
#!/usr/bin/env node 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 };