Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions backend/services/risks_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ def get_risks(user_id):
for scan in scans:
scan_result = scan.scan_result
scan_type = scan.scan_type
failed_checks = scan_result.get("results", {}).get("failed_checks", [])

if "failed_checks" in scan_result:
failed_checks = scan_result["failed_checks"]
elif "results" in scan_result and "failed_checks" in scan_result["results"]:
failed_checks = scan_result["results"]["failed_checks"]
for check in failed_checks:
severity = check.get("severity", "INFO")
severity_counts[severity] = severity_counts.get(severity, 0) + 1
severity = check.get("severity") or "INFO" # Handles None explicitly
# Normalize to uppercase for counting (in case Checkov uses "high" or "High")
severity_upper = severity.upper() if severity else "INFO"
# Only count if it's one of our expected keys (avoids errors if weird values)
if severity_upper in severity_counts:
severity_counts[severity_upper] += 1
else:
severity_counts["INFO"] += 1 # Fallback for unknown severities
detailed_risks.append({
"severity": severity,
"check_id": check.get("check_id"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const GitHubScanner = ({ setResult, isLoading, setIsLoading }) => {
formData.append("input_type", "repo");
formData.append("repo_url", url);

console.log("Sending request to backend with URL:", url);
const response = await fetch("http://localhost:5000/semgrep", {
method: "POST",
headers: {
Expand Down
83 changes: 83 additions & 0 deletions frontend/src/pages/RiskDashboard/RiskDashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -499,4 +499,87 @@
.vulnerability-table__scroll::-webkit-scrollbar,
.vulnerability-table__cards::-webkit-scrollbar {
width: 8px;
}
/* Scan Type Filter Styles */
.scan-type-filter {
background: #fff;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.scan-type-filter__label {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
font-size: 0.875rem;
color: #374151;
margin-bottom: 1rem;
}

.scan-type-filter__icon {
width: 1.25rem;
height: 1.25rem;
color: #6b7280;
}

.scan-type-filter__buttons {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}

.scan-type-filter__button {
padding: 0.5rem 1rem;
border: 2px solid #e5e7eb;
border-radius: 6px;
background: #fff;
color: #6b7280;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}

.scan-type-filter__button:hover {
border-color: #3b82f6;
color: #3b82f6;
background: #eff6ff;
}

.scan-type-filter__button--active {
border-color: #3b82f6;
background: #3b82f6;
color: #fff;
}

.scan-type-filter__count {
font-size: 0.75rem;
opacity: 0.8;
}

/* Update vulnerability table styles */
.vulnerability-table__scan-type {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 4px;
background: #f3f4f6;
color: #374151;
font-size: 0.875rem;
font-weight: 600;
text-transform: uppercase;
}

.vulnerability-card__scan-type {
padding: 0.25rem 0.625rem;
border-radius: 4px;
background: #f3f4f6;
color: #374151;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
84 changes: 77 additions & 7 deletions frontend/src/pages/RiskDashboard/RiskDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,43 @@
import React from 'react';
import { ChartBarIcon, ArrowPathIcon, ExclamationCircleIcon } from '@heroicons/react/24/outline';
import React, { useState } from 'react';
import { ChartBarIcon, ArrowPathIcon, ExclamationCircleIcon, FunnelIcon } from '@heroicons/react/24/outline';
import { Bar } from 'react-chartjs-2';
import { useRisks } from '../../hooks/UseRisk';
import { createChartData, createChartOptions, getSeverityColor } from '../../utils/chartConfig';
import './RiskDashboard.css';

const RiskDashboardPage = () => {
const { risks, details, loading, error, isRefreshing, refreshRisks } = useRisks();
const [selectedScanType, setSelectedScanType] = useState('all');

// Get unique scan types from details
const scanTypes = ['all', ...new Set(details.map(d => d.scan_type).filter(Boolean))];

// Filter details based on selected scan type
const filteredDetails = selectedScanType === 'all'
? details
: details.filter(d => d.scan_type === selectedScanType);

// Aggregate risks by severity for filtered data
const getFilteredRisks = () => {
if (selectedScanType === 'all') return risks;

const severityCounts = { ERROR: 0, WARNING: 0, INFO: 0 };

filteredDetails.forEach(detail => {
const severity = (detail.severity || 'INFO').toUpperCase();
if (severity in severityCounts) {
severityCounts[severity] += 1;
} else {
severityCounts.INFO += 1;
}
});

return [
{ name: "Critical (ERROR)", level: severityCounts.ERROR * 10 },
{ name: "High (WARNING)", level: severityCounts.WARNING * 5 },
{ name: "Low (INFO)", level: severityCounts.INFO * 2 }
];
};

// Loading Spinner Component
const LoadingSpinner = ({ message = 'Loading...' }) => (
Expand Down Expand Up @@ -34,10 +65,39 @@ const RiskDashboardPage = () => {
</div>
);

// Scan Type Filter Component
const ScanTypeFilter = () => (
<div className="scan-type-filter">
<div className="scan-type-filter__label">
<FunnelIcon className="scan-type-filter__icon" />
<span>Filter by Scan Type:</span>
</div>
<div className="scan-type-filter__buttons">
{scanTypes.map(type => (
<button
key={type}
className={`scan-type-filter__button ${
selectedScanType === type ? 'scan-type-filter__button--active' : ''
}`}
onClick={() => setSelectedScanType(type)}
>
{type === 'all' ? 'All Scans' : type.charAt(0).toUpperCase() + type.slice(1)}
{type !== 'all' && (
<span className="scan-type-filter__count">
({details.filter(d => d.scan_type === type).length})
</span>
)}
</button>
))}
</div>
</div>
);

// Risk Chart Section
const RiskChart = ({ risks }) => {
const chartData = createChartData(risks);
const chartOptions = createChartOptions(risks);
const filteredRisks = getFilteredRisks();
const chartData = createChartData(filteredRisks);
const chartOptions = createChartOptions(filteredRisks);

return (
<div className="risk-chart">
Expand All @@ -58,7 +118,7 @@ const RiskDashboardPage = () => {
<div className="vulnerability-table">
<h3 className="vulnerability-table__title">Vulnerability Details</h3>
<div className="vulnerability-table__empty">
<p>No vulnerabilities found</p>
<p>No vulnerabilities found{selectedScanType !== 'all' ? ` for ${selectedScanType}` : ''}</p>
</div>
</div>
);
Expand All @@ -67,7 +127,7 @@ const RiskDashboardPage = () => {
return (
<div className="vulnerability-table">
<h3 className="vulnerability-table__title">
Vulnerability Details ({details.length})
Vulnerabilities Details ({details.length})
</h3>

{/* Desktop Table */}
Expand All @@ -76,6 +136,7 @@ const RiskDashboardPage = () => {
<table className="vulnerability-table__table">
<thead>
<tr>
<th>Scan Type</th>
<th>Severity</th>
<th>Problem</th>
<th>Suggestion</th>
Expand All @@ -84,6 +145,11 @@ const RiskDashboardPage = () => {
<tbody>
{details.map((detail, index) => (
<tr key={index}>
<td>
<span className="vulnerability-table__scan-type">
{detail.scan_type || 'N/A'}
</span>
</td>
<td>
<span
className="vulnerability-table__severity"
Expand Down Expand Up @@ -111,6 +177,9 @@ const RiskDashboardPage = () => {
{details.map((detail, index) => (
<div key={index} className="vulnerability-card">
<div className="vulnerability-card__header">
<span className="vulnerability-card__scan-type">
{detail.scan_type || 'N/A'}
</span>
<span
className="vulnerability-card__severity"
style={{
Expand Down Expand Up @@ -150,8 +219,9 @@ const RiskDashboardPage = () => {

return (
<>
<ScanTypeFilter />
<RiskChart risks={risks} />
<VulnerabilityTable details={details} />
<VulnerabilityTable details={filteredDetails} />
</>
);
};
Expand Down
Loading