Skip to content

Commit 913d2dd

Browse files
committed
more file types
1 parent 37a9fee commit 913d2dd

File tree

6 files changed

+276
-119
lines changed

6 files changed

+276
-119
lines changed

netlify/functions/scan-progress.js

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,52 @@
1+
import { getProgressForScan } from './utils/progressHandler.js';
2+
13
export const handler = async (event, context) => {
2-
if (event.requestContext.eventType === 'CONNECT') {
3-
// Handle WebSocket connection
4+
const headers = {
5+
'Access-Control-Allow-Origin': '*',
6+
'Access-Control-Allow-Headers': 'Content-Type',
7+
'Access-Control-Allow-Methods': 'GET, OPTIONS'
8+
};
9+
10+
// Handle preflight requests
11+
if (event.httpMethod === 'OPTIONS') {
412
return {
5-
statusCode: 200,
6-
body: 'Connected'
13+
statusCode: 204,
14+
headers
715
};
816
}
917

10-
if (event.requestContext.eventType === 'DISCONNECT') {
11-
// Handle WebSocket disconnection
18+
// Only allow GET requests
19+
if (event.httpMethod !== 'GET') {
1220
return {
13-
statusCode: 200,
14-
body: 'Disconnected'
21+
statusCode: 405,
22+
headers,
23+
body: JSON.stringify({ error: 'Method not allowed' })
1524
};
1625
}
1726

18-
if (event.requestContext.eventType === 'MESSAGE') {
19-
// Handle incoming messages (if needed)
27+
try {
28+
const scanId = event.queryStringParameters?.scanId;
29+
30+
if (!scanId) {
31+
return {
32+
statusCode: 400,
33+
headers,
34+
body: JSON.stringify({ error: 'Scan ID is required' })
35+
};
36+
}
37+
38+
const progress = await getProgressForScan(scanId);
39+
2040
return {
2141
statusCode: 200,
22-
body: 'Message received'
42+
headers,
43+
body: JSON.stringify(progress)
44+
};
45+
} catch (error) {
46+
return {
47+
statusCode: 500,
48+
headers,
49+
body: JSON.stringify({ error: error.message })
2350
};
2451
}
25-
26-
return {
27-
statusCode: 400,
28-
body: 'Unknown event type'
29-
};
30-
};
52+
};

netlify/functions/scan-repository.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,20 +155,37 @@ export const handler = async (event, context) => {
155155
// Start recursive scan from the initial path
156156
const allFindings = await scanDirectory(path);
157157

158+
// Process findings to match client-side data structure
159+
const processedFindings = allFindings.reduce((acc, finding) => {
160+
const key = finding.type;
161+
if (!acc[key]) {
162+
acc[key] = {
163+
type: finding.type,
164+
severity: finding.severity || 'LOW',
165+
description: finding.description || 'No description provided',
166+
allLineNumbers: { [finding.file]: finding.lineNumbers || [] }
167+
};
168+
} else {
169+
// Merge line numbers if same type
170+
const file = finding.file;
171+
if (!acc[key].allLineNumbers[file]) {
172+
acc[key].allLineNumbers[file] = finding.lineNumbers || [];
173+
} else {
174+
const merged = new Set([...acc[key].allLineNumbers[file], ...finding.lineNumbers]);
175+
acc[key].allLineNumbers[file] = Array.from(merged).sort((a, b) => a - b);
176+
}
177+
}
178+
return acc;
179+
}, {});
180+
158181
// Generate report
159182
const report = scannerInstance.generateReport(allFindings);
160183

161184
return {
162185
statusCode: 200,
163186
headers,
164187
body: JSON.stringify({
165-
files: files.map(f => ({
166-
name: f.name,
167-
path: f.path,
168-
type: f.type,
169-
size: f.size
170-
})),
171-
findings: allFindings,
188+
findings: processedFindings,
172189
summary: report.summary,
173190
rateLimit: rateLimit.data.rate
174191
})

netlify/functions/utils/progressHandler.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// In-memory progress tracking (will be lost on function restart)
2+
const scanProgress = new Map();
3+
14
export class ProgressHandler {
25
constructor(callback) {
36
this.callback = callback;
@@ -40,4 +43,38 @@ export class ProgressHandler {
4043
});
4144
}
4245
}
43-
}
46+
}
47+
48+
export const updateProgress = (scanId, data) => {
49+
scanProgress.set(scanId, {
50+
...data,
51+
timestamp: Date.now()
52+
});
53+
};
54+
55+
export const getProgressForScan = async (scanId) => {
56+
const progress = scanProgress.get(scanId);
57+
58+
if (!progress) {
59+
return {
60+
status: 'unknown',
61+
message: 'No progress data found for this scan'
62+
};
63+
}
64+
65+
// Clear old progress data after 1 hour
66+
if (Date.now() - progress.timestamp > 3600000) {
67+
scanProgress.delete(scanId);
68+
return {
69+
status: 'expired',
70+
message: 'Scan progress data has expired'
71+
};
72+
}
73+
74+
return {
75+
status: progress.status,
76+
current: progress.current,
77+
total: progress.total,
78+
message: progress.message
79+
};
80+
};

src/components/ScanResults.jsx

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect } from 'react';
22
import { patterns, patternCategories, recommendations } from '../lib/patterns';
3-
import { scan } from '../lib/patterns'; // Update this line
3+
44

