docs: comprehensive README rewrite for current feature set

Rewrites the README from scratch to reflect the full current state of the
application. Major additions over the previous version:

- Ivanti/RiskSense integration: env vars, sync behaviour, findings cache
- Reporting page: all 4 donut charts, findings table columns, column
  management, per-column filtering (including empty-cell filter),
  inline hostname/DNS overrides, inline notes, CSV/XLSX export
- FP workflow tracking: finding vs ticket count distinction, closed-finding
  sweep for Approved FPs
- import_notes_from_csv.py script documentation with usage/args
- Full API reference updated with all Ivanti findings endpoints
- Architecture diagram updated with new route and component files
- Database schema updated with all Ivanti tables and new columns
- Migrations section updated with two new Ivanti migration scripts
- Configuration section updated with all IVANTI_* env vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 09:05:16 -06:00
parent ae04bc981e
commit b58bd0650a

478
README.md
View File

@@ -1,6 +1,6 @@
# CVE Dashboard
A self-hosted vulnerability management dashboard for tracking CVE remediation status, maintaining vendor documentation, and managing risk acceptance workflows.
A self-hosted vulnerability management dashboard for tracking CVE remediation status, managing vendor documentation, monitoring Ivanti host findings, and overseeing False Positive (FP) workflows.
---
@@ -13,6 +13,15 @@ A self-hosted vulnerability management dashboard for tracking CVE remediation st
- [Configuration](#configuration)
- [Running the Application](#running-the-application)
- [Features](#features)
- [Authentication and User Roles](#authentication-and-user-roles)
- [Home Dashboard — CVE Management](#home-dashboard--cve-management)
- [Reporting — Host Findings](#reporting--host-findings)
- [Knowledge Base](#knowledge-base)
- [Archer Risk Acceptance Tickets](#archer-risk-acceptance-tickets)
- [Weekly Reports](#weekly-reports)
- [User Management](#user-management-admin)
- [Audit Log](#audit-log-admin)
- [Scripts](#scripts)
- [API Reference](#api-reference)
- [Architecture](#architecture)
- [Database Schema](#database-schema)
@@ -23,12 +32,15 @@ A self-hosted vulnerability management dashboard for tracking CVE remediation st
## 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:
The CVE Dashboard answers a common problem in vulnerability management: tracking which CVEs have been addressed, whether supporting vendor documentation exists, and where each finding is in the remediation or exception workflow.
- A searchable, filterable CVE list with per-vendor tracking
- Document storage attached to each CVE/vendor pair (advisories, emails, screenshots, patches)
The application provides:
- A searchable, filterable CVE list with per-vendor tracking and document storage
- NVD API integration to auto-populate CVE metadata
- Archer risk acceptance ticket tracking (EXC numbers)
- **Ivanti/RiskSense integration** to sync open and closed host findings with live FP workflow tracking
- **Reporting page** with donut charts, advanced per-column filtering, inline editing, and CSV/XLSX export
- Archer risk acceptance ticket tracking (EXC numbers) linked to CVE/vendor pairs
- Weekly vulnerability report upload and processing
- A knowledge base for internal documentation and policies
- Role-based access control with a full audit trail
@@ -43,8 +55,9 @@ The CVE Dashboard answers a common problem in vulnerability management: before r
| Database | SQLite3 |
| File uploads | Multer 2 |
| Auth | bcryptjs, cookie-based sessions |
| Frontend | React 19, lucide-react, react-markdown |
| Report processing | Python 3 (pandas, openpyxl) |
| Frontend | React 19, lucide-react, xlsx |
| Report processing | Python 3 (stdlib only — no extra packages required for notes import) |
| Weekly report processing | Python 3, pandas, openpyxl |
---
@@ -52,7 +65,7 @@ The CVE Dashboard answers a common problem in vulnerability management: before r
- Node.js 18 or later
- npm
- Python 3 with pip (required only for weekly report processing)
- Python 3 (required for weekly report processing and bulk notes import)
---
@@ -72,8 +85,6 @@ 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
```bash
@@ -81,7 +92,7 @@ cd frontend
npm install
```
### 4. Install Python dependencies (for weekly report upload feature)
### 4. Install Python dependencies (for weekly report processing)
```bash
cd backend/scripts
@@ -90,9 +101,11 @@ pip install -r requirements.txt
Required packages: `pandas>=2.0.0`, `openpyxl>=3.0.0`
> The bulk notes import script (`import_notes_from_csv.py`) uses only Python stdlib and does **not** require these packages.
### 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:
Run once from the `backend/` directory to create the SQLite database, all tables, indexes, and a default admin user:
```bash
cd backend
@@ -107,15 +120,19 @@ This creates `backend/cve_database.db` and a default admin account:
### 6. Run database migrations
After the initial setup, apply the feature migrations in order:
After the initial setup, apply feature migrations in order:
```bash
cd backend
node migrations/add_weekly_reports_table.js
node migrations/add_knowledge_base_table.js
node migrations/add_archer_tickets_table.js
node migrations/add_ivanti_sync_table.js
node migrations/add_ivanti_findings_tables.js
```
The Ivanti findings tables migration also handles adding the `fp_workflow_counts_json` and `fp_id_counts_json` columns idempotently on each server start — no manual re-run is needed after the first run.
---
## Configuration
@@ -124,28 +141,37 @@ The application is configured via `.env` files. These files are gitignored and m
### Backend: `backend/.env`
```
```env
PORT=3001
API_HOST=localhost
CORS_ORIGINS=http://YOUR_IP:3000
SESSION_SECRET=change-this-to-a-random-secret
NODE_ENV=development
SESSION_SECRET=change-this-to-a-long-random-string
NODE_ENV=production
# Optional: NVD API key for higher rate limits
# Optional: NVD API key for higher rate limits (50 req/30s vs 5 req/30s)
# Register at https://nvd.nist.gov/developers/request-an-api-key
NVD_API_KEY=your-key-here
# Ivanti / RiskSense integration (required for Reporting page sync)
IVANTI_API_KEY=your-ivanti-api-key
IVANTI_CLIENT_ID=1550
# Optional: filter workflows to a specific person's submissions
IVANTI_FIRST_NAME=
IVANTI_LAST_NAME=
# Set to 'true' if your network has SSL inspection / self-signed certs
IVANTI_SKIP_TLS=false
```
### Frontend: `frontend/.env`
```
```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.
Replace `YOUR_IP` with the machine's IP address or hostname. Use `localhost` for local-only access.
**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.
> **Important:** React caches environment variables at build/start time. After changing `frontend/.env`, fully restart the frontend process — a browser refresh alone is not sufficient.
---
@@ -165,19 +191,21 @@ The start script saves PIDs to `backend.pid` and `frontend.pid`. Logs are writte
### Running manually
```bash
# Terminal 1 - backend
# Terminal 1 backend
cd backend
node server.js
# Terminal 2 - frontend
# Terminal 2 frontend
cd frontend
npm start
```
### Default ports
- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:3001 |
---
@@ -189,38 +217,167 @@ 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 |
| `viewer` | Read-only: CVEs, documents, findings, reports, knowledge base, Archer tickets |
| `editor` | All viewer permissions plus: create/update CVEs, upload documents, sync Ivanti findings, save notes and overrides, manage knowledge base articles, manage Archer tickets, upload weekly reports |
| `admin` | All editor permissions plus: delete documents, delete 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
### Home Dashboard — CVE Management
The home page is the primary CVE workflow tool.
**CVE List**
- Search CVEs by keyword (matches CVE ID, vendor, description)
- Filter by vendor, severity (Critical / High / Medium / Low), and status
- Color-coded severity badges: Critical (red), High (amber), Medium (sky blue), Low (green)
- Paginated list view
**CVE Operations (editor/admin)**
- Add a new CVE entry — NVD auto-fill populates description, severity, and published date automatically
- Edit any field on an existing CVE entry
- Update status for all vendor rows matching a CVE ID in one click
- 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
- The same CVE ID can be tracked across multiple vendors independently
### NVD Integration
**Document Management**
- Upload documents attached to a CVE/vendor pair
- 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, ZIP, GZ, TAR, 7Z)
- File size limit: 10 MB per upload
- Admins can delete documents
**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_KEY` environment variable for higher rate limits
- Bulk NVD Sync (editor/admin): fetch updated metadata for all CVEs in the database in one operation
- CVSS severity cascade: v3.1 preferred, then v3.0, then v2.0
- Rate-limit aware: respects NVD's 5 req/30s unauthenticated limit; with `NVD_API_KEY` the limit increases to 50 req/30s
### Document Management
**Archer Ticket Quick Navigation**
- Archer EXC numbers shown on CVE rows
- Clicking an EXC badge navigates to the Reporting page with that EXC number pre-filtered
Documents are attached to a CVE/vendor pair and stored on disk under `backend/uploads/<CVE-ID>/<vendor>/`.
**Calendar Widget**
- Shows current month with red dot indicators on dates where Ivanti findings are due
- Click a date to navigate to the Reporting page filtered to that due date
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).
### Reporting — Host Findings
File size limit: 10 MB per upload.
The Reporting page is the core operational view for remediation tracking. It integrates with Ivanti/RiskSense to show all host findings for the configured business units.
#### Syncing Data
Click **Sync** in the top-right of the page to pull the latest findings from Ivanti. The sync:
1. Fetches all open host findings matching your BU filters and severity range (8.59.9)
2. Fetches the closed finding count separately
3. Sweeps all closed findings to capture FP workflow states (including Approved FPs that are now closed)
4. Stores everything in the local SQLite cache
Findings are auto-synced on a 24-hour schedule. The last sync timestamp and status are shown at the top of the page.
> **Note:** The Reporting page will show "No data — click Sync to load" until the first sync completes. `IVANTI_API_KEY` must be set in `backend/.env`.
#### Metric Charts
Four donut charts are shown at the top of the page.
| Chart | What it shows |
|---|---|
| **Open vs Closed** | Total open vs closed host findings. Counts come from the Ivanti API directly (not from the local cache) so closed findings are always reflected even though they aren't stored locally. |
| **Action Coverage** | Findings broken down by action taken: **FP Request** (has an FP# workflow ticket) · **Archer Exception** (has an EXC- number in notes) · **Pending** (no action yet). Click any segment to filter the table. |
| **FP Finding Status** | How many *findings* fall into each FP workflow state (Actionable, Requested, Reworked, Approved, Rejected, Expired, Unknown). Includes closed findings — an Approved FP closes the finding and would be invisible otherwise. |
| **FP Workflow Status** | How many *unique FP# ticket IDs* are in each state. One FP# ticket can cover many findings; this chart counts tickets, not findings. |
#### Findings Table
The table shows all open findings from the cache. Each row represents a single host finding.
**Columns**
| Column | Description |
|---|---|
| Finding ID | Ivanti finding identifier |
| Severity | Numerical VRR score with group label (CRITICAL / HIGH) |
| Title | Vulnerability title |
| CVEs | Associated CVE IDs — up to 2 shown, remainder as "+N" |
| Host | Hostname — inline editable (see Overrides below) |
| IP Address | Host IP address |
| DNS | DNS/FQDN — inline editable |
| Due Date | Remediation due date; red if overdue, amber if within 30 days |
| SLA | SLA status: OVERDUE / AT_RISK / WITHIN_SLA |
| BU | Business unit; STEAM rows are highlighted |
| Workflow | FP# ticket ID and state badge — color-coded by state |
| Last Found | Last detection date from Ivanti |
| Notes | Free-form notes field — inline editable, persists across syncs |
**Column Management**
Click the **Columns** button to open the column manager:
- Toggle column visibility with the eye icon
- Drag rows to reorder columns
- Column order and visibility persist to `localStorage`
**Sorting**
Click any sortable column header to sort ascending; click again to sort descending.
**Filtering**
Click the filter icon (⊙) on any filterable column header to open a filter dropdown:
- Search box to narrow options
- Multi-select checkboxes — all values are selected by default
- **`— empty —`** option at the top: selects findings where the cell has no value (e.g., filter the Workflow column to `— empty —` to see all findings with no FP ticket assigned)
- "Select All" and "Clear" bulk buttons
- Multiple column filters work as AND (all must match)
- Active filter badge and "Clear Filters" button appear when filters are applied
The **Action Coverage** donut chart also acts as a filter — click a segment to filter the table to that action type.
**Inline Editing**
- **Hostname / DNS**: Click a cell to edit. An amber dot (●) indicates the value has been overridden from what Ivanti reported. Use the revert button (↻) to restore the original value. Changes save on blur or Enter; Escape cancels.
- **Notes**: Click to edit. Saves on blur. Maximum 255 characters. Notes survive cache refreshes.
**Exporting**
Click the **Export** button to download the current view (filtered, sorted, visible columns only):
- **CSV** — UTF-8 with BOM for Excel compatibility
- **Excel (.xlsx)** — Auto-fitted column widths
Filename format: `findings-export-YYYY-MM-DD.csv` / `.xlsx`
---
### 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 (DOC, DOCX, XLS, XLSX, PPT, PPTX), HTML, JSON, YAML, and images (PNG, JPG, GIF).
---
### 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
- Clicking an EXC number on the home page navigates directly to the Reporting page with that EXC pre-filtered
---
### Weekly Reports
@@ -232,40 +389,71 @@ Editors and admins can upload weekly vulnerability reports as `.xlsx` files. The
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:
Admins can manage user accounts from the UI:
- 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.
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 log with filtering by user, action type, entity type, and date range. Results are paginated (25 per page).
---
## Scripts
### `backend/scripts/import_notes_from_csv.py`
Bulk-import notes into the findings cache from a CSV file. Useful for onboarding existing notes or migrating from a spreadsheet.
**CSV format:**
```csv
ID,NOTES
12345678,EXC-5754
87654321,Patched in Feb maintenance window
```
**Usage:**
```bash
cd backend/scripts
# Preview what would be imported (no writes)
python3 import_notes_from_csv.py input.csv --dry-run
# Import against the default database path
python3 import_notes_from_csv.py input.csv
# Import against a specific database
python3 import_notes_from_csv.py input.csv --db /path/to/cve_database.db
```
| Argument | Description |
|---|---|
| `csv_file` | Path to the input CSV (required) |
| `--db` | Path to the SQLite database (default: `../cve_database.db`) |
| `--dry-run` | Preview changes without writing to the database |
- Notes longer than 255 characters are truncated with a warning
- Finding IDs not present in the active Ivanti cache are skipped
- Uses UPSERT — running the same CSV twice is safe
**Dependencies:** Python stdlib only (no pip install required).
---
### `backend/scripts/split_cve_report.py`
Called automatically by the weekly report upload flow. Not intended to be run manually. Splits multi-CVE rows in the uploaded Excel report into one row per CVE ID.
**Dependencies:** `pandas>=2.0.0`, `openpyxl>=3.0.0`
---
@@ -286,21 +474,21 @@ All endpoints are prefixed with `/api`. All endpoints except `/api/auth/login` a
| Method | Path | Role | Description |
|---|---|---|---|
| GET | `/api/cves` | viewer+ | List CVEs with optional filters: `search`, `vendor`, `severity`, `status` |
| GET | `/api/cves` | viewer+ | List CVEs; query params: `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 |
| GET | `/api/cves/check/:cveId` | viewer+ | Quick check: existence and status of a CVE |
| GET | `/api/cves/distinct-ids` | viewer+ | All distinct CVE IDs (used by NVD sync) |
| GET | `/api/cves/:cveId/vendors` | viewer+ | 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=` |
| GET | `/api/cves/:cveId/documents` | viewer+ | List documents for a CVE; optional `?vendor=` filter |
| 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 |
@@ -308,9 +496,27 @@ All endpoints are prefixed with `/api`. All endpoints except `/api/auth/login` a
| Method | Path | Role | Description |
|---|---|---|---|
| GET | `/api/nvd/lookup/:cveId` | viewer+ | Look up a single CVE in the NVD API |
| GET | `/api/nvd/lookup/:cveId` | viewer+ | Look up a single CVE in the NVD 2.0 API |
| POST | `/api/cves/nvd-sync` | editor+ | Bulk update CVE metadata from NVD |
### Ivanti / RiskSense — Workflows
| Method | Path | Role | Description |
|---|---|---|---|
| GET | `/api/ivanti/workflows` | viewer+ | Get cached workflow data (total, list, sync status) |
| POST | `/api/ivanti/workflows/sync` | viewer+ | Trigger an immediate workflow sync from Ivanti |
### Ivanti / RiskSense — Host Findings
| Method | Path | Role | Description |
|---|---|---|---|
| GET | `/api/ivanti/findings` | viewer+ | Get cached findings with notes and overrides merged in |
| POST | `/api/ivanti/findings/sync` | viewer+ | Trigger an immediate findings sync from Ivanti |
| GET | `/api/ivanti/findings/counts` | viewer+ | Open vs closed finding totals |
| GET | `/api/ivanti/findings/fp-workflow-counts` | viewer+ | FP workflow state breakdown — returns `findingCounts`, `findingTotal`, `idCounts`, `idTotal` |
| PUT | `/api/ivanti/findings/:findingId/override` | editor+ | Override `hostName` or `dns` for a finding; empty value clears the override |
| PUT | `/api/ivanti/findings/:findingId/note` | viewer+ | Save or update a finding note (max 255 chars) |
### Weekly Reports
| Method | Path | Role | Description |
@@ -335,7 +541,7 @@ All endpoints are prefixed with `/api`. All endpoints except `/api/auth/login` a
| Method | Path | Role | Description |
|---|---|---|---|
| GET | `/api/archer-tickets` | viewer+ | List tickets, optional filters: `cve_id`, `vendor`, `status` |
| 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 |
@@ -354,15 +560,15 @@ All endpoints are prefixed with `/api`. All endpoints except `/api/auth/login` a
| Method | Path | Role | Description |
|---|---|---|---|
| GET | `/api/audit-logs` | admin | Paginated audit log with filters |
| GET | `/api/audit-logs/actions` | admin | List distinct action types |
| GET | `/api/audit-logs` | admin | Paginated audit log; filters: `user`, `action`, `entityType`, `startDate`, `endDate` |
| GET | `/api/audit-logs/actions` | admin | List distinct action types for filter dropdowns |
### 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) |
| GET | `/api/stats` | viewer+ | Dashboard statistics (total, critical count, addressed count, document count) |
---
@@ -374,82 +580,105 @@ cve-dashboard/
├── stop-servers.sh # Stop all servers
├── backend/
│ ├── server.js # Express app, CVE/document endpoints, middleware
│ ├── server.js # Express app — routes, middleware, file upload, security headers
│ ├── setup.js # One-time DB initialization and default admin creation
│ ├── cve_database.db # SQLite database (gitignored)
│ ├── uploads/ # File storage (gitignored)
│ ├── uploads/ # File storage root (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
│ │ ├── 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
│ │ ├── ivantiWorkflows.js # Ivanti workflow batch sync and cache
│ │ └── ivantiFindings.js # Ivanti host findings sync, notes, overrides, FP counts
│ ├── middleware/
│ │ └── auth.js # requireAuth and requireRole middleware
│ │ └── auth.js # requireAuth and requireRole middleware
│ ├── helpers/
│ │ ├── auditLog.js # logAudit helper
│ │ └── excelProcessor.js # Calls Python script for report processing
│ │ ├── auditLog.js # logAudit helper (fire-and-forget)
│ │ └── excelProcessor.js # Calls Python script for report processing
│ ├── migrations/
│ │ ├── add_weekly_reports_table.js
│ │ ├── add_knowledge_base_table.js
│ │ ── add_archer_tickets_table.js
│ │ ── add_archer_tickets_table.js
│ │ ├── add_ivanti_sync_table.js # Ivanti workflow cache table
│ │ └── add_ivanti_findings_tables.js # Findings cache, notes, counts, overrides tables
│ └── scripts/
│ ├── split_cve_report.py # Python: splits multi-CVE rows in Excel reports
── requirements.txt # pandas, openpyxl
│ ├── split_cve_report.py # Splits multi-CVE rows in Excel reports
── import_notes_from_csv.py # Bulk-import finding notes from CSV
│ └── requirements.txt # pandas, openpyxl (weekly report processing only)
└── frontend/
└── src/
├── App.js # Main application, CVE list, filters, modals
├── App.css # Global styles
├── App.js # Home dashboard — CVE list, filters, modals, calendar
├── App.css # Global styles and CSS variables
├── contexts/
│ └── AuthContext.js # Auth state provider
│ └── AuthContext.js # Auth state provider (login, logout, role helpers)
└── 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
├── LoginForm.js # Login page
├── NavDrawer.js # Side navigation drawer
├── UserMenu.js # User dropdown in header
├── CalendarWidget.js # Due-date calendar with Ivanti finding indicators
├── UserManagement.js # Admin user management panel
├── AuditLog.js # Admin audit log viewer
├── NvdSyncModal.js # Bulk NVD sync dialog with review/apply flow
── KnowledgeBaseModal.js # Knowledge base upload/list modal
├── KnowledgeBaseViewer.js # Inline document viewer
└── pages/
├── ReportingPage.js # Host findings: charts, table, filters, export
├── KnowledgeBasePage.js # Knowledge base page (placeholder)
└── ExportsPage.js # Exports page (placeholder)
```
---
## Database Schema
### Core tables
### Core tables (created by `setup.js`)
**`cves`** - One row per CVE/vendor pair. `UNIQUE(cve_id, vendor)`.
**`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)`.
**`documents`** Files attached to a CVE/vendor pair. Foreign key to `cves(cve_id)`.
**`required_documents`** - Vendor-specific document requirements (advisory, screenshot, etc.).
**`required_documents`** Vendor-specific document requirements.
**`users`** - Accounts with roles: `admin`, `editor`, `viewer`.
**`users`** Accounts with roles: `admin`, `editor`, `viewer`.
**`sessions`** - Active sessions. Expire after 24 hours.
**`sessions`** Active sessions with 24-hour expiry.
**`audit_logs`** - Append-only log of all state-changing actions.
**`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.
**`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.
**`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)`.
**`archer_tickets`** Archer EXC exception tickets linked to CVE/vendor pairs. `UNIQUE(exc_number)`. Foreign key `(cve_id, vendor)` with CASCADE delete.
**`ivanti_sync_state`** — Single-row cache (id=1) for Ivanti workflow batch data: total count, JSON array of workflows, sync timestamp, sync status.
**`ivanti_findings_cache`** — Single-row cache (id=1) for Ivanti host findings: total count, JSON array of slimmed finding objects, sync timestamp, sync status.
**`ivanti_finding_notes`** — Persistent per-finding notes keyed by finding ID. Survives findings cache refreshes. `UNIQUE(finding_id)`.
**`ivanti_counts_cache`** — Single-row cache (id=1) for finding metrics:
- `open_count` / `closed_count` — total open and closed findings
- `fp_workflow_counts_json` — JSON object mapping FP workflow state → number of findings
- `fp_id_counts_json` — JSON object mapping FP workflow state → number of unique FP# ticket IDs
**`ivanti_finding_overrides`** — Editor-applied overrides for `hostName` and `dns` fields. `UNIQUE(finding_id, field)`.
### 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`).
**`cve_document_status`** Aggregates document counts per CVE/vendor and derives a `compliance_status` (`Complete` when an advisory is present, otherwise `Missing Required Docs`).
---
@@ -473,13 +702,14 @@ cve-dashboard/
- 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
- Finding override field must be one of: `hostName`, `dns`
- All database operations use prepared statements (no string interpolation in SQL)
### Error handling
- 500 responses never leak internal error messages to the client
- 500 responses never expose 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
- Descriptive 400/409 responses are safe as they contain only application-authored validation messages
### Security headers
@@ -493,23 +723,27 @@ Applied to all responses:
### Session cookies
`httpOnly: true`, `sameSite: lax`, `secure: true` in production.
`httpOnly: true`, `sameSite: lax`, `secure: true` in production (`NODE_ENV=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.
Migrations are standalone Node.js scripts that modify the database directly. Run them in the listed order on a fresh install. They use `CREATE TABLE IF NOT EXISTS` so they are safe to re-run if needed.
```bash
cd backend
node migrations/add_weekly_reports_table.js
node migrations/add_knowledge_base_table.js
node migrations/add_archer_tickets_table.js
node migrations/add_ivanti_sync_table.js
node migrations/add_ivanti_findings_tables.js
```
For an existing deployment upgrading from an earlier schema, also check the legacy migration scripts in `backend/`:
For an existing deployment upgrading from an earlier schema, check the legacy migration scripts in `backend/`:
- `migrate_multivendor.js` - Adds multi-vendor support to an older single-vendor schema
- `migrate-audit-log.js` - Adds the audit_logs table to pre-auth deployments
- `migrate-to-1.1.js` - General 1.0 to 1.1 schema update
- `migrate_multivendor.js` Adds multi-vendor support to an older single-vendor schema
- `migrate-audit-log.js` Adds the `audit_logs` table to pre-auth deployments
- `migrate-to-1.1.js` General 1.0 1.1 schema update
> The Ivanti FP workflow count columns (`fp_workflow_counts_json`, `fp_id_counts_json`) are added automatically via `ALTER TABLE ... ADD COLUMN` each time the server starts. These statements are idempotent — the error for a duplicate column is silently ignored.