woaru
Version:
Universal Project Setup Autopilot - Analyze and automatically configure development tools for ANY programming language
982 lines • 42.9 kB
JavaScript
/**
* WOARU Production Readiness Auditor Module
* Production-Ready implementation with comprehensive security and error handling
*/
import * as path from 'path';
import fs from 'fs-extra';
import { ToolsDatabaseManager } from '../database/ToolsDatabaseManager.js';
import { QualityRunner } from '../quality/QualityRunner.js';
import { NotificationManager } from '../supervisor/NotificationManager.js';
import { t, initializeI18n } from '../config/i18n.js';
/**
* Security constants for input validation
*/
const SECURITY_LIMITS = {
MAX_FILE_SIZE: 10 * 1024 * 1024, // 10MB
MAX_PATH_LENGTH: 500,
MAX_PACKAGE_NAME_LENGTH: 100,
MAX_VULNERABILITIES_DISPLAY: 10,
MAX_AUDIT_TIME: 60000, // 60 seconds
};
/**
* Sanitizes package names to prevent injection attacks
* @param packageName - The package name to sanitize
* @returns Sanitized package name safe for display
*/
function sanitizePackageName(packageName) {
if (typeof packageName !== 'string') {
return 'unknown-package';
}
return (packageName
.replace(/[^a-zA-Z0-9@/_.-]/g, '')
.substring(0, SECURITY_LIMITS.MAX_PACKAGE_NAME_LENGTH) ||
'unknown-package');
}
/**
* Sanitizes file paths to prevent directory traversal
* @param filePath - The file path to sanitize
* @returns Sanitized file path safe for processing
*/
function sanitizeFilePath(filePath) {
if (typeof filePath !== 'string') {
return 'unknown-file';
}
const normalized = path.normalize(filePath);
// Check for directory traversal
if (normalized.includes('..') || normalized.includes('\x00')) {
return 'suspicious-file';
}
const baseName = path.basename(normalized);
return (baseName.replace(/[^a-zA-Z0-9._-]/g, '').substring(0, 100) || 'unknown-file');
}
/**
* Sanitizes error messages to prevent information leakage
* @param error - The error to sanitize
* @returns Sanitized error message safe for display
*/
function sanitizeError(error) {
if (typeof error === 'string') {
return error.replace(/\/[^\s]*\/[^\s]*/g, '[PATH]').substring(0, 200);
}
if (error instanceof Error) {
return sanitizeError(error.message);
}
return 'Unknown audit error';
}
// Removed unused isValidVulnerability function
/**
* Validates project path for security
* @param projectPath - Project path to validate
* @returns True if path appears safe
*/
function isSecureProjectPath(projectPath) {
try {
const normalized = path.normalize(projectPath);
// Check for suspicious patterns
if (normalized.includes('..') ||
normalized.includes('\x00') ||
normalized.length > SECURITY_LIMITS.MAX_PATH_LENGTH) {
return false;
}
return true;
}
catch {
return false;
}
}
export class ProductionReadinessAuditor {
projectPath;
databaseManager;
auditMetrics = {
auditsPerformed: 0,
totalIssuesFound: 0,
criticalIssuesFound: 0,
averageAuditTime: 0,
securityIssues: 0,
};
constructor(projectPath) {
if (!isSecureProjectPath(projectPath)) {
throw new Error('Invalid or suspicious project path provided');
}
this.projectPath = path.normalize(projectPath);
this.databaseManager = new ToolsDatabaseManager();
}
/**
* Perform comprehensive project audit with security validation
* @param config - Audit configuration
* @returns Promise<ProductionAudit[]> Array of audit findings
*/
async auditProject(config) {
const startTime = Date.now();
try {
await initializeI18n();
// Validate configuration
if (!this.validateAuditConfig(config)) {
console.warn('Invalid audit configuration provided');
return [];
}
const audits = [];
// Run all audit categories with timeout protection
const auditPromises = [
this.auditErrorMonitoring(config),
this.auditTestingFramework(config),
this.auditContainerization(config),
this.auditSecurity(config),
this.auditEnvironmentConfig(config),
];
const results = await Promise.allSettled(auditPromises.map(p => this.withTimeout(p, SECURITY_LIMITS.MAX_AUDIT_TIME)));
// Process results and collect successful audits
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
audits.push(...result.value);
}
else {
console.debug(`Audit ${index} failed: ${sanitizeError(result.reason)}`);
}
});
// Update metrics
this.updateAuditMetrics(audits, Date.now() - startTime);
return audits.filter(audit => audit.status === 'missing' || audit.status === 'partial');
}
catch (error) {
console.error(`Project audit failed: ${sanitizeError(error)}`);
return [];
}
}
/**
* Validate audit configuration for security
*/
validateAuditConfig(config) {
if (!config || typeof config !== 'object') {
return false;
}
const validLanguages = [
'javascript',
'typescript',
'python',
'java',
'go',
'rust',
];
const validProjectTypes = [
'frontend',
'backend',
'fullstack',
'library',
'cli',
];
return (typeof config.language === 'string' &&
validLanguages.includes(config.language.toLowerCase()) &&
typeof config.projectType === 'string' &&
validProjectTypes.includes(config.projectType) &&
Array.isArray(config.frameworks));
}
/**
* Add timeout protection to audit operations
*/
async withTimeout(promise, timeout) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Audit operation timeout'));
}, timeout);
promise
.then(resolve)
.catch(reject)
.finally(() => clearTimeout(timer));
});
}
/**
* Update audit performance metrics
*/
updateAuditMetrics(audits, duration) {
this.auditMetrics.auditsPerformed++;
this.auditMetrics.totalIssuesFound += audits.length;
this.auditMetrics.criticalIssuesFound += audits.filter(a => a.priority === 'critical').length;
this.auditMetrics.averageAuditTime =
(this.auditMetrics.averageAuditTime + duration) / 2;
}
/**
* Focused audit on changed files only with security validation
* @param changedFiles - Array of changed file paths
* @param config - Audit configuration
* @returns Promise<ProductionAudit[]> Array of relevant audit findings
*/
async auditChangedFiles(changedFiles, config) {
try {
await initializeI18n();
// Validate inputs
if (!Array.isArray(changedFiles) || !this.validateAuditConfig(config)) {
console.warn(t('production_auditor.invalid_changed_files_config'));
return [];
}
// Sanitize file paths
const sanitizedFiles = changedFiles
.filter(file => typeof file === 'string' && file.length > 0)
.map(file => sanitizeFilePath(file))
.slice(0, 100); // Limit number of files
const audits = [];
// Categorize changed files
const relevantFiles = this.categorizeRelevantFiles(sanitizedFiles);
// Only audit categories that are relevant to the changed files
if (relevantFiles.packageJson.length > 0) {
audits.push(...(await this.auditErrorMonitoring(config)));
audits.push(...(await this.auditTestingFramework(config)));
audits.push(...(await this.auditSecurity(config)));
}
if (relevantFiles.docker.length > 0) {
audits.push(...(await this.auditContainerization(config)));
}
if (relevantFiles.config.length > 0) {
audits.push(...(await this.auditEnvironmentConfig(config)));
}
if (relevantFiles.source.length > 0) {
audits.push(...this.auditSourceFileChanges(relevantFiles.source, config));
}
return audits.filter(audit => audit.status === 'missing' || audit.status === 'partial');
}
catch (error) {
console.error(t('production_auditor.audit_changed_files_error'), sanitizeError(error));
return [];
}
}
async auditErrorMonitoring(config) {
const audits = [];
try {
// Get error monitoring tools from live database
const errorMonitoringTools = await this.databaseManager.getToolsByCategory('error-monitoring');
if (config.language === 'javascript' ||
config.language === 'typescript') {
const packageJson = await this.readPackageJson();
if (!packageJson)
return audits;
const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};
const depsList = Object.keys(allDeps);
// Check if any error monitoring tools are installed using database keywords
let hasErrorTracking = false;
// foundTool tracking removed as unused
for (const [toolKey, tool] of Object.entries(errorMonitoringTools)) {
const hasThisTool = tool.keywords.some((keyword) => depsList.some(dep => dep.includes(keyword)));
if (hasThisTool) {
hasErrorTracking = true;
// tool.name would be stored in foundTool but not used
// Check if the found tool is deprecated
if (tool.isDeprecated) {
this.addDeprecationWarning(audits, tool, toolKey);
}
break;
}
}
if (!hasErrorTracking) {
// Get recommended tool for the specific framework
const recommendedTool = this.getRecommendedErrorMonitoringTool(errorMonitoringTools, config.frameworks);
// Get alternative recommendations (sorted by popularity, excluding deprecated)
const alternatives = Object.values(errorMonitoringTools)
.filter((tool) => tool.name !== recommendedTool.name && !tool.isDeprecated)
.sort((a, b) => {
const popularityDiff = (b.popularity || 0) - (a.popularity || 0);
if (popularityDiff !== 0)
return popularityDiff;
return (b.downloads?.weekly || 0) - (a.downloads?.weekly || 0);
})
.slice(0, 2);
// Single consolidated recommendation
audits.push({
category: 'error-monitoring',
check: 'error-tracking-setup',
status: 'missing',
priority: config.projectType === 'library' ? 'low' : 'critical',
message: t('production_auditor.error_monitoring_critical'),
recommendation: t('production_auditor.error_monitoring_recommendation', {
tool: sanitizePackageName(recommendedTool.name),
alternatives: alternatives.length > 0
? ` (Alternativen: ${alternatives.map(t => sanitizePackageName(t.name)).join(', ')})`
: '',
installCommand: this.getInstallCommand(recommendedTool, config.frameworks),
}),
packages: [
...this.getRecommendedPackages(recommendedTool, config.frameworks),
...this.getAllErrorMonitoringPackages(errorMonitoringTools, config.frameworks).slice(0, 3),
],
});
}
}
}
catch {
// Fallback to original logic if database fails
return this.auditErrorMonitoringFallback(config);
}
if (config.language === 'python') {
// Check for Python error monitoring
const requirementFiles = [
'requirements.txt',
'pyproject.toml',
'Pipfile',
];
let hasPythonErrorTracking = false;
for (const file of requirementFiles) {
const filePath = path.join(this.projectPath, file);
if (await fs.pathExists(filePath)) {
const content = await fs.readFile(filePath, 'utf-8');
if (content.includes('sentry-sdk') ||
content.includes('rollbar') ||
content.includes('bugsnag')) {
hasPythonErrorTracking = true;
break;
}
}
}
if (!hasPythonErrorTracking) {
audits.push({
category: 'error-monitoring',
check: 'python-error-tracking',
status: 'missing',
priority: 'high',
message: '💡 PRO-TIPP: Kein Error-Monitoring für Python gefunden',
recommendation: 'Installiere sentry-sdk: pip install sentry-sdk',
packages: ['sentry-sdk'],
});
}
}
return audits;
}
async auditTestingFramework(config) {
const audits = [];
if (config.language === 'javascript' || config.language === 'typescript') {
const packageJson = await this.readPackageJson();
if (!packageJson)
return audits;
const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};
const testingFrameworks = [
'jest',
'vitest',
'mocha',
'jasmine',
'cypress',
'playwright',
'@testing-library/react',
];
const hasTestingFramework = testingFrameworks.some(framework => allDeps[framework]);
if (!hasTestingFramework) {
audits.push({
category: 'testing',
check: 'testing-framework',
status: 'missing',
priority: config.projectType === 'library' ? 'critical' : 'high',
message: '⚠️ WARNUNG: Kein Testing-Framework gefunden',
recommendation: 'Tests sind essentiell für Code-Qualität. Für React: npm install --save-dev jest @testing-library/react, für allgemein: npm install --save-dev vitest',
packages: ['jest', 'vitest', '@testing-library/react'],
});
}
// Check for test scripts
const hasTestScript = packageJson.scripts &&
(packageJson.scripts.test ||
packageJson.scripts['test:unit'] ||
packageJson.scripts['test:e2e']);
if (hasTestingFramework && !hasTestScript) {
audits.push({
category: 'testing',
check: 'test-scripts',
status: 'partial',
priority: 'medium',
message: 'Testing-Framework gefunden, aber keine Test-Scripts in package.json',
recommendation: 'Füge Test-Scripts hinzu: "test": "jest" oder "test": "vitest"',
});
}
}
if (config.language === 'python') {
const requirementFiles = [
'requirements.txt',
'pyproject.toml',
'Pipfile',
];
let hasPythonTesting = false;
for (const file of requirementFiles) {
const filePath = path.join(this.projectPath, file);
if (await fs.pathExists(filePath)) {
const content = await fs.readFile(filePath, 'utf-8');
if (content.includes('pytest') ||
content.includes('unittest') ||
content.includes('nose')) {
hasPythonTesting = true;
break;
}
}
}
if (!hasPythonTesting) {
audits.push({
category: 'testing',
check: 'python-testing',
status: 'missing',
priority: 'high',
message: '⚠️ WARNUNG: Kein Testing-Framework für Python gefunden',
recommendation: 'Installiere pytest: pip install pytest',
packages: ['pytest'],
});
}
}
return audits;
}
async auditContainerization(config) {
const audits = [];
const dockerfilePath = path.join(this.projectPath, 'Dockerfile');
const hasDockerfile = await fs.pathExists(dockerfilePath);
if (!hasDockerfile && config.projectType !== 'library') {
audits.push({
category: 'containerization',
check: 'dockerfile',
status: 'missing',
priority: 'medium',
message: '🐳 Kein Dockerfile gefunden',
recommendation: 'Containerisierung vereinfacht Deployment und Skalierung. Erstelle ein Dockerfile für konsistente Deployments.',
files: ['Dockerfile'],
});
}
const dockerIgnorePath = path.join(this.projectPath, '.dockerignore');
const hasDockerIgnore = await fs.pathExists(dockerIgnorePath);
if (hasDockerfile && !hasDockerIgnore) {
audits.push({
category: 'containerization',
check: 'dockerignore',
status: 'partial',
priority: 'low',
message: 'Dockerfile gefunden, aber keine .dockerignore',
recommendation: 'Erstelle .dockerignore um Build-Performance zu verbessern und Image-Größe zu reduzieren.',
files: ['.dockerignore'],
});
}
return audits;
}
async auditSecurity(config) {
const audits = [];
// Run comprehensive Snyk security scan
try {
const notificationManager = new NotificationManager({
terminal: false,
desktop: false,
});
const qualityRunner = new QualityRunner(notificationManager);
// Get all project files for comprehensive scan
const allFiles = await this.getAllProjectFiles();
const snykResults = await qualityRunner.runSnykChecks(allFiles);
// Process Snyk results into production audits
for (const result of snykResults) {
if (result.error) {
// If Snyk is not installed or configured
audits.push({
category: 'security',
check: 'snyk-setup',
status: 'missing',
priority: 'critical',
message: '🚨 Snyk Security Scanner nicht konfiguriert',
recommendation: result.error.includes('not installed')
? 'Installiere Snyk global: npm install -g snyk && snyk auth'
: 'Konfiguriere Snyk: snyk auth',
packages: ['snyk'],
});
continue;
}
// Process dependency vulnerabilities
if (result.type === 'dependencies' &&
result.vulnerabilities &&
result.vulnerabilities.length > 0) {
const criticalVulns = result.vulnerabilities.filter(v => v.severity === 'critical');
const highVulns = result.vulnerabilities.filter(v => v.severity === 'high');
const mediumVulns = result.vulnerabilities.filter(v => v.severity === 'medium');
if (criticalVulns.length > 0) {
audits.push({
category: 'security',
check: 'critical-vulnerabilities',
status: 'missing',
priority: 'critical',
message: `🚨 ${criticalVulns.length} KRITISCHE Sicherheitslücken in Dependencies gefunden`,
recommendation: `Sofortiges Handeln erforderlich! ${criticalVulns.filter(v => v.isUpgradable).length} können durch Updates behoben werden. Führe "snyk wizard" aus für geführte Behebung.`,
packages: [
...new Set(criticalVulns
.filter(v => v.isUpgradable)
.map(v => v.packageName)),
],
});
// Add specific critical vulnerabilities (max 3)
criticalVulns.slice(0, 3).forEach(vuln => {
audits.push({
category: 'security',
check: `vuln-${vuln.id}`,
status: 'missing',
priority: 'critical',
message: `🔴 ${vuln.title} in ${vuln.packageName}@${vuln.version}`,
recommendation: vuln.isUpgradable
? `Update auf ${vuln.fixedIn?.[0] || 'neueste Version'}: npm update ${vuln.packageName}`
: `Kein direkter Fix verfügbar. Erwäge Alternative oder warte auf Patch.`,
packages: vuln.isUpgradable ? [vuln.packageName] : [],
});
});
}
if (highVulns.length > 0) {
audits.push({
category: 'security',
check: 'high-vulnerabilities',
status: 'missing',
priority: 'high',
message: `⚠️ ${highVulns.length} hohe Sicherheitslücken in Dependencies`,
recommendation: `${highVulns.filter(v => v.isUpgradable).length} können durch Updates behoben werden. Führe "snyk test" für Details aus.`,
packages: [
...new Set(highVulns
.filter(v => v.isUpgradable)
.map(v => v.packageName)
.slice(0, 5)),
],
});
}
if (mediumVulns.length > 5) {
audits.push({
category: 'security',
check: 'medium-vulnerabilities',
status: 'partial',
priority: 'medium',
message: `🔵 ${mediumVulns.length} mittlere Sicherheitslücken gefunden`,
recommendation: 'Plane Updates für betroffene Packages ein. Führe "snyk test --severity-threshold=medium" aus.',
packages: [],
});
}
}
// Process code vulnerabilities
if (result.type === 'code' &&
result.codeIssues &&
result.codeIssues.length > 0) {
const criticalCodeIssues = result.codeIssues.filter(i => i.severity === 'critical');
const highCodeIssues = result.codeIssues.filter(i => i.severity === 'high');
if (criticalCodeIssues.length > 0) {
audits.push({
category: 'security',
check: 'code-vulnerabilities-critical',
status: 'missing',
priority: 'critical',
message: `🚨 ${criticalCodeIssues.length} kritische Sicherheitsprobleme im Code gefunden`,
recommendation: 'Behebe diese Sicherheitslücken sofort! Führe "snyk code test" für Details aus.',
files: [...new Set(criticalCodeIssues.map(i => i.filePath))],
});
}
if (highCodeIssues.length > 0) {
audits.push({
category: 'security',
check: 'code-vulnerabilities-high',
status: 'missing',
priority: 'high',
message: `⚠️ ${highCodeIssues.length} hohe Sicherheitsprobleme im Code`,
recommendation: 'Review und behebe diese Sicherheitsprobleme vor dem Production-Deploy.',
files: [
...new Set(highCodeIssues.map(i => i.filePath).slice(0, 5)),
],
});
}
}
}
}
catch (error) {
// If Snyk scan fails completely, still run basic security checks
console.warn('Snyk security scan failed, falling back to basic checks:', error);
}
// Basic security checks (always run these)
if (config.language === 'javascript' || config.language === 'typescript') {
const packageJson = await this.readPackageJson();
if (!packageJson)
return audits;
const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};
// Check for helmet (Express security)
if (allDeps.express && !allDeps.helmet) {
audits.push({
category: 'security',
check: 'helmet',
status: 'missing',
priority: 'high',
message: '🔒 Express ohne Helmet-Schutz',
recommendation: 'Installiere Helmet für sichere HTTP-Headers: npm install helmet',
packages: ['helmet'],
});
}
// Check for HTTPS enforcement
const hasHttpsEnforcement = allDeps['express-enforces-ssl'] || allDeps['express-sslify'];
if (allDeps.express &&
!hasHttpsEnforcement &&
config.projectType === 'backend') {
audits.push({
category: 'security',
check: 'https-enforcement',
status: 'missing',
priority: 'high',
message: '🔒 Keine HTTPS-Enforcement gefunden',
recommendation: 'Erzwinge HTTPS in Production: npm install express-enforces-ssl',
packages: ['express-enforces-ssl'],
});
}
// Check for security headers middleware
if (config.frameworks.includes('next') &&
!allDeps['next-secure-headers']) {
audits.push({
category: 'security',
check: 'security-headers',
status: 'missing',
priority: 'medium',
message: '🔒 Keine Security Headers Konfiguration für Next.js',
recommendation: 'Installiere next-secure-headers für besseren Schutz: npm install next-secure-headers',
packages: ['next-secure-headers'],
});
}
// Check for rate limiting
if (allDeps.express &&
!allDeps['express-rate-limit'] &&
!allDeps['rate-limiter-flexible']) {
audits.push({
category: 'security',
check: 'rate-limiting',
status: 'missing',
priority: 'high',
message: '🔒 Kein Rate Limiting zum Schutz vor DDoS/Brute-Force',
recommendation: 'Installiere express-rate-limit: npm install express-rate-limit',
packages: ['express-rate-limit'],
});
}
// Check for input validation
if (!allDeps.joi &&
!allDeps.yup &&
!allDeps['express-validator'] &&
!allDeps.zod) {
audits.push({
category: 'security',
check: 'input-validation',
status: 'missing',
priority: 'high',
message: '🔒 Keine Input-Validierungs-Bibliothek gefunden',
recommendation: 'Installiere eine Validierungs-Bibliothek wie Joi oder Zod für sichere Input-Verarbeitung',
packages: ['joi', 'zod', 'yup'],
});
}
}
// Python security checks
if (config.language === 'python') {
const requirementFiles = [
'requirements.txt',
'pyproject.toml',
'Pipfile',
];
let hasBandit = false;
// hasSafety variable removed as unused
for (const file of requirementFiles) {
const filePath = path.join(this.projectPath, file);
if (await fs.pathExists(filePath)) {
const content = await fs.readFile(filePath, 'utf-8');
if (content.includes('bandit'))
hasBandit = true;
// if (content.includes('safety')) { /* hasSafety would be set but unused */ }
}
}
if (!hasBandit) {
audits.push({
category: 'security',
check: 'python-security-linter',
status: 'missing',
priority: 'high',
message: '🔒 Kein Python Security Linter (Bandit) gefunden',
recommendation: 'Installiere Bandit für Security-Checks: pip install bandit',
packages: ['bandit'],
});
}
}
return audits;
}
/**
* Helper to get all project files (for comprehensive Snyk scan)
*/
async getAllProjectFiles() {
// Return empty array as Snyk scans the whole project anyway
return [];
}
async auditEnvironmentConfig(config) {
const audits = [];
const envExamplePath = path.join(this.projectPath, '.env.example');
const envPath = path.join(this.projectPath, '.env');
const hasEnvExample = await fs.pathExists(envExamplePath);
const hasEnv = await fs.pathExists(envPath);
if (hasEnv && !hasEnvExample) {
audits.push({
category: 'config',
check: 'env-example',
status: 'missing',
priority: 'medium',
message: '.env gefunden, aber keine .env.example',
recommendation: 'Erstelle .env.example für Team-Kollaboration und Setup-Dokumentation.',
files: ['.env.example'],
});
}
if (config.language === 'javascript' || config.language === 'typescript') {
const packageJson = await this.readPackageJson();
if (packageJson?.dependencies?.dotenv && !hasEnv && !hasEnvExample) {
audits.push({
category: 'config',
check: 'env-config',
status: 'partial',
priority: 'medium',
message: 'dotenv installiert, aber keine .env-Dateien gefunden',
recommendation: 'Erstelle .env für lokale Konfiguration und .env.example als Template.',
files: ['.env', '.env.example'],
});
}
}
return audits;
}
getSentryPackageForFramework(frameworks) {
if (frameworks.includes('next'))
return ['@sentry/nextjs'];
if (frameworks.includes('react'))
return ['@sentry/react'];
if (frameworks.includes('vue'))
return ['@sentry/vue'];
if (frameworks.includes('express'))
return ['@sentry/node'];
return ['@sentry/browser'];
}
async readPackageJson() {
try {
const packageJsonPath = path.join(this.projectPath, 'package.json');
const content = await fs.readJson(packageJsonPath);
return content;
}
catch {
return null;
}
}
// Helper methods for changed file analysis
categorizeRelevantFiles(changedFiles) {
const result = {
packageJson: [],
docker: [],
config: [],
source: [],
};
changedFiles.forEach(file => {
const filename = path.basename(file);
const ext = path.extname(file).toLowerCase();
if (filename === 'package.json') {
result.packageJson.push(file);
}
else if (filename.startsWith('Dockerfile') ||
filename === '.dockerignore') {
result.docker.push(file);
}
else if (this.isConfigFile(filename)) {
result.config.push(file);
}
else if (this.isSourceFile(ext)) {
result.source.push(file);
}
});
return result;
}
isConfigFile(filename) {
const configFiles = [
'.env',
'.env.example',
'.env.local',
'.env.production',
'tsconfig.json',
'jest.config.js',
'webpack.config.js',
'.eslintrc',
'.prettierrc',
'babel.config.js',
];
return configFiles.some(config => filename.includes(config));
}
isSourceFile(ext) {
const sourceExtensions = [
'.js',
'.jsx',
'.ts',
'.tsx',
'.py',
'.rs',
'.go',
'.java',
'.cs',
'.php',
'.rb',
];
return sourceExtensions.includes(ext);
}
auditSourceFileChanges(sourceFiles, _config) {
const audits = [];
// Check if new components were added without tests
const newComponents = sourceFiles.filter(file => file.includes('component') || file.includes('Component'));
if (newComponents.length > 0) {
audits.push({
category: 'testing',
check: 'new-components-testing',
status: 'missing',
priority: 'high',
message: `${newComponents.length} neue Komponenten ohne Tests`,
recommendation: `Erstelle Tests für die neuen Komponenten: ${newComponents.map(f => path.basename(f)).join(', ')}`,
files: newComponents,
});
}
// Check for new API endpoints without error handling
const apiFiles = sourceFiles.filter(file => file.includes('api') ||
file.includes('route') ||
file.includes('endpoint'));
if (apiFiles.length > 0) {
audits.push({
category: 'error-monitoring',
check: 'api-error-handling',
status: 'missing',
priority: 'high',
message: 'Neue API-Endpunkte ohne Error-Monitoring',
recommendation: 'Integriere Sentry oder ähnliche Tools für API-Error-Tracking',
packages: ['@sentry/node', '@sentry/react'],
files: apiFiles,
});
}
return audits;
}
// New database-driven helper methods
getRecommendedErrorMonitoringTool(tools, frameworks) {
// Prioritize based on framework compatibility and popularity
const toolsArray = Object.values(tools);
// First, try to find a tool that specifically supports the frameworks
for (const framework of frameworks) {
const frameworkTool = toolsArray.find((tool) => typeof tool.frameworks === 'object' &&
!Array.isArray(tool.frameworks) &&
tool.frameworks[framework]);
if (frameworkTool &&
frameworkTool.isRecommended &&
!frameworkTool.isDeprecated) {
return frameworkTool;
}
}
// Fallback to most popular recommended tool (prioritize by downloads and stars)
return (toolsArray
.filter((tool) => tool.isRecommended && !tool.isDeprecated)
.sort((a, b) => {
// Primary sort: popularity score
const popularityDiff = (b.popularity || 0) - (a.popularity || 0);
if (popularityDiff !== 0)
return popularityDiff;
// Secondary sort: weekly downloads
const downloadsDiff = (b.downloads?.weekly || 0) - (a.downloads?.weekly || 0);
if (downloadsDiff !== 0)
return downloadsDiff;
// Tertiary sort: GitHub stars
return (b.stars || 0) - (a.stars || 0);
})[0] || toolsArray[0]);
}
getInstallCommand(tool, frameworks) {
if (typeof tool.installCommand === 'string') {
return tool.installCommand;
}
// Find specific install command for framework
for (const framework of frameworks) {
if (typeof tool.installCommand === 'object' &&
tool.installCommand[framework]) {
return tool.installCommand[framework];
}
}
// Fallback to common commands
if (typeof tool.installCommand === 'object') {
const commands = tool.installCommand;
if (commands.javascript || commands.react) {
return commands.javascript || commands.react;
}
}
return `Install ${tool.name}`;
}
getRecommendedPackages(tool, frameworks) {
const packages = [];
if (typeof tool.frameworks === 'object' &&
!Array.isArray(tool.frameworks)) {
// Get framework-specific packages
frameworks.forEach(framework => {
const frameworkPackage = tool.frameworks[framework];
if (frameworkPackage) {
packages.push(frameworkPackage);
}
});
}
// If no framework-specific packages, use keywords
if (packages.length === 0) {
packages.push(...tool.keywords.slice(0, 3));
}
return packages;
}
getAllErrorMonitoringPackages(tools, frameworks) {
const packages = [];
Object.values(tools).forEach((tool) => {
if (typeof tool.frameworks === 'object' &&
!Array.isArray(tool.frameworks)) {
frameworks.forEach(framework => {
const frameworkPackage = tool.frameworks[framework];
if (frameworkPackage) {
packages.push(frameworkPackage);
}
});
}
// Add main keywords
packages.push(...tool.keywords.slice(0, 2));
});
return [...new Set(packages)]; // Remove duplicates
}
// Method to add deprecation warnings to audit results
addDeprecationWarning(audits, tool, _toolKey) {
audits.push({
category: 'error-monitoring',
check: 'deprecated-tool',
status: 'missing',
priority: 'high',
message: `⚠️ WARNUNG: Das von dir genutzte Tool ${tool.name} ist veraltet`,
recommendation: tool.successor
? `Der empfohlene Nachfolger ist ${tool.successor}. Migriere zu: npm install ${tool.successor}`
: `Erwäge Migration zu einer modernen Alternative`,
packages: tool.successor ? [tool.successor] : [],
});
}
// Fallback method for when database is unavailable
async auditErrorMonitoringFallback(config) {
const audits = [];
if (config.language === 'javascript' || config.language === 'typescript') {
const packageJson = await this.readPackageJson();
if (!packageJson)
return audits;
const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies,
};
// Check for Sentry (fallback)
const sentryPackages = [
'@sentry/node',
'@sentry/react',
'@sentry/browser',
'@sentry/nextjs',
];
const hasSentry = sentryPackages.some(pkg => allDeps[pkg]);
if (!hasSentry) {
audits.push({
category: 'error-monitoring',
check: 'sentry-fallback',
status: 'missing',
priority: config.projectType === 'library' ? 'low' : 'high',
message: '💡 PRO-TIPP: Kein Error-Monitoring gefunden (Fallback)',
recommendation: 'Installiere Sentry für Production-Error-Tracking. Für React: npm install @sentry/react, für Node.js: npm install @sentry/node',
packages: ['@sentry/react', '@sentry/node'],
});
}
}
return audits;
}
}
//# sourceMappingURL=ProductionReadinessAuditor.js.map