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
};