Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitecdb880

Browse files
committed
Add detailed vulnerability statistics to history view and database model
1 parent5357e9e commitecdb880

File tree

6 files changed

+304
-110
lines changed

6 files changed

+304
-110
lines changed

‎client/src/components/History/HistoryView.jsx‎

Lines changed: 147 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
Fade,
1515
Grow,
1616
useMediaQuery,
17+
Grid,
1718
}from"@mui/material";
1819
import{
1920
AccessTime,
@@ -260,85 +261,169 @@ const HistoryView = ({ currentLanguage, serverAddress }) => {
260261
{sortedFileHistory.length>0&&(
261262
<>
262263
<Typographyvariant="h6"gutterBottom>
263-
{currentLanguage==="ENG"
264-
?"Completed Reports"
265-
:"Завершенные отчеты"}
264+
{currentLanguage==="ENG" ?"Completed Reports" :"Завершенные отчеты"}
266265
</Typography>
267266
{sortedFileHistory.map((file,index)=>(
268267
<React.Fragmentkey={index}>
269268
<Fadein={true}timeout={500}>
270269
<ListItem
271270
sx={{
272271
transition:"all 0.2s",
272+
flexDirection:"column",
273+
alignItems:"flex-start",
274+
p:2,
273275
"&:hover":{
274276
bgcolor:`${theme.palette.primary.main}10`,
275277
},
276278
}}
277279
>
278-
<ListItemText
279-
primary={
280-
<Box
281-
sx={{
282-
display:"flex",
283-
alignItems:"center",
284-
gap:1,
285-
}}
286-
>
287-
<CheckCircleOutline
288-
fontSize="small"
289-
color="success"
290-
/>
291-
<Typographyvariant="subtitle1">
292-
{getInputTypeLabel(file.vulnerabilityScanner)}
293-
</Typography>
294-
{file.cveCount!==undefined&&(
295-
<Chip
296-
size="small"
280+
<Boxsx={{width:"100%",display:"flex",alignItems:"center",mb:1}}>
281+
<ListItemText
282+
primary={
283+
<Box
284+
sx={{
285+
display:"flex",
286+
alignItems:"center",
287+
gap:1,
288+
}}
289+
>
290+
<CheckCircleOutline
291+
fontSize="small"
297292
color="success"
298-
variant="outlined"
299-
label={`${file.cveCount} CVE`}
300-
sx={{ml:1}}
301293
/>
302-
)}
303-
</Box>
304-
}
305-
secondary={
306-
<>
294+
<Typographyvariant="subtitle1">
295+
{getInputTypeLabel(file.vulnerabilityScanner)}
296+
</Typography>
297+
</Box>
298+
}
299+
secondary={
307300
<Typographyvariant="body2"color="text.secondary">
308-
{currentLanguage==="ENG"
309-
?"Completed: "
310-
:"Завершено: "}
301+
{currentLanguage==="ENG" ?"Completed: " :"Завершено: "}
311302
{file.time}
312303
</Typography>
313-
<Boxsx={{mt:1,display:"flex",gap:1}}>
314-
<Chip
315-
icon={<FileDownload/>}
316-
label="HTML"
317-
size="small"
318-
color="primary"
319-
variant="outlined"
320-
onClick={(e)=>{
321-
e.stopPropagation();
322-
handleDownload(file.htmlFilename);
323-
}}
324-
clickable
325-
/>
326-
<Chip
327-
icon={<FileDownload/>}
328-
label="EXCEL"
329-
size="small"
330-
color="primary"
331-
variant="outlined"
332-
onClick={(e)=>{
333-
e.stopPropagation();
334-
handleDownload(file.excelFilename);
335-
}}
336-
clickable
337-
/>
338-
</Box>
339-
</>
340-
}
341-
/>
304+
}
305+
/>
306+
<Boxsx={{display:"flex",gap:1}}>
307+
<Chip
308+
icon={<FileDownload/>}
309+
label="HTML"
310+
size="small"
311+
color="primary"
312+
variant="outlined"
313+
onClick={(e)=>{
314+
e.stopPropagation();
315+
handleDownload(file.htmlFilename);
316+
}}
317+
clickable
318+
/>
319+
<Chip
320+
icon={<FileDownload/>}
321+
label="EXCEL"
322+
size="small"
323+
color="primary"
324+
variant="outlined"
325+
onClick={(e)=>{
326+
e.stopPropagation();
327+
handleDownload(file.excelFilename);
328+
}}
329+
clickable
330+
/>
331+
</Box>
332+
</Box>
333+
334+
{/* Vulnerability Statistics */}
335+
<Gridcontainerspacing={1}sx={{mt:1}}>
336+
<Griditemxs={12}sm={4}md={2.4}>
337+
<Paper
338+
elevation={0}
339+
sx={{
340+
p:1,
341+
bgcolor:`${theme.palette.warning.main}10`,
342+
borderRadius:1,
343+
height:'100%'
344+
}}
345+
>
346+
<Typographyvariant="caption"color="text.secondary"display="block">
347+
{currentLanguage==="ENG" ?"Total Vulnerabilities" :"Всего уязвимостей"}
348+
</Typography>
349+
<Typographyvariant="h6"color="warning.main"sx={{mt:0.5}}>
350+
{file.cveCount||0}
351+
</Typography>
352+
</Paper>
353+
</Grid>
354+
<Griditemxs={12}sm={4}md={2.4}>
355+
<Paper
356+
elevation={0}
357+
sx={{
358+
p:1,
359+
bgcolor:`${theme.palette.error.main}10`,
360+
borderRadius:1,
361+
height:'100%'
362+
}}
363+
>
364+
<Typographyvariant="caption"color="text.secondary"display="block">
365+
{currentLanguage==="ENG" ?"CISA KEV Vulnerabilities" :"Уязвимостей в каталоге CISA KEV"}
366+
</Typography>
367+
<Typographyvariant="h6"color="error.main"sx={{mt:0.5}}>
368+
{file.kevCount||0}
369+
</Typography>
370+
</Paper>
371+
</Grid>
372+
<Griditemxs={12}sm={4}md={2.4}>
373+
<Paper
374+
elevation={0}
375+
sx={{
376+
p:1,
377+
bgcolor:`${theme.palette.info.main}10`,
378+
borderRadius:1,
379+
height:'100%'
380+
}}
381+
>
382+
<Typographyvariant="caption"color="text.secondary"display="block">
383+
{currentLanguage==="ENG" ?"ExploitDB Vulnerabilities" :"Уязвимостей в ExploitDB"}
384+
</Typography>
385+
<Typographyvariant="h6"color="info.main"sx={{mt:0.5}}>
386+
{file.edbCount||0}
387+
</Typography>
388+
</Paper>
389+
</Grid>
390+
<Griditemxs={12}sm={4}md={2.4}>
391+
<Paper
392+
elevation={0}
393+
sx={{
394+
p:1,
395+
bgcolor:`${theme.palette.success.main}10`,
396+
borderRadius:1,
397+
height:'100%'
398+
}}
399+
>
400+
<Typographyvariant="caption"color="text.secondary"display="block">
401+
{currentLanguage==="ENG" ?"Nuclei Template Vulnerabilities" :"Уязвимостей с шаблоном Nuclei"}
402+
</Typography>
403+
<Typographyvariant="h6"color="success.main"sx={{mt:0.5}}>
404+
{file.nucleiCount||0}
405+
</Typography>
406+
</Paper>
407+
</Grid>
408+
<Griditemxs={12}sm={4}md={2.4}>
409+
<Paper
410+
elevation={0}
411+
sx={{
412+
p:1,
413+
bgcolor:`${theme.palette.secondary.main}10`,
414+
borderRadius:1,
415+
height:'100%'
416+
}}
417+
>
418+
<Typographyvariant="caption"color="text.secondary"display="block">
419+
{currentLanguage==="ENG" ?"Public Exploit Vulnerabilities" :"Уязвимостей с публичными эксплоитами"}
420+
</Typography>
421+
<Typographyvariant="h6"color="secondary.main"sx={{mt:0.5}}>
422+
{file.publicExploitCount||0}
423+
</Typography>
424+
</Paper>
425+
</Grid>
426+
</Grid>
342427
</ListItem>
343428
</Fade>
344429
{index<sortedFileHistory.length-1&&<Divider/>}

‎client/src/hooks/useTaskManager.jsx‎

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -88,57 +88,64 @@ export function useTaskManager(serverAddress) {
8888
localStorage.setItem("activeTasks",JSON.stringify(activeTasks));
8989
},[activeTasks]);
9090

91-
// Periodically request progress for all active tasks
92-
useEffect(()=>{
93-
// Check only if there is a server address and active tasks
94-
if(!serverAddress||Object.keys(activeTasks).length===0)return;
91+
// Function to poll task progress
92+
constpollTaskProgress=async(id)=>{
93+
if(!serverAddress)return;
9594

96-
// Function for requesting progress of all active tasks
97-
constpollAllActiveTasks=async()=>{
98-
consttaskIds=Object.keys(activeTasks);
95+
try{
96+
// First check the detailed status
97+
conststatusResponse=awaitaxios.get(
98+
`${serverAddress}/api/analysis-status/${id}`
99+
);
100+
const{status:detailedStatus,progress:currentProgress}=statusResponse.data;
99101

100-
// Request progress for each task except the current one (it's tracked through useProgress)
101-
for(constidoftaskIds){
102-
if(id===taskId)continue;// Current task is already tracked
102+
// Update progress in the progress map
103+
setTaskProgressMap((prev)=>({
104+
...prev,
105+
[id]:currentProgress||0,
106+
}));
103107

104-
try{
105-
constresponse=awaitaxios.get(
108+
// If we have a detailed status, use it
109+
if(detailedStatus){
110+
console.log(`Task${id} status update:${detailedStatus}, progress:${currentProgress}%`);
111+
112+
if(detailedStatus==="completed"||detailedStatus==="error"){
113+
// Double check the status with progress endpoint
114+
constprogressResponse=awaitaxios.get(
106115
`${serverAddress}/api/analysis-progress/${id}`
107116
);
108-
const{progress:currentProgress,status:currentStatus}=
109-
response.data;
110-
111-
// Update progress for the task
112-
setTaskProgressMap((prev)=>({
113-
...prev,
114-
[id]:currentProgress,
115-
}));
116-
117-
// If task is completed or an error occurred, update the list of active tasks
118-
if(currentStatus==="completed"||currentStatus==="error"){
117+
118+
if(progressResponse.data.status===detailedStatus){
119119
setActiveTasks((prev)=>{
120120
constnewTasks={ ...prev};
121121
deletenewTasks[id];
122122
returnnewTasks;
123123
});
124124

125-
// If task is completed, update file history
126-
if(currentStatus==="completed"){
127-
fetchFileHistory();
125+
if(detailedStatus==="completed"){
126+
awaitfetchFileHistory();
128127
}
129128
}
130-
}catch(err){
131-
console.error(`Error polling task${id} progress:`,err);
132129
}
133130
}
134-
};
135-
136-
// Start polling all tasks every 3 seconds
137-
constintervalId=setInterval(pollAllActiveTasks,3000);
131+
}catch(err){
132+
console.error(`Error polling task${id} status:`,err);
133+
// If we get an error, we'll keep the task in the active list
134+
// but we'll mark it as having an error in the UI
135+
setTaskProgressMap((prev)=>({
136+
...prev,
137+
[id]:-1,// Use -1 to indicate error
138+
}));
139+
}
140+
};
138141

139-
// Clear interval when unmounting
140-
return()=>clearInterval(intervalId);
141-
},[serverAddress,activeTasks,taskId,fetchFileHistory]);
142+
// Poll all active tasks
143+
constpollAllActiveTasks=async()=>{
144+
consttaskIds=Object.keys(activeTasks);
145+
for(constidoftaskIds){
146+
awaitpollTaskProgress(id);
147+
}
148+
};
142149

143150
// Check current task state during initialization
144151
useEffect(()=>{
@@ -308,26 +315,40 @@ export function useTaskManager(serverAddress) {
308315

309316
// Create list of active tasks for display in history
310317
constactiveTasks_UI=useMemo(()=>{
311-
// Convert to array and sort by creation time (newest first)
312318
returnObject.entries(activeTasks)
313319
.map(([id,taskInfo])=>{
314-
// Get progress for this task from progress map
315320
consttaskProgress=getTaskProgress(id);
321+
consthasError=taskProgress===-1;
316322

317323
return{
318324
id,
319-
status:"processing",
320-
progress:taskProgress,
325+
status:hasError ?"error" :"processing",
326+
progress:hasError ?0 :taskProgress,
321327
vulnerabilityScanner:taskInfo.vulnerabilityScanner||"Unknown",
322-
time:taskInfo.creationTime,// Use saved creation time
328+
time:taskInfo.creationTime,
323329
creationTimestamp:newDate(taskInfo.creationTime).getTime(),
324330
isActive:true,
325-
cveCount:taskInfo.cveCount,// Add CVE count
331+
cveCount:taskInfo.cveCount,
332+
error:hasError ?"Failed to get status" :null
326333
};
327334
})
328-
.sort((a,b)=>b.creationTimestamp-a.creationTimestamp);// Sort by creation time (newest first)
335+
.sort((a,b)=>b.creationTimestamp-a.creationTimestamp);
329336
},[activeTasks,getTaskProgress]);
330337

338+
// Poll all active tasks periodically
339+
useEffect(()=>{
340+
if(!serverAddress||Object.keys(activeTasks).length===0)return;
341+
342+
// Start polling all tasks every 3 seconds
343+
constintervalId=setInterval(pollAllActiveTasks,3000);
344+
345+
// Initial poll
346+
pollAllActiveTasks();
347+
348+
// Clear interval when unmounting
349+
return()=>clearInterval(intervalId);
350+
},[serverAddress,activeTasks]);
351+
331352
return{
332353
taskId,
333354
progress,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp