UNPKG

@ordojs/security

Version:

Security package for OrdoJS with XSS, CSRF, and injection protection

314 lines 14.4 kB
import { execSync } from 'child_process'; import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; export class SecurityAuditor { vulnerabilities = []; options; constructor(options) { this.options = { includePatterns: ['**/*.ts', '**/*.js', '**/*.ordo'], excludePatterns: ['**/node_modules/**', '**/dist/**', '**/*.test.*'], enableDependencyCheck: true, enableCodeAnalysis: true, enableConfigurationCheck: true, owaspLevel: 'standard', ...options, }; } async audit() { this.vulnerabilities = []; if (this.options.enableDependencyCheck) { await this.auditDependencies(); } if (this.options.enableCodeAnalysis) { await this.auditSourceCode(); } if (this.options.enableConfigurationCheck) { await this.auditConfiguration(); } return this.generateReport(); } async auditDependencies() { try { const packageJsonPath = join(this.options.projectPath, 'package.json'); if (!existsSync(packageJsonPath)) { return; } // Check for known vulnerable packages const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; // Run npm audit if available try { const auditResult = execSync('npm audit --json', { cwd: this.options.projectPath, encoding: 'utf-8', stdio: 'pipe' }); const audit = JSON.parse(auditResult); if (audit.vulnerabilities) { Object.entries(audit.vulnerabilities).forEach(([pkg, vuln]) => { this.vulnerabilities.push({ id: `dep-${pkg}-${vuln.via?.[0]?.source || 'unknown'}`, severity: this.mapNpmSeverity(vuln.severity), type: 'dependency', description: `Vulnerable dependency: ${pkg} - ${vuln.via?.[0]?.title || 'Unknown vulnerability'}`, recommendation: `Update ${pkg} to version ${vuln.fixAvailable?.version || 'latest'}`, owaspCategory: 'A06:2021 – Vulnerable and Outdated Components' }); }); } } catch (error) { // npm audit failed, check manually for known vulnerable packages this.checkKnownVulnerablePackages(dependencies); } } catch (error) { console.warn('Failed to audit dependencies:', error); } } checkKnownVulnerablePackages(dependencies) { const knownVulnerable = [ { name: 'lodash', versions: ['<4.17.21'], issue: 'Prototype pollution vulnerability' }, { name: 'axios', versions: ['<0.21.2'], issue: 'SSRF vulnerability' }, { name: 'express', versions: ['<4.17.3'], issue: 'Open redirect vulnerability' }, { name: 'jsonwebtoken', versions: ['<8.5.1'], issue: 'Algorithm confusion vulnerability' }, ]; Object.entries(dependencies).forEach(([pkg, version]) => { const vulnerable = knownVulnerable.find(v => v.name === pkg); if (vulnerable) { this.vulnerabilities.push({ id: `known-vuln-${pkg}`, severity: 'high', type: 'dependency', description: `Known vulnerable package: ${pkg}@${version} - ${vulnerable.issue}`, recommendation: `Update ${pkg} to a secure version`, owaspCategory: 'A06:2021 – Vulnerable and Outdated Components' }); } }); } async auditSourceCode() { const glob = await import('glob'); const files = glob.globSync(this.options.includePatterns, { cwd: this.options.projectPath, ignore: this.options.excludePatterns || [], }); for (const file of files) { await this.auditFile(join(this.options.projectPath, file)); } } async auditFile(filePath) { try { const content = readFileSync(filePath, 'utf-8'); const lines = content.split('\n'); lines.forEach((line, index) => { this.checkForXSSVulnerabilities(line, filePath, index + 1); this.checkForSQLInjection(line, filePath, index + 1); this.checkForCSRFVulnerabilities(line, filePath, index + 1); this.checkForInsecureCrypto(line, filePath, index + 1); this.checkForHardcodedSecrets(line, filePath, index + 1); }); } catch (error) { console.warn(`Failed to audit file ${filePath}:`, error); } } checkForXSSVulnerabilities(line, file, lineNumber) { // Check for dangerous innerHTML usage if (line.includes('innerHTML') && !line.includes('sanitize') && !line.includes('DOMPurify')) { this.vulnerabilities.push({ id: `xss-innerHTML-${file}-${lineNumber}`, severity: 'high', type: 'xss', description: 'Potential XSS vulnerability: Direct innerHTML usage without sanitization', file, line: lineNumber, recommendation: 'Use DOMPurify.sanitize() or textContent instead of innerHTML', owaspCategory: 'A03:2021 – Injection' }); } // Check for dangerous eval usage if (line.includes('eval(') || line.includes('Function(')) { this.vulnerabilities.push({ id: `xss-eval-${file}-${lineNumber}`, severity: 'critical', type: 'xss', description: 'Critical XSS vulnerability: Use of eval() or Function() constructor', file, line: lineNumber, recommendation: 'Avoid using eval() or Function() constructor. Use safer alternatives', owaspCategory: 'A03:2021 – Injection' }); } // Check for unescaped template literals in HTML context if (line.includes('${') && (line.includes('<') || line.includes('>'))) { this.vulnerabilities.push({ id: `xss-template-${file}-${lineNumber}`, severity: 'medium', type: 'xss', description: 'Potential XSS vulnerability: Unescaped template literal in HTML context', file, line: lineNumber, recommendation: 'Ensure template literals are properly escaped when used in HTML', owaspCategory: 'A03:2021 – Injection' }); } } checkForSQLInjection(line, file, lineNumber) { // Check for string concatenation in SQL queries const sqlKeywords = ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE']; const hasSQLKeyword = sqlKeywords.some(keyword => line.toUpperCase().includes(keyword) && line.includes('+')); if (hasSQLKeyword) { this.vulnerabilities.push({ id: `sql-injection-${file}-${lineNumber}`, severity: 'high', type: 'injection', description: 'Potential SQL injection vulnerability: String concatenation in SQL query', file, line: lineNumber, recommendation: 'Use parameterized queries or prepared statements', owaspCategory: 'A03:2021 – Injection' }); } } checkForCSRFVulnerabilities(line, file, lineNumber) { // Check for forms without CSRF protection if (line.includes('<form') && !line.includes('csrf') && !line.includes('token')) { this.vulnerabilities.push({ id: `csrf-form-${file}-${lineNumber}`, severity: 'medium', type: 'csrf', description: 'Potential CSRF vulnerability: Form without CSRF protection', file, line: lineNumber, recommendation: 'Add CSRF token to forms', owaspCategory: 'A01:2021 – Broken Access Control' }); } } checkForInsecureCrypto(line, file, lineNumber) { const insecureAlgorithms = ['md5', 'sha1', 'des', 'rc4']; const hasInsecureAlgo = insecureAlgorithms.some(algo => line.toLowerCase().includes(algo)); if (hasInsecureAlgo) { this.vulnerabilities.push({ id: `crypto-weak-${file}-${lineNumber}`, severity: 'medium', type: 'other', description: 'Weak cryptographic algorithm detected', file, line: lineNumber, recommendation: 'Use strong cryptographic algorithms like SHA-256, AES, etc.', owaspCategory: 'A02:2021 – Cryptographic Failures' }); } } checkForHardcodedSecrets(line, file, lineNumber) { const secretPatterns = [ /password\s*=\s*['"][^'"]+['"]/i, /api[_-]?key\s*=\s*['"][^'"]+['"]/i, /secret\s*=\s*['"][^'"]+['"]/i, /token\s*=\s*['"][^'"]+['"]/i, ]; secretPatterns.forEach(pattern => { if (pattern.test(line)) { this.vulnerabilities.push({ id: `hardcoded-secret-${file}-${lineNumber}`, severity: 'high', type: 'other', description: 'Hardcoded secret or credential detected', file, line: lineNumber, recommendation: 'Use environment variables or secure configuration management', owaspCategory: 'A07:2021 – Identification and Authentication Failures' }); } }); } async auditConfiguration() { // Check for security headers configuration const configFiles = ['ordojs.config.ts', 'ordojs.config.js', 'next.config.js', 'vite.config.ts']; for (const configFile of configFiles) { const configPath = join(this.options.projectPath, configFile); if (existsSync(configPath)) { const content = readFileSync(configPath, 'utf-8'); if (!content.includes('helmet') && !content.includes('security')) { this.vulnerabilities.push({ id: `config-no-security-headers-${configFile}`, severity: 'medium', type: 'configuration', description: 'Missing security headers configuration', file: configFile, recommendation: 'Configure security headers using helmet or similar middleware', owaspCategory: 'A05:2021 – Security Misconfiguration' }); } } } // Check for HTTPS configuration const packageJsonPath = join(this.options.projectPath, 'package.json'); if (existsSync(packageJsonPath)) { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); const scripts = packageJson.scripts || {}; const hasHttpsConfig = Object.values(scripts).some((script) => typeof script === 'string' && script.includes('https')); if (!hasHttpsConfig) { this.vulnerabilities.push({ id: 'config-no-https', severity: 'low', type: 'configuration', description: 'No HTTPS configuration found in development scripts', recommendation: 'Configure HTTPS for development and production environments', owaspCategory: 'A02:2021 – Cryptographic Failures' }); } } } generateReport() { const summary = this.vulnerabilities.reduce((acc, vuln) => { acc.total++; acc[vuln.severity]++; return acc; }, { total: 0, critical: 0, high: 0, medium: 0, low: 0 }); const owaspCategories = { 'A01:2021 – Broken Access Control': false, 'A02:2021 – Cryptographic Failures': false, 'A03:2021 – Injection': false, 'A04:2021 – Insecure Design': false, 'A05:2021 – Security Misconfiguration': false, 'A06:2021 – Vulnerable and Outdated Components': false, 'A07:2021 – Identification and Authentication Failures': false, 'A08:2021 – Software and Data Integrity Failures': false, 'A09:2021 – Security Logging and Monitoring Failures': false, 'A10:2021 – Server-Side Request Forgery': false, }; // Mark categories as compliant if no vulnerabilities found this.vulnerabilities.forEach(vuln => { if (vuln.owaspCategory && vuln.owaspCategory in owaspCategories) { owaspCategories[vuln.owaspCategory] = false; } }); // Calculate compliance score const compliantCategories = Object.values(owaspCategories).filter(Boolean).length; const totalCategories = Object.keys(owaspCategories).length; const complianceScore = Math.round((compliantCategories / totalCategories) * 100); return { vulnerabilities: this.vulnerabilities, summary, owaspCompliance: { score: complianceScore, categories: owaspCategories, }, timestamp: new Date(), }; } mapNpmSeverity(severity) { switch (severity?.toLowerCase()) { case 'critical': return 'critical'; case 'high': return 'high'; case 'moderate': return 'medium'; case 'low': return 'low'; default: return 'medium'; } } } //# sourceMappingURL=security-auditor.js.map