recoder-security
Version:
Enterprise-grade security and compliance layer for CodeCraft CLI
183 lines • 8 kB
JavaScript
/**
* Code Vulnerability Scanner
* Scans code for security vulnerabilities and issues
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeVulnerabilityScanner = void 0;
const shared_1 = require("@recoder/shared");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class CodeVulnerabilityScanner {
constructor() {
this.vulnerabilityPatterns = [
{
pattern: /(?:password|pwd|pass)\s*=\s*['""][^'""]+['""];?/gi,
severity: 'high',
category: 'hardcoded_secret',
title: 'Hardcoded Password',
description: 'Password found hardcoded in source code',
recommendation: 'Use environment variables or secure configuration files'
},
{
pattern: /(?:api_key|apikey|secret)\s*=\s*['""][^'""]+['""];?/gi,
severity: 'critical',
category: 'hardcoded_secret',
title: 'Hardcoded API Key',
description: 'API key found hardcoded in source code',
recommendation: 'Store API keys in environment variables or secure vault'
},
{
pattern: /innerHTML\s*=\s*[^;]+;?/gi,
severity: 'medium',
category: 'xss',
title: 'Potential XSS via innerHTML',
description: 'Direct innerHTML assignment may lead to XSS vulnerabilities',
recommendation: 'Use textContent or properly sanitize HTML content'
},
{
pattern: /eval\s*\([^)]+\)/gi,
severity: 'high',
category: 'injection',
title: 'Code Injection via eval()',
description: 'Use of eval() can lead to code injection vulnerabilities',
recommendation: 'Avoid eval() and use safer alternatives like JSON.parse()'
},
{
pattern: /\.\.\//g,
severity: 'medium',
category: 'path_traversal',
title: 'Path Traversal',
description: 'Directory traversal patterns detected',
recommendation: 'Validate and sanitize file paths, use path.resolve()'
},
{
pattern: /exec\s*\([^)]+\)/gi,
severity: 'high',
category: 'command_injection',
title: 'Command Injection',
description: 'Use of exec() can lead to command injection',
recommendation: 'Use safer alternatives or properly validate input'
}
];
}
async scanDirectory(dirPath) {
const vulnerabilities = [];
try {
await this.scanDirectoryRecursive(dirPath, vulnerabilities);
}
catch (error) {
shared_1.Logger.error('Error scanning directory:', error);
}
return vulnerabilities;
}
async scanFile(filePath) {
const vulnerabilities = [];
try {
const content = await fs.promises.readFile(filePath, 'utf-8');
const lines = content.split('\n');
lines.forEach((line, lineIndex) => {
this.vulnerabilityPatterns.forEach(pattern => {
const matches = line.match(pattern.pattern);
if (matches) {
matches.forEach(match => {
const columnIndex = line.indexOf(match);
vulnerabilities.push({
id: `vuln-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
title: pattern.title,
description: pattern.description,
severity: pattern.severity,
category: pattern.category,
file: filePath,
line: lineIndex + 1,
column: columnIndex + 1,
code: line.trim(),
recommendation: pattern.recommendation
});
});
}
});
});
}
catch (error) {
shared_1.Logger.error(`Error scanning file ${filePath}:`, error);
}
return vulnerabilities;
}
generateReport(vulnerabilities) {
const summary = {
critical: vulnerabilities.filter(v => v.severity === 'critical').length,
high: vulnerabilities.filter(v => v.severity === 'high').length,
medium: vulnerabilities.filter(v => v.severity === 'medium').length,
low: vulnerabilities.filter(v => v.severity === 'low').length
};
// Calculate security score (0-100)
const totalIssues = summary.critical + summary.high + summary.medium + summary.low;
const weightedScore = (summary.critical * 10) + (summary.high * 5) + (summary.medium * 2) + (summary.low * 1);
const securityScore = totalIssues === 0 ? 100 : Math.max(0, 100 - weightedScore);
return {
totalFiles: new Set(vulnerabilities.map(v => v.file)).size,
vulnerabilities,
securityScore,
summary
};
}
async scanDirectoryRecursive(dirPath, vulnerabilities) {
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
// Skip node_modules and other common directories
if (!['node_modules', '.git', 'dist', 'build', '.next'].includes(entry.name)) {
await this.scanDirectoryRecursive(fullPath, vulnerabilities);
}
}
else if (entry.isFile()) {
// Only scan code files
if (this.isCodeFile(entry.name)) {
const fileVulns = await this.scanFile(fullPath);
vulnerabilities.push(...fileVulns);
}
}
}
}
isCodeFile(filename) {
const codeExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cs', '.php', '.rb', '.go', '.rs', '.cpp', '.c', '.h'];
return codeExtensions.some(ext => filename.endsWith(ext));
}
}
exports.CodeVulnerabilityScanner = CodeVulnerabilityScanner;
//# sourceMappingURL=code-vulnerability-scanner.js.map
;