Weekly report feature was removed previously. Cleans up all remaining references from README, architecture diagram, and deletes WEEKLY_REPORT_FEATURE.md entirely.
820 lines
33 KiB
Markdown
820 lines
33 KiB
Markdown
# STEAM Security Dashboard
|
||
|
||
A self-hosted vulnerability management dashboard for the NTS-AEO-STEAM and NTS-AEO-ACCESS-ENG business units. Centralises CVE tracking, Ivanti host finding triage, AEO compliance posture, FP and Archer exception workflows, and internal documentation in a single interface.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Overview](#overview)
|
||
- [Tech Stack](#tech-stack)
|
||
- [Prerequisites](#prerequisites)
|
||
- [Installation](#installation)
|
||
- [Configuration](#configuration)
|
||
- [Running the Application](#running-the-application)
|
||
- [Features](#features)
|
||
- [Authentication and User Roles](#authentication-and-user-roles)
|
||
- [Home — CVE Management](#home--cve-management)
|
||
- [Reporting — Host Findings](#reporting--host-findings)
|
||
- [Ivanti Queue](#ivanti-queue)
|
||
- [Compliance — AEO Posture](#compliance--aeo-posture)
|
||
- [Knowledge Base](#knowledge-base)
|
||
- [Exports](#exports)
|
||
- [Archer Risk Acceptance Tickets](#archer-risk-acceptance-tickets)
|
||
- [User Management (Admin)](#user-management-admin)
|
||
- [Audit Log (Admin)](#audit-log-admin)
|
||
- [Scripts](#scripts)
|
||
- [API Reference](#api-reference)
|
||
- [Architecture](#architecture)
|
||
- [Database Schema](#database-schema)
|
||
- [Security Model](#security-model)
|
||
- [Migrations](#migrations)
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
The STEAM Security Dashboard answers a common problem in vulnerability management: tracking which CVEs have been addressed, whether supporting vendor documentation exists, where each finding is in the remediation or exception workflow, and how the team's overall AEO compliance posture is trending week over week.
|
||
|
||
The application provides:
|
||
|
||
- A searchable, filterable CVE list with per-vendor tracking and document storage
|
||
- NVD API integration to auto-populate CVE metadata
|
||
- **Ivanti/RiskSense integration** — sync open host findings with live FP workflow tracking
|
||
- **Reporting page** with donut charts, advanced per-column filtering, inline editing, Ivanti Queue, and CSV/XLSX export
|
||
- **Ivanti Queue** — personal staging list for batch-processing FP, Archer, and CARD workflows
|
||
- **AEO Compliance page** — weekly xlsx upload, diff preview, per-team metric health cards, device-level violation tracking with notes history
|
||
- Archer risk acceptance ticket tracking (EXC numbers) linked to CVE/vendor pairs
|
||
- 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, xlsx |
|
||
| Compliance xlsx parsing | Python 3, pandas, openpyxl |
|
||
| Bulk notes import | Python 3 (stdlib only) |
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
- Node.js 18 or later
|
||
- npm
|
||
- Python 3 with `pandas` and `openpyxl` (required for compliance xlsx parsing)
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
### 1. Clone the repository
|
||
|
||
```bash
|
||
git clone <repo-url>
|
||
cd cve-dashboard
|
||
```
|
||
|
||
### 2. Install backend dependencies
|
||
|
||
```bash
|
||
cd backend
|
||
npm install
|
||
```
|
||
|
||
### 3. Install frontend dependencies
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
```
|
||
|
||
### 4. Install Python dependencies
|
||
|
||
```bash
|
||
cd backend/scripts
|
||
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 once from the `backend/` directory to create the SQLite database, all tables, indexes, and a default admin user:
|
||
|
||
```bash
|
||
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
|
||
|
||
Apply all feature migrations in order:
|
||
|
||
```bash
|
||
cd backend
|
||
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
|
||
node migrations/add_ivanti_todo_queue_table.js
|
||
node migrations/add_card_workflow_type.js
|
||
node migrations/add_todo_queue_ip_address.js
|
||
node migrations/add_compliance_tables.js
|
||
```
|
||
|
||
### 7. Build the frontend
|
||
|
||
```bash
|
||
cd frontend
|
||
npm run build
|
||
```
|
||
|
||
Or use `npm start` for the development server (see [Running the Application](#running-the-application)).
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
The application is configured via `.env` files. These files are gitignored and must be created manually per environment.
|
||
|
||
### Backend: `backend/.env`
|
||
|
||
```env
|
||
PORT=3001
|
||
API_HOST=localhost
|
||
CORS_ORIGINS=http://YOUR_IP:3000
|
||
SESSION_SECRET=change-this-to-a-long-random-string
|
||
NODE_ENV=production
|
||
|
||
# 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 hostname. Use `localhost` for local-only access.
|
||
|
||
> **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.
|
||
|
||
---
|
||
|
||
## Running the Application
|
||
|
||
### Using the helper scripts (recommended)
|
||
|
||
From the project root:
|
||
|
||
```bash
|
||
./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
|
||
|
||
```bash
|
||
# Terminal 1 — backend
|
||
cd backend
|
||
node server.js
|
||
|
||
# Terminal 2 — frontend (development server)
|
||
cd frontend
|
||
npm start
|
||
```
|
||
|
||
### Default ports
|
||
|
||
| Service | URL |
|
||
|---|---|
|
||
| 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: CVEs, documents, findings, reports, knowledge base, Archer tickets, compliance data |
|
||
| `editor` | All viewer permissions plus: create/update CVEs, upload documents, sync Ivanti findings, save notes and overrides, manage knowledge base, manage Archer tickets, upload compliance reports, manage Ivanti Queue |
|
||
| `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.
|
||
|
||
---
|
||
|
||
### Home — CVE Management
|
||
|
||
The home page is the primary CVE research and tracking 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
|
||
- The same CVE ID can be tracked across multiple vendors independently
|
||
|
||
**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 (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
|
||
|
||
**Archer Ticket Quick Navigation**
|
||
- Archer EXC numbers shown on CVE rows
|
||
- Clicking an EXC badge navigates to the Reporting page pre-filtered to findings with that EXC number
|
||
|
||
**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
|
||
|
||
---
|
||
|
||
### Reporting — Host Findings
|
||
|
||
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** (top right) to pull the latest findings from Ivanti. The sync:
|
||
1. Fetches all open host findings matching your BU filters and severity range (8.5–9.9 VRR)
|
||
2. Fetches the closed finding count separately
|
||
3. Sweeps closed findings to capture FP workflow states (including Approved FPs now closed)
|
||
4. Stores everything in the local SQLite cache
|
||
|
||
Findings are also auto-synced on a 24-hour schedule. The last sync timestamp is shown at the top of the page.
|
||
|
||
> `IVANTI_API_KEY` must be set in `backend/.env` for sync to work.
|
||
|
||
#### Metric Charts
|
||
|
||
| Chart | What it shows |
|
||
|---|---|
|
||
| **Open vs Closed** | Total open vs closed host findings direct from the Ivanti API |
|
||
| **Action Coverage** | Findings by action taken: FP Request · Archer Exception · Pending. Click a segment to filter the table. |
|
||
| **FP Finding Status** | How many *findings* are in each FP workflow state (Actionable, Requested, Reworked, Approved, Rejected, Expired) |
|
||
| **FP Workflow Status** | How many *unique FP# ticket IDs* are in each state — one ticket can cover many findings |
|
||
|
||
#### Findings Table
|
||
|
||
Each row represents a single Ivanti host finding.
|
||
|
||
| 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" badge |
|
||
| Host | Hostname — inline editable |
|
||
| 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 |
|
||
| Workflow | FP# ticket ID and state badge — colour-coded by urgency |
|
||
| Last Found | Last detection date from Ivanti |
|
||
| Notes | Free-form notes — inline editable, persists across syncs |
|
||
|
||
**Inline editing:** Click a Host or DNS cell to override the Ivanti value. An amber dot (●) marks overridden cells; use the revert button (↻) to restore the original. Overrides survive re-syncs.
|
||
|
||
**Filtering:** Click ⊙ on any column header for multi-select filtering. The `— empty —` option filters to findings with no value in that column. Multiple filters are ANDed. The Action Coverage chart also acts as a filter.
|
||
|
||
**Column management:** Toggle visibility and drag to reorder via the **Columns** button. Order and visibility persist to `localStorage`.
|
||
|
||
**Export:** Click **Export** to download the current filtered view as CSV or XLSX.
|
||
|
||
---
|
||
|
||
### Ivanti Queue
|
||
|
||
A personal staging list for batch-processing FP, Archer, and CARD workflows without context-switching into Ivanti mid-review.
|
||
|
||
**Adding items:** Check the checkbox at the far left of any finding row. A popover appears:
|
||
- For **FP** and **Archer** items: enter the Vendor / Platform (e.g., "Juniper MX", "Cisco IOS-XE")
|
||
- For **CARD** items: no vendor entry required — the IP address is captured automatically
|
||
- Select the workflow type: **FP**, **Archer**, or **CARD**
|
||
- Click **Add to Queue** — the row checkbox turns solid blue
|
||
|
||
**Queue panel:** Click the **Queue** button (top right of Reporting page) to open the slide-out panel:
|
||
- **CARD** items appear at the top in their own section with the IP address displayed
|
||
- **FP and Archer** items are grouped alphabetically by vendor below
|
||
- Badges show workflow type: amber = FP, sky = Archer, green = CARD
|
||
|
||
**Working the queue:**
|
||
- Check the green checkbox on an item to mark it complete (strikethrough at reduced opacity)
|
||
- Delete individual items with the trash icon, or select multiple and use **Delete (N)**
|
||
- **Clear Completed** removes all marked-complete items at once
|
||
|
||
Queue items are stored in the database, are **personal to your login**, and persist across sessions and page refreshes.
|
||
|
||
---
|
||
|
||
### Compliance — AEO Posture
|
||
|
||
The Compliance page tracks NTS-AEO team posture against the AEO compliance framework using weekly xlsx reports exported from the NTS_AEO reporting system.
|
||
|
||
#### Upload Workflow
|
||
|
||
Editors and admins can upload a new compliance report via the **Upload Report** button:
|
||
|
||
1. Drop or browse for the `NTS_AEO_YYYY_MM_DD.xlsx` file
|
||
2. The report is parsed server-side and a **diff preview** is shown — new violations, resolved items, and recurring items since the last upload
|
||
3. Click **Confirm Upload** to commit. The upload is recorded and the device table updates immediately.
|
||
|
||
The report date is extracted automatically from the filename.
|
||
|
||
#### Metric Health Cards
|
||
|
||
Each AEO metric (e.g., `2.3.4i`, `5.2.4`) is shown as a health card displaying:
|
||
- Compliance percentage vs target
|
||
- Status: Meets/Exceeds Target · Within 15% of Target · Below 15% of Target
|
||
|
||
Click a card to filter the device table to only devices failing that metric.
|
||
|
||
#### Device Table
|
||
|
||
Shows all devices currently failing one or more metrics (Active tab) or previously resolved (Resolved tab). Columns: Hostname, IP Address, Type, Failing Metrics, Times Seen. Click a row to open the detail panel.
|
||
|
||
#### Detail Panel
|
||
|
||
A slide-out panel for a selected device showing:
|
||
- **Failing Metrics** — each metric with surfaced extra fields (CVEs, SLA status, due date, OS, EoL, Splunk last seen, MFA software)
|
||
- For **2.3.x vulnerability metrics**: the `Ivanti_Vulnerability_ID` is displayed with a **View in Reporting →** button that navigates directly to the Reporting page
|
||
- **Resolved Metrics** — previously failing metrics now back in compliance
|
||
- **History** — how many times the device has appeared on the report and since when
|
||
- **Notes** — timestamped notes per metric with a multi-metric selector if multiple metrics are failing
|
||
|
||
Notes persist across uploads and are keyed to the device hostname and metric ID.
|
||
|
||
#### Teams
|
||
|
||
Only **STEAM** and **ACCESS-ENG** teams are tracked. The team selector at the top of the page switches context between them.
|
||
|
||
---
|
||
|
||
### Knowledge Base
|
||
|
||
A document library for internal reference material — policies, runbooks, vendor advisories, and process guides.
|
||
|
||
- Upload documents with a title, optional description, and category
|
||
- View documents inline in the browser (PDFs render in an iframe; Markdown files render 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).
|
||
|
||
---
|
||
|
||
### Exports
|
||
|
||
Bulk export tools for reports and data extracts.
|
||
|
||
---
|
||
|
||
### 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
|
||
- Clicking an EXC badge on the Home page navigates to the Reporting page pre-filtered to findings with that EXC number in their notes
|
||
|
||
---
|
||
|
||
### User Management (Admin)
|
||
|
||
- 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 payload. Admins can view the log filtered by user, action type, entity type, and date range. Results are paginated (25 per page).
|
||
|
||
---
|
||
|
||
## Scripts
|
||
|
||
### `backend/scripts/parse_compliance_xlsx.py`
|
||
|
||
Called automatically by the compliance upload flow. Parses the NTS_AEO xlsx report and outputs structured JSON to stdout for consumption by the Node compliance route.
|
||
|
||
- Reads all detail sheets; skips `Summary` and `CMDB_9box`
|
||
- Filters to rows where `Compliant == False`
|
||
- Extracts hostname, IP, device type, team, and metric ID per row
|
||
- Captures all non-core columns in `extra_json` (CVEs, SLA status, OS, EoL, Splunk, MFA, Ivanti_Vulnerability_ID, etc.)
|
||
- Parses `Summary` sheet for per-team metric health (compliance_pct, target, status)
|
||
- Extracts report date from the filename (`NTS_AEO_YYYY_MM_DD.xlsx`)
|
||
|
||
**Dependencies:** `pandas>=2.0.0`, `openpyxl>=3.0.0`
|
||
|
||
---
|
||
|
||
### `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).
|
||
|
||
---
|
||
|
||
## 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; 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: 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; 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 |
|
||
|
||
### NVD
|
||
|
||
| Method | Path | Role | Description |
|
||
|---|---|---|---|
|
||
| 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 — 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 |
|
||
| PUT | `/api/ivanti/findings/:findingId/override` | editor+ | Override `hostName` or `dns`; empty value clears the override |
|
||
| PUT | `/api/ivanti/findings/:findingId/note` | viewer+ | Save or update a finding note (max 255 chars) |
|
||
|
||
### Ivanti — Workflows
|
||
|
||
| Method | Path | Role | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/workflows` | viewer+ | Get cached workflow data |
|
||
| POST | `/api/ivanti/workflows/sync` | viewer+ | Trigger an immediate workflow sync |
|
||
|
||
### Ivanti Queue
|
||
|
||
| Method | Path | Role | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/queue` | viewer+ | Get all queue items for the current user |
|
||
| POST | `/api/ivanti/queue` | editor+ | Add a finding to the queue |
|
||
| PATCH | `/api/ivanti/queue/:id` | editor+ | Update a queue item (mark complete, edit vendor/type) |
|
||
| DELETE | `/api/ivanti/queue/:id` | editor+ | Delete a single queue item |
|
||
| DELETE | `/api/ivanti/queue` | editor+ | Delete multiple queue items (body: `{ ids: [...] }`) |
|
||
|
||
### Compliance
|
||
|
||
| Method | Path | Role | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/compliance/preview` | editor+ | Parse an xlsx upload and return diff + temp file path |
|
||
| POST | `/api/compliance/commit` | editor+ | Commit a previewed upload to the database |
|
||
| GET | `/api/compliance/uploads` | viewer+ | List all compliance upload records |
|
||
| GET | `/api/compliance/summary` | viewer+ | Metric health summary; `?team=STEAM` |
|
||
| GET | `/api/compliance/items` | viewer+ | Device list; `?team=STEAM&status=active` |
|
||
| GET | `/api/compliance/items/:hostname` | viewer+ | Full detail for a device (metrics + notes) |
|
||
| GET | `/api/compliance/notes/:hostname/:metricId` | viewer+ | Notes for a specific hostname/metric |
|
||
| POST | `/api/compliance/notes` | editor+ | Add a note for a hostname/metric |
|
||
|
||
### 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; 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 |
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
cve-dashboard/
|
||
├── start-servers.sh # Start backend + frontend in background
|
||
├── stop-servers.sh # Stop all servers
|
||
│
|
||
├── backend/
|
||
│ ├── server.js # Express app — routes, middleware, security headers
|
||
│ ├── setup.js # One-time DB initialization and default admin creation
|
||
│ ├── cve_database.db # SQLite database (gitignored)
|
||
│ ├── uploads/ # File storage root (gitignored)
|
||
│ │ ├── <CVE-ID>/<vendor>/ # CVE documents
|
||
│ │ ├── knowledge_base/ # Knowledge base documents
|
||
│ │ └── temp/ # Temporary upload staging
|
||
│ ├── routes/
|
||
│ │ ├── auth.js # Login, logout, session check
|
||
│ │ ├── users.js # User CRUD (admin)
|
||
│ │ ├── auditLog.js # Audit log viewer (admin)
|
||
│ │ ├── nvdLookup.js # NVD API proxy
|
||
│ │ ├── 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
|
||
│ │ ├── ivantiTodoQueue.js # Ivanti Queue — personal FP/Archer/CARD staging list
|
||
│ │ └── compliance.js # AEO compliance upload, diff, device tracking, notes
|
||
│ ├── middleware/
|
||
│ │ └── auth.js # requireAuth and requireRole middleware
|
||
│ ├── helpers/
|
||
│ │ ├── auditLog.js # logAudit helper (fire-and-forget)
|
||
│ ├── migrations/
|
||
│ │ ├── add_knowledge_base_table.js
|
||
│ │ ├── add_archer_tickets_table.js
|
||
│ │ ├── add_ivanti_sync_table.js
|
||
│ │ ├── add_ivanti_findings_tables.js
|
||
│ │ ├── add_ivanti_todo_queue_table.js # Ivanti Queue table
|
||
│ │ ├── add_card_workflow_type.js # CARD workflow type support
|
||
│ │ ├── add_todo_queue_ip_address.js # IP address column on queue items
|
||
│ │ └── add_compliance_tables.js # AEO compliance tables
|
||
│ └── scripts/
|
||
│ ├── parse_compliance_xlsx.py # Parses NTS_AEO xlsx compliance reports
|
||
│ ├── import_notes_from_csv.py # Bulk-import finding notes from CSV
|
||
│ └── requirements.txt # pandas, openpyxl
|
||
│
|
||
└── frontend/
|
||
└── src/
|
||
├── App.js # Home dashboard — CVE list, filters, modals, calendar
|
||
├── App.css # Global styles and CSS variables
|
||
├── contexts/
|
||
│ └── AuthContext.js # Auth state provider (login, logout, role helpers)
|
||
└── components/
|
||
├── 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
|
||
├── KnowledgeBaseModal.js # Knowledge base upload/list modal
|
||
├── KnowledgeBaseViewer.js # Inline document viewer
|
||
└── pages/
|
||
├── ReportingPage.js # Host findings: charts, table, queue, export
|
||
├── CompliancePage.js # AEO compliance: metric cards, device table
|
||
├── ComplianceUploadModal.js # xlsx upload with diff preview
|
||
├── ComplianceDetailPanel.js # Per-device metrics, history, notes
|
||
├── KnowledgeBasePage.js # Knowledge base page
|
||
└── ExportsPage.js # Exports page
|
||
```
|
||
|
||
---
|
||
|
||
## Database Schema
|
||
|
||
### Core tables (created by `setup.js`)
|
||
|
||
**`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.
|
||
|
||
**`users`** — Accounts with roles: `admin`, `editor`, `viewer`.
|
||
|
||
**`sessions`** — Active sessions with 24-hour expiry.
|
||
|
||
**`audit_logs`** — Append-only log of all state-changing actions.
|
||
|
||
### Feature tables (added by migrations)
|
||
|
||
**`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)`.
|
||
|
||
**`ivanti_sync_state`** — Single-row cache for Ivanti workflow batch data.
|
||
|
||
**`ivanti_findings_cache`** — Single-row cache for Ivanti host findings.
|
||
|
||
**`ivanti_finding_notes`** — Persistent per-finding notes keyed by finding ID. Survives cache refreshes. `UNIQUE(finding_id)`.
|
||
|
||
**`ivanti_counts_cache`** — Single-row cache for finding metrics: open/closed counts, FP workflow state breakdowns by finding and by unique ticket ID.
|
||
|
||
**`ivanti_finding_overrides`** — Editor-applied overrides for `hostName` and `dns` fields. `UNIQUE(finding_id, field)`.
|
||
|
||
**`ivanti_todo_queue`** — Personal per-user queue of findings staged for FP, Archer, or CARD processing. Keyed by `(user_id, finding_id)`.
|
||
|
||
**`compliance_uploads`** — Record of each compliance xlsx upload: filename, report date, uploader, timestamp, and new/resolved/recurring counts.
|
||
|
||
**`compliance_items`** — One row per device/metric violation. Tracks hostname, IP, device type, team, metric ID, category, `extra_json` (all non-core xlsx columns), status (active/resolved), first seen upload, and times seen. Identity key: `(hostname, metric_id)`.
|
||
|
||
**`compliance_notes`** — Timestamped notes per hostname/metric. Multiple notes per combination are supported. Foreign-key linked to compliance items.
|
||
|
||
### 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 in `path.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+$/`
|
||
- 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 expose internal error messages to the client
|
||
- Full errors are logged server-side only
|
||
- Descriptive 400/409 responses contain only application-authored validation messages
|
||
|
||
### Security headers
|
||
|
||
Applied to all responses:
|
||
|
||
- `X-Content-Type-Options: nosniff`
|
||
- `X-Frame-Options: SAMEORIGIN`
|
||
- `X-XSS-Protection: 1; mode=block`
|
||
- `Referrer-Policy: strict-origin-when-cross-origin`
|
||
- `Permissions-Policy: camera=(), microphone=(), geolocation=()`
|
||
|
||
### Session cookies
|
||
|
||
`httpOnly: true`, `sameSite: lax`, `secure: true` in production (`NODE_ENV=production`).
|
||
|
||
---
|
||
|
||
## Migrations
|
||
|
||
Migrations are standalone Node.js scripts. Run them in the listed order on a fresh install. All use `CREATE TABLE IF NOT EXISTS` or `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` and are safe to re-run.
|
||
|
||
```bash
|
||
cd backend
|
||
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
|
||
node migrations/add_ivanti_todo_queue_table.js
|
||
node migrations/add_card_workflow_type.js
|
||
node migrations/add_todo_queue_ip_address.js
|
||
node migrations/add_compliance_tables.js
|
||
```
|
||
|
||
For deployments upgrading from an older schema, the following legacy migration scripts are also available 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 → 1.1 schema update
|
||
|
||
> Several columns (`fp_workflow_counts_json`, `fp_id_counts_json`, `seen_count`, `summary_json`) are added automatically via idempotent `ALTER TABLE` statements each time the server starts. No manual re-run is needed.
|