Backend: adds ivanti_counts_cache table, fetches Closed count (page 0, size 1) from Ivanti after each Open sync, and exposes GET /counts endpoint. Frontend: replaces the Metrics placeholder with an SVG donut chart showing Open vs Closed proportions with counts and percentages. Counts are fetched on mount and refreshed after manual sync. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CVE Dashboard
A self-hosted vulnerability management dashboard for tracking CVE remediation status, maintaining vendor documentation, and managing risk acceptance workflows.
Table of Contents
- Overview
- Tech Stack
- Prerequisites
- Installation
- Configuration
- Running the Application
- Features
- API Reference
- Architecture
- Database Schema
- Security Model
- Migrations
Overview
The CVE Dashboard answers a common problem in vulnerability management: before requesting false positive designations, you need to know whether a CVE has already been addressed, and whether the supporting vendor documentation exists. This application provides:
- A searchable, filterable CVE list with per-vendor tracking
- Document storage attached to each CVE/vendor pair (advisories, emails, screenshots, patches)
- NVD API integration to auto-populate CVE metadata
- Archer risk acceptance ticket tracking (EXC numbers)
- Weekly vulnerability report upload and processing
- A knowledge base for internal documentation and policies
- Role-based access control with a full audit trail
Tech Stack
| Layer | Technology |
|---|---|
| Backend | Node.js, Express 5 |
| Database | SQLite3 |
| File uploads | Multer 2 |
| Auth | bcryptjs, cookie-based sessions |
| Frontend | React 19, lucide-react, react-markdown |
| Report processing | Python 3 (pandas, openpyxl) |
Prerequisites
- Node.js 18 or later
- npm
- Python 3 with pip (required only for weekly report processing)
Installation
1. Clone the repository
git clone <repo-url>
cd cve-dashboard
2. Install backend dependencies
cd backend
npm install
The root package.json lists the backend dependencies. Install them from the backend/ directory where server.js lives.
3. Install frontend dependencies
cd frontend
npm install
4. Install Python dependencies (for weekly report upload feature)
cd backend/scripts
pip install -r requirements.txt
Required packages: pandas>=2.0.0, openpyxl>=3.0.0
5. Initialize the database
Run this once from the backend/ directory to create the SQLite database, all tables, indexes, the uploads directory, and a default admin user:
cd backend
node setup.js
This creates backend/cve_database.db and a default admin account:
- Username:
admin - Password:
admin123
Change the admin password immediately after first login.
6. Run database migrations
After the initial setup, apply the feature migrations in order:
cd backend
node migrations/add_weekly_reports_table.js
node migrations/add_knowledge_base_table.js
node migrations/add_archer_tickets_table.js
Configuration
The application is configured via .env files. These files are gitignored and must be created manually per environment.
Backend: backend/.env
PORT=3001
API_HOST=localhost
CORS_ORIGINS=http://YOUR_IP:3000
SESSION_SECRET=change-this-to-a-random-secret
NODE_ENV=development
# Optional: NVD API key for higher rate limits
# Register at https://nvd.nist.gov/developers/request-an-api-key
NVD_API_KEY=your-key-here
Frontend: frontend/.env
REACT_APP_API_BASE=http://YOUR_IP:3001/api
REACT_APP_API_HOST=http://YOUR_IP:3001
Replace YOUR_IP with the machine's IP address or localhost for local development.
Important: React caches environment variables at build/start time. After changing frontend/.env, you must fully restart the frontend process. A page refresh alone is not sufficient.
Running the Application
Using the helper scripts (recommended)
From the project root:
./start-servers.sh # Starts backend and frontend in the background
./stop-servers.sh # Stops all servers
The start script saves PIDs to backend.pid and frontend.pid. Logs are written to backend/backend.log and frontend/frontend.log.
Running manually
# Terminal 1 - backend
cd backend
node server.js
# Terminal 2 - frontend
cd frontend
npm start
Default ports
- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
Features
Authentication and User Roles
All routes require authentication. Three roles are supported:
| Role | Permissions |
|---|---|
viewer |
Read-only access to CVEs, documents, weekly reports, knowledge base, Archer tickets |
editor |
All viewer permissions plus: create/update CVEs, upload documents, upload weekly reports, manage knowledge base articles, manage Archer tickets |
admin |
All editor permissions plus: delete documents, delete weekly reports, manage users, view audit logs |
Sessions expire after 24 hours. Session tokens are stored in httpOnly cookies.
CVE Management
- Add CVEs with full metadata: CVE ID, vendor, severity (Critical/High/Medium/Low), description, published date, and status (Open/In Progress/Addressed/Resolved)
- The same CVE ID can be tracked across multiple vendors independently
- Filter the CVE list by search term, vendor, severity, and status
- Edit any field on an existing CVE entry; file paths are updated automatically when CVE ID or vendor changes
- Delete a single vendor entry or all vendor entries for a CVE ID
- Paginated list view to prevent performance issues with large datasets
- Quick Check: look up a CVE ID and see all vendors tracking it with their current status
NVD Integration
- Auto-fill CVE description, severity, and published date from the NIST NVD API 2.0 when adding a new CVE
- Bulk NVD Sync: fetch updated metadata for all CVEs in the database in one operation (editor/admin)
- CVSS severity mapping cascades: v3.1 preferred, then v3.0, then v2.0
- NVD API key support via
NVD_API_KEYenvironment variable for higher rate limits
Document Management
Documents are attached to a CVE/vendor pair and stored on disk under backend/uploads/<CVE-ID>/<vendor>/.
Supported document types: advisory, email, screenshot, patch, other
Allowed file extensions: PDF, images (PNG, JPG, GIF, BMP, TIFF), Office documents (DOC, DOCX, XLS, XLSX, PPT, PPTX), text files (TXT, MD, CSV, LOG), email files (MSG, EML), and others (RTF, HTML, XML, JSON, YAML, ODF variants).
File size limit: 10 MB per upload.
Weekly Reports
Editors and admins can upload weekly vulnerability reports as .xlsx files. The report is processed by a Python script (backend/scripts/split_cve_report.py) that:
- Reads the
Vulnerabilitiessheet - Splits rows where multiple CVE IDs are comma-separated in the
CVE IDcolumn into individual rows - Saves the processed file alongside the original
Both the original and processed files can be downloaded from the weekly reports list. Only the most recently uploaded report is marked as current. Admins can delete old report records and their associated files.
Archer Risk Acceptance Tickets
Track Archer exception tickets (EXC numbers) linked to specific CVE/vendor pairs.
- EXC number format:
EXC-NNNNN - Statuses:
Draft,Open,Under Review,Accepted - Optional Archer URL field for deep-linking to the Archer record
- Filter tickets by CVE ID, vendor, or status
- EXC numbers are unique across the system
Knowledge Base
A document library for internal reference material such as policies, runbooks, and vendor advisories.
- Upload documents with a title, optional description, and category
- View documents inline in the browser (PDFs render in an iframe; markdown files are rendered as HTML)
- Download any document
- Filter and browse by category
- Editors and admins can upload and delete; all authenticated users can view
Allowed file types: PDF, Markdown, TXT, Office documents, HTML, JSON, YAML, and images.
User Management (Admin)
Admins can create, update, and delete user accounts from the UI. Supported operations:
- Create users with a role assignment
- Change username, email, password, role, or active status
- Deactivating a user immediately invalidates all their active sessions
- Admins cannot demote themselves or deactivate their own account
Audit Log (Admin)
Every state-changing action is recorded with the user identity, IP address, action type, target entity, and a before/after details payload. Admins can view the audit log with filtering by user, action type, entity type, and date range. Results are paginated.
API Reference
All endpoints are prefixed with /api. All endpoints except /api/auth/login and /api/auth/logout require a valid session cookie.
Auth
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/auth/login |
Public | Log in, receive session cookie |
| POST | /api/auth/logout |
Public | Invalidate session |
| GET | /api/auth/me |
Session | Get current user info |
| POST | /api/auth/cleanup-sessions |
Session | Delete expired sessions |
CVEs
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/cves |
viewer+ | List CVEs with optional filters: search, vendor, severity, status |
| POST | /api/cves |
editor+ | Create a new CVE entry |
| PUT | /api/cves/:id |
editor+ | Update a CVE entry by row ID |
| PATCH | /api/cves/:cveId/status |
editor+ | Update status for all vendor rows matching a CVE ID |
| DELETE | /api/cves/:id |
editor+ | Delete a single CVE vendor entry |
| DELETE | /api/cves/by-cve-id/:cveId |
editor+ | Delete all vendor entries for a CVE ID |
| GET | /api/cves/check/:cveId |
viewer+ | Quick check: does this CVE exist and what is its status? |
| GET | /api/cves/distinct-ids |
viewer+ | List all distinct CVE IDs (used by NVD sync) |
| GET | /api/cves/:cveId/vendors |
viewer+ | List all vendor entries for a specific CVE ID |
Documents
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/cves/:cveId/documents |
viewer+ | List documents for a CVE, optionally filtered by ?vendor= |
| POST | /api/cves/:cveId/documents |
editor+ | Upload a document for a CVE/vendor pair |
| DELETE | /api/documents/:id |
admin | Delete a document and its file from disk |
NVD
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/nvd/lookup/:cveId |
viewer+ | Look up a single CVE in the NVD API |
| POST | /api/cves/nvd-sync |
editor+ | Bulk update CVE metadata from NVD |
Weekly Reports
| Method | Path | Role | Description |
|---|---|---|---|
| POST | /api/weekly-reports/upload |
editor+ | Upload and process a .xlsx vulnerability report |
| GET | /api/weekly-reports |
viewer+ | List all uploaded reports |
| GET | /api/weekly-reports/:id/download/:type |
viewer+ | Download original or processed file |
| DELETE | /api/weekly-reports/:id |
admin | Delete a report record and its files |
Knowledge Base
| Method | Path | Role | Description |
|---|---|---|---|
| POST | /api/knowledge-base/upload |
editor+ | Upload a new knowledge base document |
| GET | /api/knowledge-base |
viewer+ | List all articles |
| GET | /api/knowledge-base/:id |
viewer+ | Get article metadata |
| GET | /api/knowledge-base/:id/content |
viewer+ | Get file content for inline display |
| GET | /api/knowledge-base/:id/download |
viewer+ | Download the file |
| DELETE | /api/knowledge-base/:id |
editor+ | Delete article and file |
Archer Tickets
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/archer-tickets |
viewer+ | List tickets, optional filters: cve_id, vendor, status |
| POST | /api/archer-tickets |
editor+ | Create a new Archer ticket |
| PUT | /api/archer-tickets/:id |
editor+ | Update an Archer ticket |
| DELETE | /api/archer-tickets/:id |
editor+ | Delete an Archer ticket |
Users (Admin only)
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/users |
admin | List all users |
| GET | /api/users/:id |
admin | Get a single user |
| POST | /api/users |
admin | Create a user |
| PATCH | /api/users/:id |
admin | Update a user |
| DELETE | /api/users/:id |
admin | Delete a user |
Audit Logs (Admin only)
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/audit-logs |
admin | Paginated audit log with filters |
| GET | /api/audit-logs/actions |
admin | List distinct action types |
Utility
| Method | Path | Role | Description |
|---|---|---|---|
| GET | /api/vendors |
viewer+ | List all distinct vendor names |
| GET | /api/stats |
viewer+ | Dashboard statistics (total CVEs, critical count, addressed count, document count) |
Architecture
cve-dashboard/
├── start-servers.sh # Start backend + frontend in background
├── stop-servers.sh # Stop all servers
│
├── backend/
│ ├── server.js # Express app, CVE/document endpoints, middleware
│ ├── setup.js # One-time DB initialization and default admin creation
│ ├── cve_database.db # SQLite database (gitignored)
│ ├── uploads/ # File storage (gitignored)
│ │ ├── <CVE-ID>/
│ │ │ └── <vendor>/ # CVE documents stored here
│ │ ├── weekly_reports/ # Uploaded vulnerability reports
│ │ ├── knowledge_base/ # Knowledge base documents
│ │ └── temp/ # Temporary upload staging directory
│ ├── routes/
│ │ ├── auth.js # Login, logout, session check
│ │ ├── users.js # User CRUD (admin)
│ │ ├── auditLog.js # Audit log viewer (admin)
│ │ ├── nvdLookup.js # NVD API proxy
│ │ ├── weeklyReports.js # Weekly report upload and management
│ │ ├── knowledgeBase.js # Knowledge base document management
│ │ └── archerTickets.js # Archer EXC ticket CRUD
│ ├── middleware/
│ │ └── auth.js # requireAuth and requireRole middleware
│ ├── helpers/
│ │ ├── auditLog.js # logAudit helper
│ │ └── excelProcessor.js # Calls Python script for report processing
│ ├── migrations/
│ │ ├── add_weekly_reports_table.js
│ │ ├── add_knowledge_base_table.js
│ │ └── add_archer_tickets_table.js
│ └── scripts/
│ ├── split_cve_report.py # Python: splits multi-CVE rows in Excel reports
│ └── requirements.txt # pandas, openpyxl
│
└── frontend/
└── src/
├── App.js # Main application, CVE list, filters, modals
├── App.css # Global styles
├── contexts/
│ └── AuthContext.js # Auth state provider
└── components/
├── LoginForm.js # Login page
├── UserMenu.js # User dropdown in header
├── UserManagement.js # Admin user management panel
├── AuditLog.js # Admin audit log viewer
├── NvdSyncModal.js # Bulk NVD sync dialog
├── WeeklyReportModal.js # Weekly report upload dialog
├── KnowledgeBaseModal.js # Knowledge base upload/list
└── KnowledgeBaseViewer.js # Inline document viewer
Database Schema
Core tables
cves - One row per CVE/vendor pair. UNIQUE(cve_id, vendor).
documents - Files attached to a CVE/vendor pair. Foreign key to cves(cve_id).
required_documents - Vendor-specific document requirements (advisory, screenshot, etc.).
users - Accounts with roles: admin, editor, viewer.
sessions - Active sessions. Expire after 24 hours.
audit_logs - Append-only log of all state-changing actions.
Feature tables (added by migrations)
weekly_reports - Metadata for uploaded vulnerability reports. Tracks original and processed file paths, row counts, uploader, and a is_current flag.
knowledge_base - Document library entries with title, slug, category, description, and file metadata.
archer_tickets - Archer EXC exception tickets linked to CVE/vendor pairs. UNIQUE(exc_number).
View
cve_document_status - Aggregates document counts per CVE/vendor and derives a compliance_status (Complete when an advisory is present, otherwise Missing Required Docs).
Security Model
File upload security
- Extension allowlist enforced by Multer; executables (
.exe,.js,.sh,.py,.bat, etc.) are blocked - MIME type prefix validation in addition to extension checking
- 10 MB per-file size limit
- Filenames are sanitized: path separators,
..sequences, null bytes, and non-alphanumeric characters are removed
Path traversal prevention
sanitizePathSegment()strips/,\,.., and null bytes from any value used inpath.join()isPathWithinUploads()verifies resolved paths stay within the uploads root before any file operation
Input validation
- CVE ID must match
/^CVE-\d{4}-\d{4,}$/ - Severity must be one of:
Critical,High,Medium,Low - Status must be one of:
Open,Addressed,In Progress,Resolved - Archer EXC numbers must match
/^EXC-\d+$/ - All database operations use prepared statements
Error handling
- 500 responses never leak internal error messages to the client
- Full errors are logged server-side only
- Descriptive 400/409 responses are safe because they contain only validation messages written by the application
Security headers
Applied to all responses:
X-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: camera=(), microphone=(), geolocation=()
Session cookies
httpOnly: true, sameSite: lax, secure: true in production.
Migrations
Migrations are standalone Node.js scripts that alter the database directly. Run them in the order listed. They use CREATE TABLE IF NOT EXISTS, so they are safe to run again if needed.
cd backend
node migrations/add_weekly_reports_table.js
node migrations/add_knowledge_base_table.js
node migrations/add_archer_tickets_table.js
For an existing deployment upgrading from an earlier schema, also check the legacy migration scripts in backend/:
migrate_multivendor.js- Adds multi-vendor support to an older single-vendor schemamigrate-audit-log.js- Adds the audit_logs table to pre-auth deploymentsmigrate-to-1.1.js- General 1.0 to 1.1 schema update