55
// Severity sort order
66
const severityOrder = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
@@ -30,39 +30,14 @@ const ScanResults = ({ files, onRefreshRequest, scanning }) => {
3030

3131
const { findings = {}, summary = {} } = results;
3232

33-
// Group by (description+severity) just like before
34-
const groupedFindings = Object.entries(findings).reduce((acc, [type, data]) => {
35-
const description = data.description || 'No description provided';
36-
const severity = data.severity || 'LOW';
37-
const key = `${description}_${severity}`;
38-
39-
if (!acc[key]) {
40-
acc[key] = {
41-
type,
42-
description,
43-
severity,
44-
files: [],
45-
allLineNumbers: {},
46-
...data
47-
};
48-
} else {
49-
// Merge file line data if same description & severity
50-
Object.entries(data.allLineNumbers || {}).forEach(([file, lines]) => {
51-
if (!acc[key].allLineNumbers[file]) {
52-
acc[key].allLineNumbers[file] = lines;
53-
} else {
54-
const merged = new Set([...acc[key].allLineNumbers[file], ...lines]);
55-
acc[key].allLineNumbers[file] = Array.from(merged).sort((a, b) => a - b);
56-
}
57-
});
58-
}
59-
return acc;
60-
}, {});
61-
62-
// Convert to array, gather line counts, etc.
63-
const vulnerabilities = Object.values(groupedFindings).map((v) => {
64-
const filesSorted = Object.keys(v.allLineNumbers).sort();
65-
return { ...v, files: filesSorted };
33+
// Convert findings object to array for processing
34+
const vulnerabilities = Object.entries(findings).map(([type, data]) => {
35+
const filesSorted = Object.keys(data.allLineNumbers).sort();
36+
return {
37+
...data,
38+
type,
39+
files: filesSorted
40+
};
6641
});
6742

6843
// Sort by severity first

src/components/ScannerUI.jsx

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,50 +26,143 @@ const ScannerUI = () => {
2626
const files = Array.from(event.target.files);
2727
if (files.length === 0) return;
2828

29+
// Validate files first
30+
const validFiles = files.filter(file => {
31+
// Check file size (max 10MB)
32+
if (file.size > 10 * 1024 * 1024) {
33+
setError(`File ${file.name} is too large (max 10MB)`);
34+
return false;
35+
}
36+
37+
// Check file type - extensive list of file types that could contain vulnerabilities
38+
const validExtensions = [
39+
// Web/Frontend
40+
'.js', '.jsx', '.ts', '.tsx', '.html', '.htm', '.css', '.scss', '.sass', '.less', '.vue', '.svelte',
41+
// Backend
42+
'.py', '.rb', '.php', '.java', '.jsp', '.asp', '.aspx', '.cs', '.go', '.rs', '.scala', '.kt', '.kts',
43+
// Configuration/Infrastructure
44+
'.xml', '.yaml', '.yml', '.json', '.toml', '.ini', '.conf', '.config', '.env', '.properties',
45+
'.dockerfile', 'dockerfile', '.docker-compose.yml', '.docker-compose.yaml',
46+
// Shell/Scripts
47+
'.sh', '.bash', '.zsh', '.bat', '.cmd', '.ps1', '.psm1',
48+
// Database
49+
'.sql', '.graphql', '.prisma',
50+
// Mobile
51+
'.swift', '.m', '.h', '.mm', '.kotlin', '.gradle',
52+
// Other
53+
'.pl', '.pm', '.t', '.perl', '.cgi', // Perl
54+
'.lua', // Lua
55+
'.r', '.rmd', // R
56+
'.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', // C/C++
57+
'.ex', '.exs', // Elixir
58+
'.erl', '.hrl', // Erlang
59+
'.hs', '.lhs', // Haskell
60+
'.ml', '.mli', // OCaml
61+
'.fs', '.fsx', '.fsi', // F#
62+
// Template files
63+
'.ejs', '.pug', '.jade', '.hbs', '.mustache', '.twig', '.liquid',
64+
// Build/Package files
65+
'package.json', 'package-lock.json', 'yarn.lock', 'pom.xml', 'build.gradle',
66+
'requirements.txt', 'pipfile', 'gemfile', 'cargo.toml', 'mix.exs'
67+
];
68+
69+
const ext = file.name.toLowerCase();
70+
const isValidExtension = validExtensions.some(validExt => {
71+
if (validExt.startsWith('.')) {
72+
return ext.endsWith(validExt);
73+
}
74+
// For exact filename matches (like 'dockerfile')
75+
return ext === validExt;
76+
});
77+
78+
if (!isValidExtension) {
79+
setError(`File type ${ext} is not supported`);
80+
return false;
81+
}
82+
83+
return true;
84+
});
85+
86+
if (validFiles.length === 0) {
87+
setError('No valid files to scan');
88+
return;
89+
}
90+
2991
setScanning(true);
3092
setError(null);
31-
setProgress({ current: 0, total: files.length });
93+
setProgress({ current: 0, total: validFiles.length });
3294
setScanResults(null);
3395
setUsedCache(false);
3496

3597
try {
3698
const scanner = new VulnerabilityScanner({
3799
enableNewPatterns: true,
38-
enablePackageScanners: true
100+
enablePackageScanners: true,
101+
onProgress: (current, total) => {
102+
setProgress({ current, total });
103+
}
39104
});
40105

41106
let allFindings = [];
42107
let processedFiles = 0;
43108

44-
for (const file of files) {
45-
try {
46-
const content = await file.text();
47-
const fileFindings = await scanner.scanFile(content, file.name);
48-
allFindings.push(...fileFindings);
109+
// Process files in batches to avoid memory issues
110+
const batchSize = 5;
111+
for (let i = 0; i < validFiles.length; i += batchSize) {
112+
const batch = validFiles.slice(i, i + batchSize);
113+
const batchPromises = batch.map(async (file) => {
114+
try {
115+
const content = await file.text();
116+
const fileFindings = await scanner.scanFile(content, file.name);
117+
return { file: file.name, findings: fileFindings };
118+
} catch (err) {
119+
console.error(`Error scanning file ${file.name}:`, err);
120+
return { file: file.name, error: err.message };
121+
}
122+
});
123+
124+
const batchResults = await Promise.all(batchPromises);
125+
batchResults.forEach(result => {
126+
if (result.findings) {
127+
allFindings.push(...result.findings);
128+
}
49129
processedFiles++;
50-
setProgress({ current: processedFiles, total: files.length });
51-
} catch (err) {
52-
console.error(`Error scanning file ${file.name}:`, err);
53-
}
130+
setProgress({ current: processedFiles, total: validFiles.length });
131+
});
54132
}
55133

56-
// Process findings to ensure proper structure and maintain array format
57-
const processedFindings = allFindings.map(finding => ({
58-
...finding,
59-
severity: finding.severity || 'LOW',
60-
description: finding.description || 'No description provided',
61-
allLineNumbers: { [finding.file]: finding.lineNumbers || [] }
62-
}));
134+
// Process findings to ensure proper structure
135+
const processedFindings = allFindings.reduce((acc, finding) => {
136+
const key = finding.type;
137+
if (!acc[key]) {
138+
acc[key] = {
139+
type: finding.type,
140+
severity: finding.severity || 'LOW',
141+
description: finding.description || 'No description provided',
142+
allLineNumbers: { [finding.file]: finding.lineNumbers || [] }
143+
};
144+
} else {
145+
// Merge line numbers if same type
146+
const file = finding.file;
147+
if (!acc[key].allLineNumbers[file]) {
148+
acc[key].allLineNumbers[file] = finding.lineNumbers || [];
149+
} else {
150+
const merged = new Set([...acc[key].allLineNumbers[file], ...finding.lineNumbers]);
151+
acc[key].allLineNumbers[file] = Array.from(merged).sort((a, b) => a - b);
152+
}
153+
}
154+
return acc;
155+
}, {});
63156

64-
const report = scanner.generateReport(processedFindings);
157+
const report = scanner.generateReport(allFindings);
65158
setScanResults({
66-
...report,
67-
findings: processedFindings // Keep findings as array for local scans
159+
findings: processedFindings,
160+
summary: report.summary || {}
68161
});
69162

70163
const { criticalIssues = 0, highIssues = 0, mediumIssues = 0, lowIssues = 0 } = report.summary || {};
71164
setSuccessMessage(
72-
`Scan complete! Found ${processedFindings.length} potential vulnerabilities ` +
165+
`Scan complete! Found ${Object.keys(processedFindings).length} potential vulnerabilities ` +
73166
`(${criticalIssues} critical, ${highIssues} high, ${mediumIssues} medium, ${lowIssues} low)`
74167
);
75168
} catch (err) {

0 commit comments

Comments
 (0)