Skip to content

Commit 396ea49

Browse files
author
EL BADOURI Youssef
committed
the scan is shown correctly and requirements fixed
1 parent c678bb9 commit 396ea49

File tree

3 files changed

+163
-81
lines changed

3 files changed

+163
-81
lines changed

backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ google-api-core
1717
google-generativeai
1818
transformers
1919
torch
20+
flask_sqlalchemy

frontend/src/components/VulnerabilityScanner/CodeVulnerabilityScanner.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ const CodeVulnerabilityScanner = ({ setResult, isLoading, setIsLoading, userId,
3232
const errorData = await response.json();
3333
throw new Error(errorData.error || "Error during code vulnerability scan");
3434
}
35+
3536

3637
const data = await response.json();
38+
console.log(data);
3739
setResult(data);
3840
toast.success("Code vulnerability scan completed successfully", {
3941
position: "top-right",

frontend/src/components/VulnerabilityScanner/ResultDisplay.jsx

Lines changed: 160 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,109 +2,193 @@ import React, { useState } from "react";
22
import { toast } from "react-toastify";
33
import { DocumentDuplicateIcon } from "@heroicons/react/24/outline";
44

5-
const ResultDisplay = ({ result, item_name}) => {
6-
const [activeTab, setActiveTab] = useState("vulnerabilities"); // Default tab: Vulnerabilities
5+
const ResultDisplay = ({ result, item_name }) => {
6+
const [activeTab, setActiveTab] = useState("vulnerabilities");
77
console.log(result);
88

9-
if (!result) return <p className="text-gray-400">Result Not available </p>;
9+
if (!result) return <p className="text-gray-400">Result Not available</p>;
1010

1111
const handleCopy = () => {
1212
navigator.clipboard.writeText(JSON.stringify(result, null, 2)).then(() => {
1313
toast.success("Result copied", { position: "top-right", autoClose: 2000, theme: "light" });
1414
});
1515
};
1616

17-
const renderResults = (results) => {
18-
// Handle array of results (legacy support for multi-repo)
19-
if (Array.isArray(results)) {
20-
return results.map((item, index) => (
21-
<div key={index} className="mb-4">
22-
<h3 className="text-lg font-semibold text-gray-100">{item.repo}</h3>
17+
// -------- Normalization --------
18+
const normalize = (raw) => {
19+
// If array: legacy multi-repo
20+
if (Array.isArray(raw)) {
21+
return raw.map((item) => ({
22+
repo: item.repo,
23+
...(item.error
24+
? { error: item.error, vulnerabilities: [], summary: { total: 0 }, files_scanned: [] }
25+
: normalize(item.data)),
26+
}));
27+
}
28+
29+
// If {repo, data}: single repo wrapper
30+
if (raw && raw.repo && raw.data) {
31+
const inner = normalize(raw.data);
32+
return { repo: raw.repo, ...inner };
33+
}
34+
35+
// If single-file shape { file_path, vulnerabilities }
36+
if (raw && raw.file_path) {
37+
const vulns = (raw.vulnerabilities || []).map((v) => ({
38+
id: v.rule_id || v.id || v.check_id,
39+
message: v.message || v.title,
40+
severity: (v.severity || "").toUpperCase(),
41+
file_path: v.file_path || raw.file_path,
42+
line: v.line ?? (Array.isArray(v.file_line_range) ? v.file_line_range[0] : undefined),
43+
fix: v.fix || v.recommendation || v.suggestion,
44+
}));
45+
return {
46+
vulnerabilities: vulns,
47+
summary: makeSummary(vulns),
48+
files_scanned: [raw.file_path],
49+
};
50+
}
51+
52+
// If semgrep-like { results: { failed_checks: [...] } } OR directly { failed_checks: [...] }
53+
const failedChecks =
54+
raw?.results?.failed_checks ||
55+
raw?.failed_checks ||
56+
raw?.results?.vulnerabilities || // fallback if your backend renames later
57+
raw?.vulnerabilities ||
58+
[];
59+
60+
const vulns = failedChecks.map((fc) => ({
61+
id: fc.rule_id || fc.id || fc.check_id,
62+
message: fc.message || fc.title,
63+
severity: (fc.severity || "").toUpperCase(), // e.g., WARNING / LOW / MEDIUM / HIGH
64+
file_path: fc.file_path || fc.path || "unknown",
65+
line: fc.line ?? (Array.isArray(fc.file_line_range) ? fc.file_line_range[0] : undefined),
66+
fix: fc.fix || fc.recommendation || fc.suggestion,
67+
}));
68+
69+
// Files scanned (unique)
70+
const files_scanned = Array.from(new Set(vulns.map((v) => v.file_path).filter(Boolean)));
71+
72+
return {
73+
vulnerabilities: vulns,
74+
summary: makeSummary(vulns),
75+
files_scanned,
76+
};
77+
};
78+
79+
const makeSummary = (vulns) => {
80+
const s = { total: vulns.length, high: 0, medium: 0, low: 0, warning: 0 };
81+
vulns.forEach((v) => {
82+
const sev = (v.severity || "").toUpperCase();
83+
if (sev === "HIGH" || sev === "CRITICAL") s.high += 1;
84+
else if (sev === "MEDIUM") s.medium += 1;
85+
else if (sev === "LOW") s.low += 1;
86+
else if (sev === "WARNING") s.warning += 1;
87+
});
88+
return s;
89+
};
90+
91+
const normalized = normalize(result);
92+
93+
const renderResults = (resultsLike) => {
94+
// Array => multi repo
95+
if (Array.isArray(resultsLike)) {
96+
return resultsLike.map((item, idx) => (
97+
<div key={idx} className="mb-4">
98+
{item.repo && <h3 className="text-lg font-semibold text-gray-100">{item.repo}</h3>}
2399
{item.error ? (
24-
<p className="text-red-400">Error : {item.error}</p>
100+
<p className="text-red-400">Error: {item.error}</p>
25101
) : (
26-
renderSingleResult(item.data)
102+
renderSingleResult(item)
27103
)}
28104
</div>
29105
));
30106
}
31-
32-
// Handle single result from SelectedReposVulnerabilityScanner
33-
if (results.repo && results.data) {
34-
return (
35-
<div className="mb-4">
36-
<h3 className="text-lg font-semibold text-gray-100">{results.repo}</h3>
37-
{renderSingleResult(results.data)}
38-
</div>
39-
);
40-
}
41-
42-
// Handle raw vulnerability scan result
43-
return renderSingleResult(results);
107+
return renderSingleResult(resultsLike);
44108
};
45109

46110
const renderSingleResult = (data) => {
47-
// Handle single file result
48-
if (data.file_path) {
49-
const groupedVulns = {
50-
[data.file_path]: data.vulnerabilities || [],
51-
};
52-
return renderGroupedResults({ vulnerabilities: data.vulnerabilities, files_scanned: [data.file_path] }, groupedVulns);
53-
}
111+
const results = data; // already normalized => { vulnerabilities, summary, files_scanned, [repo]? }
54112

55-
// Handle directory/repo result
56-
const results = data.results || data;
57-
if (!results.vulnerabilities && !results.summary) {
113+
if (!results?.vulnerabilities?.length && !results?.summary?.total) {
58114
return <p className="text-gray-400">No vulnerabilities found.</p>;
59115
}
60116

61-
// Group vulnerabilities by file_path
62-
const groupedVulns = {};
63-
(results.vulnerabilities || []).forEach((vuln) => {
64-
const filePath = vuln.file_path || vuln.path;
65-
if (!groupedVulns[filePath]) {
66-
groupedVulns[filePath] = [];
67-
}
68-
groupedVulns[filePath].push(vuln);
117+
// Group by file
118+
const grouped = {};
119+
(results.vulnerabilities || []).forEach((v) => {
120+
const fp = v.file_path || "unknown";
121+
if (!grouped[fp]) grouped[fp] = [];
122+
grouped[fp].push(v);
69123
});
70124

71-
return renderGroupedResults(results, groupedVulns);
125+
return renderGroupedResults(results, grouped, data.repo);
72126
};
73127

74-
const renderGroupedResults = (results, groupedVulns) => {
75-
// Extract repository name from repo_url
128+
const renderGroupedResults = (results, groupedVulns, repoName) => {
129+
// repo url → item name (if you pass repo_url on parent `result`)
76130
const repoUrl = result?.repo_url;
77-
const ItemName = (repoUrl != null )? repoUrl.split("/").slice(-2).join("/")
78-
: (item_name ? item_name : "File");
131+
const ItemName =
132+
repoUrl != null
133+
? repoUrl.split("/").slice(-2).join("/")
134+
: repoName || (item_name ? item_name : "File");
135+
136+
const sevBadge = (sev) => {
137+
const s = (sev || "").toUpperCase();
138+
const base = "ml-2 px-2 py-1 rounded text-xs";
139+
if (s === "HIGH" || s === "CRITICAL") return `${base} bg-red-600`;
140+
if (s === "MEDIUM") return `${base} bg-yellow-600`;
141+
if (s === "LOW") return `${base} bg-blue-600`;
142+
if (s === "WARNING") return `${base} bg-orange-600`;
143+
return `${base} bg-gray-600`;
144+
};
79145

80146
return (
81147
<div>
82-
{/* Repository Name */}
83-
{ItemName !== "File" && <h3 className="text-lg font-semibold text-gray-100 mb-4">{ItemName}</h3>}
148+
{ItemName !== "File" && (
149+
<h3 className="text-lg font-semibold text-gray-100 mb-4">{ItemName}</h3>
150+
)}
84151

85152
{/* Summary */}
86153
<div className="mb-4">
87-
<p className="text-red-400">Vulnerabilities Found: {results.summary?.total || results.vulnerabilities?.length || 0}</p>
88-
{results.summary?.high && <p className="text-red-600">High Severity: {results.summary.high}</p>}
89-
{results.summary?.medium && <p className="text-yellow-500">Medium Severity: {results.summary.medium}</p>}
90-
{results.summary?.low && <p className="text-blue-400">Low Severity: {results.summary.low}</p>}
91-
{results.score !== undefined && <p className="text-gray-100">Security Score: {results.score}%</p>}
154+
<p className="text-red-400">
155+
Vulnerabilities Found: {results.summary?.total || results.vulnerabilities?.length || 0}
156+
</p>
157+
{results.summary?.high > 0 && (
158+
<p className="text-red-600">High Severity: {results.summary.high}</p>
159+
)}
160+
{results.summary?.medium > 0 && (
161+
<p className="text-yellow-500">Medium Severity: {results.summary.medium}</p>
162+
)}
163+
{results.summary?.low > 0 && (
164+
<p className="text-blue-400">Low Severity: {results.summary.low}</p>
165+
)}
166+
{results.summary?.warning > 0 && (
167+
<p className="text-orange-400">Warnings: {results.summary.warning}</p>
168+
)}
169+
{results.score !== undefined && (
170+
<p className="text-gray-100">Security Score: {results.score}%</p>
171+
)}
92172
</div>
93173

94174
{/* Tabs */}
95175
<div className="mb-4">
96176
<div className="flex border-b border-gray-700">
97177
<button
98178
className={`px-4 py-2 font-semibold ${
99-
activeTab === "vulnerabilities" ? "border-b-2 border-red-500 text-red-400" : "text-gray-400"
179+
activeTab === "vulnerabilities"
180+
? "border-b-2 border-red-500 text-red-400"
181+
: "text-gray-400"
100182
}`}
101183
onClick={() => setActiveTab("vulnerabilities")}
102184
>
103185
Vulnerabilities
104186
</button>
105187
<button
106188
className={`px-4 py-2 font-semibold ${
107-
activeTab === "recommendations" ? "border-b-2 border-red-500 text-red-400" : "text-gray-400"
189+
activeTab === "recommendations"
190+
? "border-b-2 border-red-500 text-red-400"
191+
: "text-gray-400"
108192
}`}
109193
onClick={() => setActiveTab("recommendations")}
110194
>
@@ -123,14 +207,11 @@ const ResultDisplay = ({ result, item_name}) => {
123207
<div key={filePath} className="mt-4">
124208
<h5 className="text-red-500 font-medium">----------------{filePath}----------------</h5>
125209
<ul className="list-disc list-inside text-gray-300">
126-
{vulns.map((vuln, idx) => (
210+
{vulns.map((v, idx) => (
127211
<li key={idx}>
128-
<span className="text-red-400">{vuln.rule_id || vuln.id}</span> - {vuln.message || vuln.title}
129-
{vuln.severity && <span className={`ml-2 px-2 py-1 rounded text-xs ${
130-
vuln.severity === 'HIGH' ? 'bg-red-600' :
131-
vuln.severity === 'MEDIUM' ? 'bg-yellow-600' : 'bg-blue-600'
132-
}`}>{vuln.severity}</span>}
133-
{vuln.line && <span> (Line: {vuln.line})</span>}
212+
<span className="text-red-400">{v.id}</span> - {v.message}
213+
{v.severity && <span className={sevBadge(v.severity)}>{v.severity}</span>}
214+
{v.line != null && <span> (Line: {v.line})</span>}
134215
</li>
135216
))}
136217
</ul>
@@ -148,11 +229,11 @@ const ResultDisplay = ({ result, item_name}) => {
148229
<div key={filePath} className="mt-4">
149230
<h5 className="text-red-700 font-medium">----------------{filePath}----------------</h5>
150231
<ul className="list-disc list-inside text-gray-300">
151-
{vulns.map((vuln, idx) => (
232+
{vulns.map((v, idx) => (
152233
<li key={idx} className="mb-2">
153-
<span className="text-red-400">{vuln.rule_id || vuln.id}</span> - {vuln.message || vuln.title}
234+
<span className="text-red-400">{v.id}</span> - {v.message}
154235
<div className="text-slate-400 mt-1">
155-
Recommendation: {vuln.fix || vuln.recommendation || "Review and fix this vulnerability"}
236+
Recommendation: {v.fix || "Review and fix this vulnerability"}
156237
</div>
157238
</li>
158239
))}
@@ -181,22 +262,20 @@ const ResultDisplay = ({ result, item_name}) => {
181262
};
182263

183264
return (
184-
<div className="mt-6 w-full max-w-260 relative h-50">
185-
<label htmlFor="vulnerability-result" className="text-gray-400 text-sm mb-2 block">
186-
Vulnerability Scan Result:
187-
</label>
188-
<div
189-
className="p-4 bg-slate-800 text-red-300 rounded-lg shadow-inner max-h-96 overflow-y-auto font-mono text-sm">
190-
{renderResults(result)}
191-
<button
192-
onClick={handleCopy}
193-
className="absolute top-8 right-6 bg-gray-700 text-white px-2 py-1 rounded hover:bg-gray-600"
194-
>
195-
<DocumentDuplicateIcon className="h-4 w-4"/>
196-
</button>
197-
</div>
198-
265+
<div className="mt-6 w-full max-w-260 relative h-50">
266+
<label htmlFor="vulnerability-result" className="text-gray-400 text-sm mb-2 block">
267+
Vulnerability Scan Result:
268+
</label>
269+
<div className="p-4 bg-slate-800 text-red-300 rounded-lg shadow-inner max-h-96 overflow-y-auto font-mono text-sm">
270+
{renderResults(normalized)}
271+
<button
272+
onClick={handleCopy}
273+
className="absolute top-8 right-6 bg-gray-700 text-white px-2 py-1 rounded hover:bg-gray-600"
274+
>
275+
<DocumentDuplicateIcon className="h-4 w-4" />
276+
</button>
199277
</div>
278+
</div>
200279
);
201280
};
202281

0 commit comments

Comments
 (0)