Spaces:
Running
Running
File size: 5,888 Bytes
f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 88ae80a 4700f34 88ae80a 4700f34 88ae80a 4700f34 88ae80a 4700f34 88ae80a 6c57ac0 88ae80a 6c57ac0 88ae80a 4700f34 88ae80a d43f6e6 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 4700f34 f8e3ab3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | /**
* Diff Analyzer
* Maps n8n vulnerability data to the format expected by the VSCode extension.
* Falls back to basic diff detection if n8n returns empty vulnerability arrays.
*/
/**
* Severity mapping from n8n types to extension severity levels
*/
const SEVERITY_MAP = {
'SQL Injection': 'critical',
'Command Injection': 'critical',
'Insecure Deserialization': 'critical',
'Hardcoded Credentials': 'high',
'Path Traversal': 'high',
'XSS (Cross-Site Scripting)': 'high',
'Weak Cryptography': 'high',
'Debug Mode Enabled': 'medium',
'Insecure Random': 'medium',
'Missing Input Validation': 'medium',
};
/**
* Map n8n severity string to extension severity level
*/
function normalizeSeverity(severity, type) {
if (!severity) return SEVERITY_MAP[type] || 'medium';
const s = severity.toLowerCase();
if (s === 'high' || s === 'critical') return SEVERITY_MAP[type] || 'high';
if (s === 'medium') return 'medium';
if (s === 'low') return 'low';
return 'medium';
}
/**
* Extract individual vulnerabilities from n8n response data.
* Primary strategy: use n8n's per-vulnerability mappings directly.
* Fallback: basic diff detection if n8n data is incomplete.
*
* @param {string} original - Original source code
* @param {string} corrected - Corrected code from n8n
* @param {string} filePath - File path for display
* @param {Array} n8nVulnerabilities - Vulnerability array from n8n workflow
* @param {Array} n8nVulnDetails - Vulnerability details from n8n (type + line)
* @returns {Array} Vulnerability objects for the extension
*/
function extractVulnerabilities(original, corrected, filePath, n8nVulnerabilities = [], n8nVulnDetails = []) {
// ββ Strategy 1: Direct n8n vulnerability mapping ββββββββββββββββββββββββββ
if (n8nVulnerabilities && n8nVulnerabilities.length > 0) {
console.log(`[DIFF] Mapping ${n8nVulnerabilities.length} vulnerabilities from n8n`);
const vulns = n8nVulnerabilities.map(nv => {
const originalCode = nv.original_code || nv.originalCode || '';
const fixedCode = nv.fixed_code || nv.fixedCode || '';
const lineNumber = nv.line_number || nv.line || 0;
const type = nv.type || 'Security Issue';
const severity = normalizeSeverity(nv.severity, type);
const issueText = nv.issue_text || nv.fix_recommendation || `${type} detected`;
// Resolve line number from code if not provided
let line = lineNumber;
if (!line && originalCode) {
// Find the line by matching the trimmed code content line-by-line
const originalLines = original.split('\n');
const searchLines = originalCode.trim().split('\n');
// Try to find exact match first
for (let i = 0; i <= originalLines.length - searchLines.length; i++) {
let match = true;
for (let j = 0; j < searchLines.length; j++) {
if (originalLines[i + j].trim() !== searchLines[j].trim()) {
match = false;
break;
}
}
if (match) {
line = i + 1; // Convert to 1-indexed
break;
}
}
// If no exact match, try finding by the first significant line
if (!line && searchLines.length > 0) {
const firstSignificantLine = searchLines[0].trim();
if (firstSignificantLine) {
for (let i = 0; i < originalLines.length; i++) {
if (originalLines[i].trim() === firstSignificantLine) {
line = i + 1; // Convert to 1-indexed
console.warn(`[DIFF] Used fuzzy match for line number: ${line}`);
break;
}
}
}
}
}
// Validate line number
if (!line || line < 1) {
console.error(`[DIFF] Invalid line number: ${line} for type: ${type}`);
line = 1; // Fallback to line 1
}
const endLine = line + (originalCode ? originalCode.split('\n').length - 1 : 0);
return {
file: filePath,
line,
endLine,
type,
severity,
description: `${type} at line ${line}: ${issueText}`,
originalCode,
fixedCode,
status: 'analyzed',
isFixed: false,
result: fixedCode
};
});
// Sort by line number
vulns.sort((a, b) => a.line - b.line);
return vulns;
}
// ββ Strategy 2: Fallback β code changed but no vulnerability data βββββββββ
if (original !== corrected) {
console.log('[DIFF] No n8n vulnerability data, creating generic vulnerability from diff');
return [{
file: filePath,
line: 1,
endLine: original.split('\n').length,
type: 'Security Issues',
severity: 'medium',
description: 'Multiple security improvements applied',
originalCode: original,
fixedCode: corrected,
status: 'analyzed',
isFixed: false,
result: corrected
}];
}
// No changes detected
return [];
}
/**
* Apply a specific vulnerability fix to the full file code.
* Used by the /api/apply-individual-fix endpoint.
*
* @param {string} fullCode - Full file code
* @param {Object} vulnerability - Vulnerability object with line, endLine, fixedCode
* @returns {string} Code with the fix applied
*/
function applyIndividualFix(fullCode, vulnerability) {
const lines = fullCode.split('\n');
const fixedLines = vulnerability.fixedCode.split('\n');
const startIdx = (vulnerability.line || 1) - 1;
const endIdx = (vulnerability.endLine || vulnerability.line || 1) - 1;
const deleteCount = endIdx - startIdx + 1;
lines.splice(startIdx, deleteCount, ...fixedLines);
return lines.join('\n');
}
module.exports = {
extractVulnerabilities,
applyIndividualFix,
normalizeSeverity,
SEVERITY_MAP
};
|