Adding reporting, and some fake/dummy routes locally for testing functionality.
All checks were successful
Deploy Frontend / deploy (push) Successful in 22s

This commit is contained in:
Bailey Taylor
2025-10-29 08:20:06 +08:00
parent 271d454bb3
commit 1d882fbfee
7 changed files with 865 additions and 0 deletions

221
package-lock.json generated
View File

@@ -19,8 +19,10 @@
"crypto-js": "^4.2.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"html2canvas": "^1.4.1",
"js-cookie": "^3.0.5",
"jsonstream": "^1.0.3",
"jspdf": "^3.0.3",
"jwt-decode": "^4.0.0",
"mysql2": "^3.14.0",
"next": "15.2.4",
@@ -36,6 +38,7 @@
"@eslint/eslintrc": "^3.3.1",
"@types/crypto-js": "^4.2.2",
"@types/js-cookie": "^3.0.6",
"@types/jspdf": "^1.3.3",
"@types/jwt-decode": "^3.1.0",
"@types/node": "^20",
"@types/react": "^19",
@@ -2474,6 +2477,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/jspdf": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@types/jspdf/-/jspdf-1.3.3.tgz",
"integrity": "sha512-DqwyAKpVuv+7DniCp2Deq1xGvfdnKSNgl9Agun2w6dFvR5UKamiv4VfYUgcypd8S9ojUyARFIlZqBrYrBMQlew==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/jwt-decode": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-3.1.0.tgz",
@@ -2496,6 +2506,12 @@
"undici-types": "~6.19.2"
}
},
"node_modules/@types/pako": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
"license": "MIT"
},
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
@@ -2508,6 +2524,13 @@
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
"license": "MIT"
},
"node_modules/@types/raf": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
"license": "MIT",
"optional": true
},
"node_modules/@types/react": {
"version": "19.0.12",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz",
@@ -2546,6 +2569,13 @@
"@types/react": "*"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.29.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz",
@@ -3543,6 +3573,15 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3688,6 +3727,33 @@
],
"license": "CC-BY-4.0"
},
"node_modules/canvg": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
"license": "MIT",
"optional": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
"core-js": "^3.8.3",
"raf": "^3.4.1",
"regenerator-runtime": "^0.13.7",
"rgbcolor": "^1.0.1",
"stackblur-canvas": "^2.0.0",
"svg-pathdata": "^6.0.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/canvg/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT",
"optional": true
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -3822,6 +3888,18 @@
"node": ">=18"
}
},
"node_modules/core-js": {
"version": "3.46.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz",
"integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@@ -3885,6 +3963,15 @@
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -4091,6 +4178,16 @@
"csstype": "^3.0.2"
}
},
"node_modules/dompurify": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz",
"integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optional": true,
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
@@ -4847,6 +4944,17 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-png": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz",
"integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
"license": "MIT",
"dependencies": {
"@types/pako": "^2.0.3",
"iobuffer": "^5.3.2",
"pako": "^2.1.0"
}
},
"node_modules/fast-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
@@ -4873,6 +4981,12 @@
"reusify": "^1.0.4"
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"license": "MIT"
},
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -5316,6 +5430,19 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"license": "MIT",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -5388,6 +5515,12 @@
"node": ">= 0.4"
}
},
"node_modules/iobuffer": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
"license": "MIT"
},
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -5948,6 +6081,23 @@
"node": "*"
}
},
"node_modules/jspdf": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.3.tgz",
"integrity": "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.9",
"fast-png": "^6.2.0",
"fflate": "^0.8.1"
},
"optionalDependencies": {
"canvg": "^3.0.11",
"core-js": "^3.6.0",
"dompurify": "^3.2.4",
"html2canvas": "^1.0.0-rc.5"
}
},
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -6854,6 +7004,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
"license": "(MIT AND Zlib)"
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -6931,6 +7087,13 @@
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"license": "MIT"
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"license": "MIT",
"optional": true
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -7085,6 +7248,16 @@
],
"license": "MIT"
},
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"license": "MIT",
"optional": true,
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/react": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
@@ -7337,6 +7510,16 @@
"node": ">=0.10.0"
}
},
"node_modules/rgbcolor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
"optional": true,
"engines": {
"node": ">= 0.8.15"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -7707,6 +7890,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/stackblur-canvas": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.1.14"
}
},
"node_modules/stream": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/stream/-/stream-0.0.3.tgz",
@@ -7922,6 +8115,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svg-pathdata": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/tailwindcss": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.0.tgz",
@@ -7937,6 +8140,15 @@
"node": ">=6"
}
},
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -8358,6 +8570,15 @@
"punycode": "^2.1.0"
}
},
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"license": "MIT",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",

View File

@@ -23,8 +23,10 @@
"crypto-js": "^4.2.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"html2canvas": "^1.4.1",
"js-cookie": "^3.0.5",
"jsonstream": "^1.0.3",
"jspdf": "^3.0.3",
"jwt-decode": "^4.0.0",
"mysql2": "^3.14.0",
"next": "15.2.4",
@@ -40,6 +42,7 @@
"@eslint/eslintrc": "^3.3.1",
"@types/crypto-js": "^4.2.2",
"@types/js-cookie": "^3.0.6",
"@types/jspdf": "^1.3.3",
"@types/jwt-decode": "^3.1.0",
"@types/node": "^20",
"@types/react": "^19",

View File

@@ -0,0 +1,483 @@
'use client';
import React, { useEffect, useState } from 'react';
import {
Box,
Typography,
Paper,
Grid,
Card,
CardContent,
Button,
CircularProgress,
Divider,
Chip,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Alert
} from '@mui/material';
import {
PictureAsPdf as PdfIcon,
Assessment as AssessmentIcon,
Security as SecurityIcon,
Devices as DevicesIcon,
Apps as AppsIcon
} from '@mui/icons-material';
import api from '@/lib/axios';
import jsPDF from 'jspdf';
import { format } from 'date-fns';
interface ComplianceSummary {
totalDevices: number;
vulnerableDevices: number;
totalVulnerabilities: number;
criticalVulns: number;
highVulns: number;
mediumVulns: number;
lowVulns: number;
totalSoftware: number;
vulnerableSoftware: number;
lastUpdated: string;
}
interface TopVulnerability {
cveId: string;
title: string;
severity: string;
score: number;
affectedDevices: number;
}
interface TopVulnerableSoftware {
softwareName: string;
totalInstances: number;
vulnerableInstances: number;
totalCves: number;
}
export default function ReportingPage() {
const [summary, setSummary] = useState<ComplianceSummary | null>(null);
const [topVulns, setTopVulns] = useState<TopVulnerability[]>([]);
const [topSoftware, setTopSoftware] = useState<TopVulnerableSoftware[]>([]);
const [loading, setLoading] = useState(true);
const [exporting, setExporting] = useState(false);
const reportRef = React.useRef<HTMLDivElement>(null);
const fetchReportingData = async () => {
try {
setLoading(true);
// Fetch compliance summary data (you'll need to create these endpoints)
const [summaryRes, vulnsRes, softwareRes] = await Promise.all([
api.get('/reporting/compliance-summary'),
api.get('/reporting/top-vulnerabilities'),
api.get('/reporting/vulnerable-software')
]);
setSummary(summaryRes.data);
setTopVulns(vulnsRes.data);
setTopSoftware(softwareRes.data);
} catch (error) {
console.error('Failed to fetch reporting data:', error);
// Create mock data for now since endpoints don't exist yet
createMockData();
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchReportingData();
}, [fetchReportingData]);
const createMockData = () => {
setSummary({
totalDevices: 127,
vulnerableDevices: 89,
totalVulnerabilities: 342,
criticalVulns: 23,
highVulns: 67,
mediumVulns: 156,
lowVulns: 96,
totalSoftware: 1247,
vulnerableSoftware: 89,
lastUpdated: new Date().toISOString()
});
setTopVulns([
{ cveId: 'CVE-2024-12345', title: 'Critical Remote Code Execution', severity: 'CRITICAL', score: 9.8, affectedDevices: 34 },
{ cveId: 'CVE-2024-12346', title: 'Privilege Escalation Vulnerability', severity: 'HIGH', score: 8.4, affectedDevices: 28 },
{ cveId: 'CVE-2024-12347', title: 'Information Disclosure', severity: 'HIGH', score: 7.9, affectedDevices: 22 },
{ cveId: 'CVE-2024-12348', title: 'Cross-Site Scripting', severity: 'MEDIUM', score: 6.1, affectedDevices: 19 },
{ cveId: 'CVE-2024-12349', title: 'Denial of Service', severity: 'MEDIUM', score: 5.8, affectedDevices: 15 }
]);
setTopSoftware([
{ softwareName: 'Apache HTTP Server', totalInstances: 45, vulnerableInstances: 23, totalCves: 12 },
{ softwareName: 'OpenSSL', totalInstances: 89, vulnerableInstances: 34, totalCves: 8 },
{ softwareName: 'Microsoft Office', totalInstances: 127, vulnerableInstances: 67, totalCves: 15 },
{ softwareName: 'Adobe Reader', totalInstances: 98, vulnerableInstances: 45, totalCves: 9 },
{ softwareName: 'Java Runtime Environment', totalInstances: 76, vulnerableInstances: 28, totalCves: 6 }
]);
};
const exportToPDF = async () => {
if (!reportRef.current || !summary) return;
try {
setExporting(true);
// Create a new jsPDF instance
const pdf = new jsPDF('p', 'mm', 'a4');
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
// Add header
pdf.setFontSize(20);
pdf.setFont('helvetica', 'bold');
pdf.text('Compliance Security Report', pageWidth / 2, 20, { align: 'center' });
pdf.setFontSize(12);
pdf.setFont('helvetica', 'normal');
pdf.text(`Generated: ${format(new Date(), 'PPP')}`, pageWidth / 2, 30, { align: 'center' });
let yPosition = 45;
// Executive Summary
pdf.setFontSize(16);
pdf.setFont('helvetica', 'bold');
pdf.text('Executive Summary', 20, yPosition);
yPosition += 10;
pdf.setFontSize(10);
pdf.setFont('helvetica', 'normal');
const summaryText = [
`Total Devices: ${summary.totalDevices}`,
`Vulnerable Devices: ${summary.vulnerableDevices} (${((summary.vulnerableDevices / summary.totalDevices) * 100).toFixed(1)}%)`,
`Total Vulnerabilities: ${summary.totalVulnerabilities}`,
`Critical: ${summary.criticalVulns} | High: ${summary.highVulns} | Medium: ${summary.mediumVulns} | Low: ${summary.lowVulns}`,
`Software Applications: ${summary.totalSoftware}`,
`Vulnerable Software: ${summary.vulnerableSoftware}`
];
summaryText.forEach(text => {
pdf.text(text, 20, yPosition);
yPosition += 6;
});
yPosition += 10;
// Top Vulnerabilities
pdf.setFontSize(16);
pdf.setFont('helvetica', 'bold');
pdf.text('Top Critical Vulnerabilities', 20, yPosition);
yPosition += 10;
pdf.setFontSize(9);
pdf.setFont('helvetica', 'normal');
topVulns.slice(0, 10).forEach((vuln, index) => {
if (yPosition > pageHeight - 30) {
pdf.addPage();
yPosition = 20;
}
pdf.text(`${index + 1}. ${vuln.cveId} - ${vuln.title}`, 20, yPosition);
yPosition += 5;
pdf.text(` Severity: ${vuln.severity} | Score: ${vuln.score} | Affected: ${vuln.affectedDevices} devices`, 20, yPosition);
yPosition += 8;
});
// Add page break if needed
if (yPosition > pageHeight - 80) {
pdf.addPage();
yPosition = 20;
} else {
yPosition += 10;
}
// Top Vulnerable Software
pdf.setFontSize(16);
pdf.setFont('helvetica', 'bold');
pdf.text('Most Vulnerable Software', 20, yPosition);
yPosition += 10;
pdf.setFontSize(9);
pdf.setFont('helvetica', 'normal');
topSoftware.slice(0, 10).forEach((software, index) => {
if (yPosition > pageHeight - 30) {
pdf.addPage();
yPosition = 20;
}
pdf.text(`${index + 1}. ${software.softwareName}`, 20, yPosition);
yPosition += 5;
pdf.text(` ${software.vulnerableInstances}/${software.totalInstances} instances vulnerable | ${software.totalCves} CVEs`, 20, yPosition);
yPosition += 8;
});
// Footer
const totalPages = pdf.internal.pages.length - 1;
for (let i = 1; i <= totalPages; i++) {
pdf.setPage(i);
pdf.setFontSize(8);
pdf.text(`Page ${i} of ${totalPages}`, pageWidth - 30, pageHeight - 10);
pdf.text('Confidential - Internal Use Only', 20, pageHeight - 10);
}
// Save the PDF
const filename = `compliance-report-${format(new Date(), 'yyyy-MM-dd')}.pdf`;
pdf.save(filename);
} catch (error) {
console.error('Failed to export PDF:', error);
} finally {
setExporting(false);
}
};
const getSeverityColor = (severity: string) => {
switch (severity?.toLowerCase()) {
case 'critical': return 'error';
case 'high': return 'warning';
case 'medium': return 'info';
case 'low': return 'success';
default: return 'default';
}
};
if (loading) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="60vh">
<CircularProgress />
</Box>
);
}
if (!summary) {
return (
<Box p={4}>
<Alert severity="error">Failed to load reporting data. Please try again later.</Alert>
</Box>
);
}
return (
<Box p={4} ref={reportRef}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={4}>
<Box>
<Typography variant="h4" gutterBottom>
<AssessmentIcon sx={{ mr: 2, verticalAlign: 'middle' }} />
Compliance Reporting
</Typography>
<Typography variant="subtitle1" color="text.secondary">
Comprehensive security compliance overview and vulnerability reporting
</Typography>
</Box>
<Button
variant="contained"
startIcon={exporting ? <CircularProgress size={20} /> : <PdfIcon />}
onClick={exportToPDF}
disabled={exporting}
size="large"
>
{exporting ? 'Generating...' : 'Export PDF'}
</Button>
</Box>
{/* Executive Summary Cards */}
<Grid container spacing={3} sx={{ mb: 4 }}>
<Grid size={{ xs: 12, md: 3 }}>
<Card>
<CardContent>
<Box display="flex" alignItems="center" mb={2}>
<DevicesIcon color="primary" sx={{ mr: 1 }} />
<Typography variant="h6">Devices</Typography>
</Box>
<Typography variant="h3" color="primary">
{summary.totalDevices}
</Typography>
<Typography variant="body2" color="text.secondary">
{summary.vulnerableDevices} vulnerable ({((summary.vulnerableDevices / summary.totalDevices) * 100).toFixed(1)}%)
</Typography>
</CardContent>
</Card>
</Grid>
<Grid size={{ xs: 12, md: 3 }}>
<Card>
<CardContent>
<Box display="flex" alignItems="center" mb={2}>
<SecurityIcon color="error" sx={{ mr: 1 }} />
<Typography variant="h6">Vulnerabilities</Typography>
</Box>
<Typography variant="h3" color="error">
{summary.totalVulnerabilities}
</Typography>
<Box display="flex" gap={1} mt={1} flexWrap="wrap">
<Chip label={`Critical: ${summary.criticalVulns}`} color="error" size="small" />
<Chip label={`High: ${summary.highVulns}`} color="warning" size="small" />
</Box>
</CardContent>
</Card>
</Grid>
<Grid size={{ xs: 12, md: 3 }}>
<Card>
<CardContent>
<Box display="flex" alignItems="center" mb={2}>
<AppsIcon color="info" sx={{ mr: 1 }} />
<Typography variant="h6">Software</Typography>
</Box>
<Typography variant="h3" color="info">
{summary.totalSoftware}
</Typography>
<Typography variant="body2" color="text.secondary">
{summary.vulnerableSoftware} with vulnerabilities
</Typography>
</CardContent>
</Card>
</Grid>
<Grid size={{ xs: 12, md: 3 }}>
<Card>
<CardContent>
<Typography variant="h6" mb={2}>Risk Level</Typography>
<Typography
variant="h3"
color={summary.criticalVulns > 0 ? "error" : summary.highVulns > 10 ? "warning" : "success"}
>
{summary.criticalVulns > 0 ? "HIGH" : summary.highVulns > 10 ? "MEDIUM" : "LOW"}
</Typography>
<Typography variant="body2" color="text.secondary">
Last updated: {format(new Date(summary.lastUpdated), 'PPp')}
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
{/* Detailed Reports */}
<Grid container spacing={3}>
<Grid size={{ xs: 12, lg: 6 }}>
<Paper sx={{ p: 3, height: 'fit-content' }}>
<Typography variant="h6" mb={3}>
<SecurityIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
Top Critical Vulnerabilities
</Typography>
<TableContainer>
<Table size="small">
<TableHead>
<TableRow>
<TableCell><strong>CVE ID</strong></TableCell>
<TableCell><strong>Severity</strong></TableCell>
<TableCell align="right"><strong>Score</strong></TableCell>
<TableCell align="right"><strong>Devices</strong></TableCell>
</TableRow>
</TableHead>
<TableBody>
{topVulns.slice(0, 8).map((vuln) => (
<TableRow key={vuln.cveId} hover>
<TableCell>
<Typography variant="body2" fontFamily="monospace">
{vuln.cveId}
</Typography>
<Typography variant="caption" color="text.secondary">
{vuln.title.length > 40 ? `${vuln.title.substring(0, 40)}...` : vuln.title}
</Typography>
</TableCell>
<TableCell>
<Chip
label={vuln.severity}
color={getSeverityColor(vuln.severity) as "error" | "warning" | "info" | "success" | "default"}
size="small"
/>
</TableCell>
<TableCell align="right">
<Typography fontWeight="bold">
{vuln.score}
</Typography>
</TableCell>
<TableCell align="right">
{vuln.affectedDevices}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Paper>
</Grid>
<Grid size={{ xs: 12, lg: 6 }}>
<Paper sx={{ p: 3, height: 'fit-content' }}>
<Typography variant="h6" mb={3}>
<AppsIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
Most Vulnerable Software
</Typography>
<TableContainer>
<Table size="small">
<TableHead>
<TableRow>
<TableCell><strong>Software</strong></TableCell>
<TableCell align="center"><strong>Vulnerable</strong></TableCell>
<TableCell align="center"><strong>CVEs</strong></TableCell>
<TableCell align="center"><strong>Risk</strong></TableCell>
</TableRow>
</TableHead>
<TableBody>
{topSoftware.slice(0, 8).map((software) => {
const riskLevel = software.vulnerableInstances / software.totalInstances;
return (
<TableRow key={software.softwareName} hover>
<TableCell>
<Typography variant="body2">
{software.softwareName}
</Typography>
</TableCell>
<TableCell align="center">
<Typography variant="body2">
{software.vulnerableInstances}/{software.totalInstances}
</Typography>
<Typography variant="caption" color="text.secondary">
({((riskLevel) * 100).toFixed(0)}%)
</Typography>
</TableCell>
<TableCell align="center">
<Typography fontWeight="bold" color="error">
{software.totalCves}
</Typography>
</TableCell>
<TableCell align="center">
<Chip
label={riskLevel > 0.5 ? "HIGH" : riskLevel > 0.2 ? "MEDIUM" : "LOW"}
color={riskLevel > 0.5 ? "error" : riskLevel > 0.2 ? "warning" : "success"}
size="small"
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</Grid>
</Grid>
<Box mt={4}>
<Divider />
<Typography variant="caption" color="text.secondary" sx={{ mt: 2, display: 'block' }}>
This report contains confidential security information. Distribution should be limited to authorized personnel only.
Report generated on {format(new Date(), 'PPP')} from data last updated {format(new Date(summary.lastUpdated), 'PPp')}.
</Typography>
</Box>
</Box>
);
}

View File

@@ -0,0 +1,20 @@
import { NextResponse } from 'next/server';
export async function GET() {
// Mock compliance summary data
// In production, this would query your database for actual metrics
const summary = {
totalDevices: 127,
vulnerableDevices: 89,
totalVulnerabilities: 342,
criticalVulns: 23,
highVulns: 67,
mediumVulns: 156,
lowVulns: 96,
totalSoftware: 1247,
vulnerableSoftware: 89,
lastUpdated: new Date().toISOString()
};
return NextResponse.json(summary);
}

View File

@@ -0,0 +1,66 @@
import { NextResponse } from 'next/server';
export async function GET() {
// Mock top vulnerabilities data
// In production, this would query your vulnerability database
const topVulns = [
{
cveId: 'CVE-2024-12345',
title: 'Critical Remote Code Execution in Apache HTTP Server',
severity: 'CRITICAL',
score: 9.8,
affectedDevices: 34
},
{
cveId: 'CVE-2024-12346',
title: 'Privilege Escalation Vulnerability in OpenSSL',
severity: 'HIGH',
score: 8.4,
affectedDevices: 28
},
{
cveId: 'CVE-2024-12347',
title: 'Information Disclosure in Microsoft Office',
severity: 'HIGH',
score: 7.9,
affectedDevices: 22
},
{
cveId: 'CVE-2024-12348',
title: 'Cross-Site Scripting in Adobe Reader',
severity: 'MEDIUM',
score: 6.1,
affectedDevices: 19
},
{
cveId: 'CVE-2024-12349',
title: 'Denial of Service in Java Runtime Environment',
severity: 'MEDIUM',
score: 5.8,
affectedDevices: 15
},
{
cveId: 'CVE-2024-12350',
title: 'Buffer Overflow in Windows Kernel',
severity: 'HIGH',
score: 7.5,
affectedDevices: 12
},
{
cveId: 'CVE-2024-12351',
title: 'SQL Injection in MySQL Client',
severity: 'MEDIUM',
score: 6.8,
affectedDevices: 10
},
{
cveId: 'CVE-2024-12352',
title: 'Use After Free in Chrome Browser',
severity: 'HIGH',
score: 8.1,
affectedDevices: 8
}
];
return NextResponse.json(topVulns);
}

View File

@@ -0,0 +1,70 @@
import { NextResponse } from 'next/server';
export async function GET() {
// Mock vulnerable software data
// In production, this would query your software inventory database
const vulnerableSoftware = [
{
softwareName: 'Apache HTTP Server',
totalInstances: 45,
vulnerableInstances: 23,
totalCves: 12
},
{
softwareName: 'OpenSSL',
totalInstances: 89,
vulnerableInstances: 34,
totalCves: 8
},
{
softwareName: 'Microsoft Office',
totalInstances: 127,
vulnerableInstances: 67,
totalCves: 15
},
{
softwareName: 'Adobe Reader',
totalInstances: 98,
vulnerableInstances: 45,
totalCves: 9
},
{
softwareName: 'Java Runtime Environment',
totalInstances: 76,
vulnerableInstances: 28,
totalCves: 6
},
{
softwareName: 'Google Chrome',
totalInstances: 112,
vulnerableInstances: 23,
totalCves: 4
},
{
softwareName: 'Mozilla Firefox',
totalInstances: 67,
vulnerableInstances: 18,
totalCves: 3
},
{
softwareName: 'MySQL Server',
totalInstances: 34,
vulnerableInstances: 12,
totalCves: 5
},
{
softwareName: 'Python',
totalInstances: 89,
vulnerableInstances: 15,
totalCves: 2
},
{
softwareName: 'Node.js',
totalInstances: 56,
vulnerableInstances: 9,
totalCves: 3
}
];
return NextResponse.json(vulnerableSoftware);
}

View File

@@ -25,6 +25,7 @@ import DashboardIcon from '@mui/icons-material/Dashboard';
import SecurityIcon from '@mui/icons-material/Security';
import DevicesIcon from '@mui/icons-material/Devices';
import AppsIcon from '@mui/icons-material/Apps';
import AssessmentIcon from '@mui/icons-material/Assessment';
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import LightModeIcon from '@mui/icons-material/LightMode';
@@ -123,6 +124,7 @@ export default function SidebarLayout({ children }: { children: React.ReactNode
{ label: 'Vulnerabilities', path: '/vulnerabilities', icon: <SecurityIcon /> },
{ label: 'Devices', path: '/devices', icon: <DevicesIcon /> },
{ label: 'Software', path: '/software', icon: <AppsIcon /> },
{ label: 'Reporting', path: '/reporting', icon: <AssessmentIcon /> },
];
const handleLogout = async () => {