Skip to content

Commit 839d7f0

Browse files
committed
fix: improve scanner reliability and UI safety
- Add file size limits and chunked processing - Add proper timeout handling with configurable durations - Add progress tracking for chunked file processing - Fix pattern matching and findings consolidation - Add safety checks in UI components - Remove duplicate security recommendations - Fix syntax error in patterns file
1 parent 2fb0b24 commit 839d7f0

File tree

3 files changed

+432
-237
lines changed

3 files changed

+432
-237
lines changed

src/components/ScanResults.jsx

Lines changed: 142 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,71 @@ const ScanResults = ({
5959

6060
const { summary, findings, recommendedFixes, rateLimit } = results;
6161

62-
const severityOrder = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];
63-
const severityIcons = {
64-
CRITICAL: AlertTriangle,
65-
HIGH: AlertCircle,
66-
MEDIUM: AlertCircle,
67-
LOW: Info
62+
// Add safety check for findings
63+
if (!findings || typeof findings !== 'object') {
64+
console.error('Invalid findings structure');
65+
return (
66+
<Alert variant="error">
67+
<AlertDescription>
68+
Invalid scan results structure. Please try again.
69+
</AlertDescription>
70+
</Alert>
71+
);
72+
}
73+
74+
// Add safety check for recommendedFixes
75+
const safeRecommendedFixes = recommendedFixes && Array.isArray(recommendedFixes)
76+
? recommendedFixes
77+
: [];
78+
79+
// Helper function to get severity icon
80+
const getSeverityIcon = (severity) => {
81+
switch(severity) {
82+
case 'CRITICAL': return <AlertTriangle className="text-red-500" />;
83+
case 'HIGH': return <AlertCircle className="text-orange-500" />;
84+
case 'MEDIUM': return <AlertCircle className="text-yellow-500" />;
85+
case 'LOW': return <Info className="text-blue-500" />;
86+
default: return <Info />;
87+
}
88+
};
89+
90+
// Helper function to consolidate findings by type
91+
const consolidateFindings = (findings) => {
92+
if (!findings || typeof findings !== 'object') {
93+
console.error('Invalid findings structure');
94+
return {};
95+
}
96+
97+
const consolidated = {};
98+
99+
try {
100+
Object.entries(findings).forEach(([category, subcategories]) => {
101+
if (!subcategories || typeof subcategories !== 'object') return;
102+
103+
Object.entries(subcategories).forEach(([subcategory, issues]) => {
104+
if (!Array.isArray(issues)) return;
105+
106+
issues.forEach(issue => {
107+
if (!issue || !issue.type) return;
108+
109+
const key = issue.type;
110+
if (!consolidated[key]) {
111+
consolidated[key] = {
112+
...issue,
113+
files: [],
114+
allLineNumbers: {}
115+
};
116+
}
117+
consolidated[key].files.push(issue.file);
118+
consolidated[key].allLineNumbers[issue.file] = issue.lineNumbers;
119+
});
120+
});
121+
});
122+
} catch (error) {
123+
console.error('Error consolidating findings:', error);
124+
}
125+
126+
return consolidated;
68127
};
69128

70129
return (
@@ -93,7 +152,7 @@ const ScanResults = ({
93152

94153
{/* Summary Grid */}
95154
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
96-
{severityOrder.map(severity => (
155+
{['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].map(severity => (
97156
<div key={severity} className={`p-4 rounded-lg ${
98157
severity === 'CRITICAL' ? 'bg-red-100' :
99158
severity === 'HIGH' ? 'bg-orange-100' :
@@ -107,78 +166,87 @@ const ScanResults = ({
107166
))}
108167
</div>
109168

110-
{/* Findings Sections */}
111-
<div className="space-y-6">
112-
{severityOrder.map(severity => {
113-
const issuesList = findings[severity] || [];
114-
if (issuesList.length === 0) return null;
115-
116-
const Icon = severityIcons[severity];
117-
118-
return (
119-
<div key={severity} className="space-y-4">
120-
<h3 className="text-lg font-semibold flex items-center">
121-
{Icon && <Icon className="h-5 w-5 mr-2" />}
122-
{severity} Findings
123-
<SeverityBadge severity={severity} count={issuesList.length} />
124-
</h3>
125-
126-
{issuesList.map((issue, index) => (
127-
<div key={`${issue.type}-${index}`} className="p-4 rounded-lg bg-white border">
128-
<div className="space-y-2">
129-
<div className="font-medium text-gray-900">{issue.type}</div>
130-
<div className="text-sm text-gray-600">{issue.description}</div>
131-
<div className="text-sm">
132-
<span className="font-medium">File: </span>
133-
<code className="px-2 py-1 bg-gray-100 rounded">{issue.file}</code>
169+
{/* Consolidated Findings */}
170+
<div className="space-y-8">
171+
{Object.entries(consolidateFindings(findings)).map(([type, finding]) => (
172+
<div key={type} className="bg-white rounded-lg shadow p-6">
173+
<div className="flex items-start">
174+
{getSeverityIcon(finding.severity)}
175+
<div className="ml-3 flex-1">
176+
<h3 className="text-lg font-medium">
177+
{type}
178+
{finding.subcategory && (
179+
<span className="text-sm text-gray-500 ml-2">
180+
CWE-{finding.subcategory}
181+
</span>
182+
)}
183+
</h3>
184+
<p className="text-gray-600 mt-1">{finding.description}</p>
185+
186+
{/* Affected Files */}
187+
<div className="mt-4 space-y-2">
188+
{Object.entries(finding.allLineNumbers).map(([file, lines]) => (
189+
<div key={file} className="text-sm">
190+
<code className="bg-gray-100 px-2 py-1 rounded">
191+
{file}
192+
</code>
193+
{lines?.length > 0 && (
194+
<span className="ml-2 text-gray-600">
195+
Line{lines.length > 1 ? 's' : ''}: {lines.join(', ')}
196+
</span>
197+
)}
134198
</div>
135-
{Array.isArray(issue.lineNumbers) && issue.lineNumbers.length > 0 && (
136-
<div className="text-sm">
137-
<span className="font-medium">Line{issue.lineNumbers.length > 1 ? 's' : ''}: </span>
138-
<code className="px-2 py-1 bg-gray-100 rounded">
139-
{issue.lineNumbers.join(', ')}
140-
</code>
141-
</div>
142-
)}
143-
{issue.recommendation && (
144-
<Alert className="mt-2">
145-
<AlertDescription>{issue.recommendation}</AlertDescription>
146-
</Alert>
147-
)}
148-
</div>
199+
))}
149200
</div>
150-
))}
201+
202+
{/* Recommendation */}
203+
{finding.recommendation && (
204+
<Alert className="mt-4" variant="info">
205+
<AlertDescription>
206+
{finding.recommendation}
207+
</AlertDescription>
208+
</Alert>
209+
)}
210+
</div>
151211
</div>
152-
);
153-
})}
212+
</div>
213+
))}
154214
</div>
155215

156-
{/* Recommended Fixes Section */}
157-
{recommendedFixes && recommendedFixes.length > 0 && (
158-
<div className="space-y-4">
159-
<h3 className="text-lg font-semibold flex items-center">
160-
<CheckCircle className="h-5 w-5 mr-2" />
161-
Recommended Security Fixes
162-
</h3>
163-
{recommendedFixes.map((fix, index) => (
164-
<div key={index} className="p-4 rounded-lg bg-white border">
165-
<div className="space-y-3">
166-
<div className="font-medium text-gray-900 flex items-center">
167-
<AlertTriangle className="h-4 w-4 mr-2 text-orange-500" />
168-
{fix.type}
169-
</div>
170-
<div className="text-sm space-y-2">
171-
<div className="font-medium">Mitigation Steps:</div>
172-
<div className="text-gray-700">{fix.recommendation?.toString()}</div>
173-
{fix.references && fix.references.length > 0 && (
216+
{/* References Section */}
217+
{safeRecommendedFixes.length > 0 && (
218+
<div className="bg-white rounded-lg shadow p-6">
219+
<h2 className="text-xl font-semibold mb-4">
220+
Security References & Mitigation
221+
</h2>
222+
<div className="space-y-4">
223+
{Array.from(new Set(safeRecommendedFixes.map(fix => fix.type))).map(type => {
224+
const fix = safeRecommendedFixes.find(f => f.type === type);
225+
return (
226+
<div key={type} className="border-t pt-4 first:border-t-0 first:pt-0">
227+
<h3 className="font-medium">
228+
{type}
229+
{fix.cwe && (
230+
<a
231+
href={`https://cwe.mitre.org/data/definitions/${fix.cwe}.html`}
232+
target="_blank"
233+
rel="noopener noreferrer"
234+
className="ml-2 text-sm text-blue-600 hover:text-blue-800"
235+
>
236+
(CWE-{fix.cwe})
237+
</a>
238+
)}
239+
</h3>
240+
<p className="text-gray-600 mt-1">{fix.recommendation}</p>
241+
{fix.references?.length > 0 && (
174242
<div className="mt-2">
175-
<div className="font-medium text-sm">Security References:</div>
176-
<ul className="list-disc pl-4 text-sm text-gray-600 space-y-1">
177-
{fix.references?.map((ref, i) => (
243+
<div className="text-sm font-medium">Additional Resources:</div>
244+
<ul className="list-disc pl-5 text-sm text-gray-600">
245+
{fix.references.map((ref, i) => (
178246
<li key={i}>
179247
<a
180-
href={ref.url}
181-
target="_blank"
248+
href={ref.url}
249+
target="_blank"
182250
rel="noopener noreferrer"
183251
className="text-blue-600 hover:text-blue-800"
184252
>
@@ -192,23 +260,10 @@ const ScanResults = ({
192260
</ul>
193261
</div>
194262
)}
195-
{fix.cwe && (
196-
<div className="mt-2">
197-
<div className="font-medium text-sm">CWE Reference:</div>
198-
<a
199-
href={`https://cwe.mitre.org/data/definitions/${fix.cwe}.html`}
200-
target="_blank"
201-
rel="noopener noreferrer"
202-
className="text-sm text-blue-600 hover:text-blue-800"
203-
>
204-
CWE-{fix.cwe?.toString()}
205-
</a>
206-
</div>
207-
)}
208263
</div>
209-
</div>
210-
</div>
211-
))}
264+
);
265+
})}
266+
</div>
212267
</div>
213268
)}
214269

0 commit comments

Comments
 (0)