#!/usr/bin/env ts-node /** * MidStream Security Check Script * * Comprehensive security audit of MidStream components * Created by rUv */ import * as fs from 'fs'; import * as path from 'path'; import chalk from 'chalk'; // ============================================================================ // Security Check Types // ============================================================================ interface SecurityIssue { severity: 'critical' | 'high' | 'medium' | 'low'; category: string; file: string; line?: number; description: string; recommendation: string; } interface SecurityReport { timestamp: Date; totalIssues: number; critical: number; high: number; medium: number; low: number; issues: SecurityIssue[]; passed: string[]; } // ============================================================================ // Security Checks // ============================================================================ class SecurityChecker { private issues: SecurityIssue[] = []; private passed: string[] = []; /** * Run all security checks */ async runAllChecks(): Promise { console.log(chalk.bold.cyan('\nšŸ” MidStream Security Check')); console.log(chalk.gray('═'.repeat(60))); await this.checkEnvironmentVariables(); await this.checkAPIKeyExposure(); await this.checkDependencyVulnerabilities(); await this.checkInputValidation(); await this.checkAuthenticationMechanisms(); await this.checkDataEncryption(); await this.checkRateLimiting(); await this.checkErrorHandling(); await this.checkLogging(); await this.checkCORS(); return this.generateReport(); } /** * Check environment variables */ private async checkEnvironmentVariables(): Promise { console.log(chalk.yellow('\nšŸ“‹ Checking environment variables...')); const envExample = path.join(__dirname, '../../.env.example'); const env = path.join(__dirname, '../../.env'); // Check if .env.example exists if (!fs.existsSync(envExample)) { this.issues.push({ severity: 'medium', category: 'Configuration', file: '.env.example', description: '.env.example file is missing', recommendation: 'Create .env.example with all required environment variables', }); } else { this.passed.push('.env.example exists'); } // Check if .env is in .gitignore const gitignore = path.join(__dirname, '../../.gitignore'); if (fs.existsSync(gitignore)) { const content = fs.readFileSync(gitignore, 'utf-8'); if (content.includes('.env')) { this.passed.push('.env is in .gitignore'); } else { this.issues.push({ severity: 'high', category: 'Configuration', file: '.gitignore', description: '.env file not excluded from version control', recommendation: 'Add .env to .gitignore to prevent credential leakage', }); } } } /** * Check for API key exposure */ private async checkAPIKeyExposure(): Promise { console.log(chalk.yellow('\nšŸ”‘ Checking for API key exposure...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); const dangerousPatterns = [ /['"]sk-[a-zA-Z0-9]{32,}['"]/, // OpenAI keys /['"][A-Z0-9]{32,}['"]/, // Generic API keys /['"]api[_-]?key['"]:\s*['"][^'"]+['"]/i, // Hardcoded API keys ]; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; for (const pattern of dangerousPatterns) { if (pattern.test(line) && !line.includes('process.env')) { this.issues.push({ severity: 'critical', category: 'Credentials', file: path.relative(srcDir, file), line: i + 1, description: 'Potential hardcoded API key detected', recommendation: 'Use environment variables: process.env.API_KEY', }); } } } } if (this.issues.filter((i) => i.category === 'Credentials').length === 0) { this.passed.push('No hardcoded API keys found'); } } /** * Check dependency vulnerabilities */ private async checkDependencyVulnerabilities(): Promise { console.log(chalk.yellow('\nšŸ“¦ Checking dependencies...')); const packageJson = path.join(__dirname, '../../package.json'); if (fs.existsSync(packageJson)) { const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf-8')); // Check for known vulnerable packages (simplified check) const knownVulnerable = ['event-stream@3.3.6', 'flatmap-stream']; const allDeps = { ...pkg.dependencies, ...pkg.devDependencies, }; for (const [name, version] of Object.entries(allDeps)) { if (knownVulnerable.includes(name)) { this.issues.push({ severity: 'high', category: 'Dependencies', file: 'package.json', description: `Known vulnerable package: ${name}`, recommendation: 'Update or remove the vulnerable package', }); } } this.passed.push('Dependency check completed'); } } /** * Check input validation */ private async checkInputValidation(): Promise { console.log(chalk.yellow('\nāœ… Checking input validation...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); let validationFound = false; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); // Check for validation patterns if ( content.includes('validate') || content.includes('sanitize') || content.includes('throw new Error') ) { validationFound = true; } // Check for dangerous eval/exec usage if (content.includes('eval(') && !content.includes('// safe')) { this.issues.push({ severity: 'critical', category: 'Input Validation', file: path.relative(srcDir, file), description: 'Potential unsafe eval() usage', recommendation: 'Avoid eval(). Use safer alternatives like JSON.parse()', }); } } if (validationFound) { this.passed.push('Input validation mechanisms found'); } } /** * Check authentication mechanisms */ private async checkAuthenticationMechanisms(): Promise { console.log(chalk.yellow('\nšŸ” Checking authentication...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); let authFound = false; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); if ( content.includes('Authorization') || content.includes('apiKey') || content.includes('Bearer') ) { authFound = true; } // Check for insecure auth if (content.includes('Basic auth') && !content.includes('https')) { this.issues.push({ severity: 'high', category: 'Authentication', file: path.relative(srcDir, file), description: 'Basic auth without HTTPS', recommendation: 'Always use HTTPS with Basic authentication', }); } } if (authFound) { this.passed.push('Authentication mechanisms present'); } } /** * Check data encryption */ private async checkDataEncryption(): Promise { console.log(chalk.yellow('\nšŸ”’ Checking data encryption...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); let httpsFound = false; let wsssFound = false; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); // Check for HTTPS/WSS usage if (content.includes('https://') || content.includes('wss://')) { if (content.includes('https://')) httpsFound = true; if (content.includes('wss://')) wsssFound = true; } // Check for insecure protocols if (content.match(/['"]http:\/\/[^'"]+['"]/)) { const match = content.match(/['"]http:\/\/[^'"]+['"]/); if (match && !match[0].includes('localhost') && !match[0].includes('127.0.0.1')) { this.issues.push({ severity: 'medium', category: 'Encryption', file: path.relative(srcDir, file), description: 'Insecure HTTP protocol detected', recommendation: 'Use HTTPS for all external connections', }); } } } if (httpsFound) this.passed.push('HTTPS usage detected'); if (wsssFound) this.passed.push('WSS (secure WebSocket) usage detected'); } /** * Check rate limiting */ private async checkRateLimiting(): Promise { console.log(chalk.yellow('\nā±ļø Checking rate limiting...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); let rateLimitingFound = false; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); if ( content.includes('rate') || content.includes('throttle') || content.includes('debounce') || content.includes('minInterval') ) { rateLimitingFound = true; } } if (rateLimitingFound) { this.passed.push('Rate limiting mechanisms found'); } else { this.issues.push({ severity: 'low', category: 'Rate Limiting', file: 'streaming.ts', description: 'No rate limiting detected for API calls', recommendation: 'Implement rate limiting to prevent abuse', }); } } /** * Check error handling */ private async checkErrorHandling(): Promise { console.log(chalk.yellow('\nāš ļø Checking error handling...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); let errorHandlingFound = 0; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); // Count try-catch blocks const tryCount = (content.match(/try\s*\{/g) || []).length; const catchCount = (content.match(/catch\s*\(/g) || []).length; if (tryCount > 0 && catchCount > 0) { errorHandlingFound++; } // Check for unhandled promises if (content.match(/\.then\(/g) && !content.match(/\.catch\(/g)) { this.issues.push({ severity: 'medium', category: 'Error Handling', file: path.relative(srcDir, file), description: 'Promise without catch handler', recommendation: 'Add .catch() to handle promise rejections', }); } } if (errorHandlingFound > 0) { this.passed.push(`Error handling found in ${errorHandlingFound} files`); } } /** * Check logging practices */ private async checkLogging(): Promise { console.log(chalk.yellow('\nšŸ“ Checking logging practices...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Check for sensitive data in logs if ( line.includes('console.log') && (line.includes('password') || line.includes('apiKey') || line.includes('secret') || line.includes('token')) ) { this.issues.push({ severity: 'high', category: 'Logging', file: path.relative(srcDir, file), line: i + 1, description: 'Potential sensitive data logging', recommendation: 'Never log passwords, API keys, or secrets', }); } } } this.passed.push('Logging practices reviewed'); } /** * Check CORS configuration */ private async checkCORS(): Promise { console.log(chalk.yellow('\n🌐 Checking CORS configuration...')); const srcDir = path.join(__dirname, '../src'); const files = this.getAllFiles(srcDir, '.ts'); let corsFound = false; for (const file of files) { const content = fs.readFileSync(file, 'utf-8'); if (content.includes('Access-Control-Allow-Origin')) { corsFound = true; // Check for unsafe CORS if (content.includes('Access-Control-Allow-Origin: *')) { this.issues.push({ severity: 'medium', category: 'CORS', file: path.relative(srcDir, file), description: 'Wildcard CORS policy detected', recommendation: 'Restrict CORS to specific origins in production', }); } } } if (corsFound) { this.passed.push('CORS configuration present'); } } /** * Get all files recursively */ private getAllFiles(dir: string, ext: string): string[] { const files: string[] = []; if (!fs.existsSync(dir)) { return files; } const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { files.push(...this.getAllFiles(fullPath, ext)); } else if (item.endsWith(ext)) { files.push(fullPath); } } return files; } /** * Generate security report */ private generateReport(): SecurityReport { const critical = this.issues.filter((i) => i.severity === 'critical').length; const high = this.issues.filter((i) => i.severity === 'high').length; const medium = this.issues.filter((i) => i.severity === 'medium').length; const low = this.issues.filter((i) => i.severity === 'low').length; return { timestamp: new Date(), totalIssues: this.issues.length, critical, high, medium, low, issues: this.issues, passed: this.passed, }; } } // ============================================================================ // Report Generation // ============================================================================ function printReport(report: SecurityReport): void { console.log(chalk.bold.cyan('\n\n' + '═'.repeat(60))); console.log(chalk.bold.cyan('Security Report')); console.log(chalk.bold.cyan('═'.repeat(60))); console.log(chalk.gray(`Generated: ${report.timestamp.toLocaleString()}\n`)); // Summary console.log(chalk.bold('Summary:')); console.log(` Total Issues: ${report.totalIssues}`); console.log(` ${chalk.red('Critical:')} ${report.critical}`); console.log(` ${chalk.yellow('High:')} ${report.high}`); console.log(` ${chalk.blue('Medium:')} ${report.medium}`); console.log(` ${chalk.gray('Low:')} ${report.low}`); // Passed checks console.log(chalk.bold.green('\nāœ“ Passed Checks:')); report.passed.forEach((check) => { console.log(chalk.green(` āœ“ ${check}`)); }); // Issues if (report.issues.length > 0) { console.log(chalk.bold.red('\nāœ— Issues Found:')); const sortedIssues = report.issues.sort((a, b) => { const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 }; return severityOrder[a.severity] - severityOrder[b.severity]; }); sortedIssues.forEach((issue, index) => { const severityColor = issue.severity === 'critical' ? chalk.red : issue.severity === 'high' ? chalk.yellow : issue.severity === 'medium' ? chalk.blue : chalk.gray; console.log(`\n${index + 1}. ${severityColor(`[${issue.severity.toUpperCase()}]`)} ${issue.category}`); console.log(` File: ${issue.file}${issue.line ? `:${issue.line}` : ''}`); console.log(` ${chalk.gray(issue.description)}`); console.log(` ${chalk.green('→')} ${issue.recommendation}`); }); } // Overall status console.log(chalk.bold.cyan('\n' + '═'.repeat(60))); if (report.critical > 0) { console.log(chalk.red.bold('āŒ SECURITY AUDIT FAILED')); console.log(chalk.red(`Critical issues must be fixed before deployment`)); } else if (report.high > 0) { console.log(chalk.yellow.bold('āš ļø SECURITY AUDIT WARNING')); console.log(chalk.yellow(`High-priority issues should be addressed`)); } else if (report.issues.length > 0) { console.log(chalk.blue.bold('āœ“ SECURITY AUDIT PASSED')); console.log(chalk.blue(`Minor issues can be addressed incrementally`)); } else { console.log(chalk.green.bold('āœ… SECURITY AUDIT PASSED')); console.log(chalk.green(`No security issues detected`)); } console.log(chalk.bold.cyan('═'.repeat(60) + '\n')); } // ============================================================================ // Main // ============================================================================ async function main() { const checker = new SecurityChecker(); const report = await checker.runAllChecks(); printReport(report); // Save report const reportPath = path.join(__dirname, '../../security-report.json'); fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); console.log(chalk.gray(`Full report saved to: ${reportPath}\n`)); // Exit with appropriate code if (report.critical > 0) { process.exit(1); } else if (report.high > 0) { process.exit(1); } } if (require.main === module) { main().catch((error) => { console.error(chalk.red('Security check failed:'), error); process.exit(1); }); } export { SecurityChecker, SecurityReport, SecurityIssue };