Brings the reference manual in line with the current codebase: - Add CCP Metrics (VCL multi-vertical) feature section and full API reference - Add CARD Asset Ownership feature section with tooltip, direct actions, queue actions - Add Granite Loader Sheet feature section with CARD enrichment details - Add Atlas Action Plans feature section with cache and badge rendering - Add Finding Archive Tracking feature section with lifecycle states and anomaly detection - Add Archer Template Library feature section with hierarchy and clone - Add In-App Notifications feature section - Add Feedback (GitLab integration) feature section with webhook lifecycle - Add FP Workflow Submission feature section with lifecycle tracking - Update Ivanti Queue to document all 6 workflow types (FP, Archer, CARD, GRANITE, DECOM, Remediate) - Update Reporting section with Group by Host, CARD tooltip, multi-BU scope - Update Jira section with flexible creation, consolidation modal, raw status - Update Configuration with CARD, Atlas, GitLab, IVANTI_BU_FILTER, IVANTI_MANAGED_BUS vars - Update Architecture tree with all routes, helpers, and components - Update Database Schema with 15+ new tables - Update Migrations section with all 46 migration files - Update API Reference with Archer Templates, CARD, Atlas, VCL, Notifications, Feedback, Webhooks - Add per-user settings (bu_teams, ivanti_identity) to auth section
1637 lines
88 KiB
Markdown
1637 lines
88 KiB
Markdown
# STEAM Security Dashboard
|
||
|
||
A self-hosted vulnerability management dashboard for the NTS-AEO-STEAM, NTS-AEO-ACCESS-ENG, NTS-AEO-ACCESS-OPS, and NTS-AEO-INTELDEV business units. Centralises CVE tracking, Ivanti host finding triage, AEO compliance posture monitoring, CCP Metrics cross-org compliance reporting, FP/Archer/CARD/GRANITE/DECOM exception workflows, CARD asset ownership management, Granite Loader Sheet generation, Jira ticket management, 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)
|
||
- [Running as systemd services (auto-start on reboot)](#running-as-systemd-services-auto-start-on-reboot)
|
||
- [Features](#features)
|
||
- [Authentication and User Groups](#authentication-and-user-groups)
|
||
- [Home — CVE Management](#home--cve-management)
|
||
- [Reporting — Host Findings](#reporting--host-findings)
|
||
- [Ivanti Queue](#ivanti-queue)
|
||
- [FP Workflow Submission](#fp-workflow-submission)
|
||
- [Compliance — AEO Posture](#compliance--aeo-posture)
|
||
- [CCP Metrics — Multi-Vertical Compliance](#ccp-metrics--multi-vertical-compliance)
|
||
- [CARD Asset Ownership](#card-asset-ownership)
|
||
- [Granite Loader Sheet](#granite-loader-sheet)
|
||
- [Atlas Action Plans](#atlas-action-plans)
|
||
- [Finding Archive Tracking](#finding-archive-tracking)
|
||
- [Knowledge Base](#knowledge-base)
|
||
- [Exports](#exports)
|
||
- [Jira Tickets](#jira-tickets)
|
||
- [Archer Risk Acceptance Tickets](#archer-risk-acceptance-tickets)
|
||
- [Archer Template Library](#archer-template-library)
|
||
- [In-App Notifications](#in-app-notifications)
|
||
- [Feedback — GitLab Integration](#feedback--gitlab-integration)
|
||
- [Admin Panel](#admin-panel)
|
||
- [Scripts](#scripts)
|
||
- [API Reference](#api-reference)
|
||
- [Architecture](#architecture)
|
||
- [Database Schema](#database-schema)
|
||
- [Security Model](#security-model)
|
||
- [Upgrading an Existing Deployment](#upgrading-an-existing-deployment)
|
||
- [Migrations](#migrations)
|
||
- [Troubleshooting](#troubleshooting)
|
||
|
||
---
|
||
|
||
## 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, per-BU trend lines, and archive detection
|
||
- **Reporting page** with donut charts, Group by Host toggle, CARD ownership tooltips, advanced per-column filtering, inline editing, Ivanti Queue, and CSV/XLSX export
|
||
- **Ivanti Queue** — personal staging list for batch-processing FP, Archer, CARD, GRANITE, DECOM, and Remediate workflows
|
||
- **FP Workflow Submission** — submit False Positive workflows directly to Ivanti API with attachments and lifecycle tracking
|
||
- **AEO Compliance page** — weekly xlsx upload, diff preview, per-team metric health cards, device-level violation tracking with notes history
|
||
- **CCP Metrics page** — multi-vertical VCL upload, cross-org compliance reporting with forecast burndown, metric drill-down, and data management
|
||
- **CARD Asset Ownership** — owner lookup, confirm/decline/redirect actions, tooltip integration, Granite enrichment
|
||
- **Granite Loader Sheet** — generate xlsx loader sheets with CARD enrichment, searchable picklists, per-row editing
|
||
- **Atlas action plan tracking** with per-host vulnerability mapping and badge rendering
|
||
- **Jira integration** — flexible ticket creation, multi-item consolidation, vendor-specific issue types, JQL sync
|
||
- **Finding archive tracking** — automatic detection of disappeared/returned findings with anomaly logging
|
||
- Archer risk acceptance ticket tracking (EXC numbers) linked to CVE/vendor pairs
|
||
- **Archer Template Library** — store and clone static content for Archer Risk Acceptance forms
|
||
- **In-app notifications** — native notification system for sync events and workflow completions
|
||
- **Feedback integration** — submit bug reports and feature requests directly to GitLab as issues
|
||
- A knowledge base for internal documentation and policies
|
||
- Group-based access control (Admin, Standard_User, Leadership, Read_Only) with a full audit trail
|
||
- Per-user BU team assignments and Ivanti identity for multi-tenant scoping
|
||
|
||
---
|
||
|
||
## Tech Stack
|
||
|
||
| Layer | Technology |
|
||
|---|---|
|
||
| Backend | Node.js 18+, Express 5 |
|
||
| Database | PostgreSQL 16 (Docker container on port 5433, `pg` driver) |
|
||
| File uploads | Multer 2 |
|
||
| Auth | bcryptjs, cookie-based sessions, express-rate-limit |
|
||
| Frontend | React 19, lucide-react, recharts, xlsx, react-markdown, rehype-sanitize, mermaid |
|
||
| Compliance xlsx parsing | Python 3, pandas, openpyxl |
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
- Node.js 18 or later
|
||
- npm
|
||
- Docker (for the PostgreSQL 16 container)
|
||
- Python 3 with `python3-pandas` and `python3-openpyxl` apt packages (required for compliance xlsx parsing)
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
### 1. Clone the repository
|
||
|
||
```bash
|
||
git clone <repo-url>
|
||
cd cve-dashboard
|
||
```
|
||
|
||
### 2. Install backend dependencies
|
||
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
### 3. Install frontend dependencies
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
```
|
||
|
||
### 4. Install Python dependencies
|
||
|
||
Install via apt — this is the correct approach on Ubuntu/Debian and mirrors the dev server setup:
|
||
|
||
```bash
|
||
apt install -y python3-pandas python3-openpyxl
|
||
```
|
||
|
||
> If apt packages are unavailable or you need a specific version, see `docs/python-venv-setup.md` for the venv fallback approach.
|
||
|
||
> A bulk notes import script (`import_notes_from_csv.py`) is also available in `backend/scripts/` for maintenance tasks like backfilling notes from a CSV. It uses only Python stdlib.
|
||
|
||
### 5. Configure environment variables
|
||
|
||
Create `backend/.env` — the server will refuse to start without `SESSION_SECRET`:
|
||
|
||
```bash
|
||
cd backend
|
||
cp .env.example .env
|
||
# Edit .env and set SESSION_SECRET to a random string:
|
||
# openssl rand -base64 32
|
||
```
|
||
|
||
See [Configuration](#configuration) for all available options.
|
||
|
||
### 6. Deploy PostgreSQL and initialize the database
|
||
|
||
The deploy script handles the full setup — starts the Postgres container, creates the schema, installs the `pg` dependency, migrates data from SQLite (if present), and builds the frontend:
|
||
|
||
```bash
|
||
chmod +x scripts/deploy-postgres.sh
|
||
./scripts/deploy-postgres.sh
|
||
```
|
||
|
||
This starts a PostgreSQL 16 container (`steam-postgres`) on port 5433 with a persistent Docker volume, then runs `backend/db-schema.sql` to create all tables, indexes, and views.
|
||
|
||
For manual setup or troubleshooting, the individual steps are:
|
||
|
||
```bash
|
||
docker compose up -d # Start Postgres container
|
||
node backend/setup-postgres.js # Run schema DDL
|
||
node backend/scripts/migrate-to-postgres.js # Migrate data from SQLite (if upgrading)
|
||
```
|
||
|
||
> **Existing deployments:** If upgrading from SQLite, the deploy script automatically runs the data migration. The original `backend/cve_database.db` file is preserved as a backup. See [Postgres Migration Plan](docs/guides/postgres-migration-plan.md) for full details.
|
||
|
||
### 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=<generate with: openssl rand -base64 32>
|
||
# NODE_ENV=production — see note below
|
||
|
||
# PostgreSQL connection (required)
|
||
DATABASE_URL=postgresql://steam:<password>@localhost:5433/cve_dashboard
|
||
|
||
# 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=
|
||
# Comma-separated list of BU values to sync from Ivanti.
|
||
# Default if unset: NTS-AEO-ACCESS-ENG,NTS-AEO-STEAM
|
||
IVANTI_BU_FILTER=NTS-AEO-ACCESS-ENG,NTS-AEO-STEAM
|
||
# Comma-separated list of BUs considered "managed" for archive drift classification.
|
||
# Findings leaving these BUs are classified as bu_reassignment.
|
||
# Default if unset: NTS-AEO-ACCESS-ENG,NTS-AEO-STEAM
|
||
IVANTI_MANAGED_BUS=NTS-AEO-ACCESS-ENG,NTS-AEO-STEAM
|
||
# Set to 'true' if your network has SSL inspection / self-signed certs
|
||
IVANTI_SKIP_TLS=false
|
||
|
||
# Atlas InfoSec API (required for Atlas action plan tracking)
|
||
ATLAS_API_URL=https://atlas-infosec.caas.charterlab.com
|
||
ATLAS_API_USER=your-atlas-user
|
||
ATLAS_API_PASS=your-atlas-password
|
||
# Set to true if behind Charter's SSL inspection proxy
|
||
ATLAS_SKIP_TLS=false
|
||
|
||
# Jira Data Center REST API (required for Jira Tickets page)
|
||
# VPN or Charter Network connection required for all Jira instances.
|
||
# Service accounts use Basic Auth (JIRA_API_USER + JIRA_API_TOKEN).
|
||
# PATs require ATLSUP approval — set JIRA_AUTH_METHOD=pat to use JIRA_PAT instead.
|
||
# Rate limits: 1440 requests/day, burst of 60/minute.
|
||
JIRA_BASE_URL=https://jira.charter.com
|
||
JIRA_AUTH_METHOD=basic
|
||
JIRA_API_USER=your-service-account
|
||
JIRA_API_TOKEN=your-api-token
|
||
# JIRA_PAT=your-pat-token
|
||
JIRA_PROJECT_KEY=VULN
|
||
JIRA_ISSUE_TYPE=Task
|
||
JIRA_SKIP_TLS=false
|
||
|
||
# CARD Asset Ownership API (required for CARD integration)
|
||
# OAuth Bearer token auth — service account must be onboarded with the CARD team.
|
||
# Tokens are acquired automatically via Basic Auth and cached for 1 hour.
|
||
CARD_API_URL=https://card.charter.com
|
||
CARD_API_USER=your-card-user
|
||
CARD_API_PASS=your-card-password
|
||
# Set to true if behind Charter's SSL inspection proxy
|
||
CARD_SKIP_TLS=false
|
||
|
||
# GitLab Feedback Integration (bug reports and feature requests from the dashboard)
|
||
# PAT needs 'api' scope. Project ID is the numeric ID from GitLab project settings.
|
||
GITLAB_URL=http://steam-gitlab.charterlab.com
|
||
GITLAB_PROJECT_ID=13
|
||
GITLAB_PAT=glpat-xxxxxxxxxxxxx
|
||
# Webhook secret — shared secret for validating incoming webhook requests.
|
||
# Set this same value in GitLab project > Settings > Webhooks > Secret Token.
|
||
# Generate with: openssl rand -hex 20
|
||
GITLAB_WEBHOOK_SECRET=changeme_generate_a_random_secret
|
||
```
|
||
|
||
**`SESSION_SECRET` is required.** The server will exit on startup if it is not set. Generate one with `openssl rand -base64 32`.
|
||
|
||
**`DATABASE_URL` is required.** The backend connects to PostgreSQL via this connection string. Format: `postgresql://user:password@host:port/database`. The deploy script adds this automatically.
|
||
|
||
**`NODE_ENV` and the Secure cookie flag:** When `NODE_ENV=production`, session cookies are set with the `Secure` flag, which means the browser will only send them over HTTPS connections. If you are running the application over plain HTTP (no TLS/SSL), you **must** leave `NODE_ENV` unset or set it to `development` — otherwise login will succeed but every subsequent API request will return 401 because the browser silently drops the cookie. Only set `NODE_ENV=production` when the application is served behind HTTPS (e.g., via a reverse proxy with TLS termination).
|
||
|
||
### 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 via systemd
|
||
./stop-servers.sh # Stops both services
|
||
```
|
||
|
||
Both scripts call `systemctl start` / `systemctl stop` on the `cve-backend` and `cve-frontend` services. The systemd units must be installed first — see [Running as systemd services](#running-as-systemd-services-auto-start-on-reboot) for setup.
|
||
|
||
### Running manually
|
||
|
||
```bash
|
||
# Terminal 1 — backend
|
||
cd backend
|
||
node server.js
|
||
|
||
# Terminal 2 — frontend (development server)
|
||
cd frontend
|
||
npm start
|
||
```
|
||
|
||
### Running as systemd services (auto-start on reboot)
|
||
|
||
Two systemd unit files are installed to `/etc/systemd/system/` so the dashboard starts automatically when the server boots:
|
||
|
||
| Unit | What it runs |
|
||
|---|---|
|
||
| `cve-backend.service` | `node server.js` from `backend/` |
|
||
| `cve-frontend.service` | `npm start` from `frontend/` (waits for backend) |
|
||
|
||
Both services load their respective `.env` files, restart on failure (5-second delay), and append output to `backend/backend.log` and `frontend/frontend.log`.
|
||
|
||
**First-time setup** (if the units are not yet installed):
|
||
|
||
```bash
|
||
# Copy the unit files into systemd
|
||
cp systemd/cve-backend.service /etc/systemd/system/
|
||
cp systemd/cve-frontend.service /etc/systemd/system/
|
||
|
||
# Reload systemd, enable on boot, and start
|
||
systemctl daemon-reload
|
||
systemctl enable --now cve-backend cve-frontend
|
||
```
|
||
|
||
**Common commands:**
|
||
|
||
```bash
|
||
systemctl status cve-backend cve-frontend # Check both services
|
||
systemctl restart cve-backend # Restart backend only
|
||
systemctl restart cve-frontend # Restart frontend only
|
||
systemctl stop cve-backend cve-frontend # Stop both
|
||
journalctl -u cve-backend -f # Follow backend journal
|
||
journalctl -u cve-frontend -f # Follow frontend journal
|
||
```
|
||
|
||
> The helper scripts (`start-servers.sh` / `stop-servers.sh`) are thin wrappers around `systemctl start` / `systemctl stop`. They require the systemd units to be installed and enabled as described above.
|
||
|
||
### Default ports
|
||
|
||
| Service | URL |
|
||
|---|---|
|
||
| Frontend | http://localhost:3000 |
|
||
| Backend API | http://localhost:3001 |
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
### Authentication and User Groups
|
||
|
||
All routes require authentication. Four user groups are supported:
|
||
|
||
| Group | Permissions |
|
||
|---|---|
|
||
| `Admin` | Full CRUD on all resources, user management, audit log access, export all data, delete any resource regardless of ownership |
|
||
| `Standard_User` | View all data, create and edit resources, delete own resources (with state and compliance restrictions), basic export (CSV/XLSX) |
|
||
| `Leadership` | View all data, export reports/compliance/visualizations, no create/edit/delete |
|
||
| `Read_Only` | View all data only — no create, edit, delete, or export |
|
||
|
||
**Standard User delete restrictions:**
|
||
- Can only delete resources they created (`created_by` ownership check)
|
||
- Cannot delete findings marked as resolved or closed
|
||
- Cannot delete tickets linked to compliance reports
|
||
- CVE deletion triggers a cascade impact check — if any associated Archer or JIRA ticket is compliance-linked, deletion is blocked and requires Admin intervention
|
||
|
||
Sessions expire after 24 hours. Session tokens are stored in `httpOnly` cookies. Login is rate-limited to 20 attempts per 15-minute window.
|
||
|
||
**Per-user settings:**
|
||
- `bu_teams` — comma-separated BU team assignments for multi-tenant scoping. Determines default BU filter on the Reporting page.
|
||
- `ivanti_first_name` / `ivanti_last_name` — per-user Ivanti identity for filtering FP workflow views to their own submissions.
|
||
|
||
**Migration from legacy roles:** The `add_user_groups.js` migration automatically maps existing users: `admin` → `Admin`, `editor` → `Standard_User`, `viewer` → `Read_Only`. Unrecognized or NULL roles default to `Read_Only`.
|
||
|
||
---
|
||
|
||
### 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 (Admin/Standard_User)**
|
||
- 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 (ownership restrictions apply for Standard_User)
|
||
- 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 (Admin/Standard_User): 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, with CARD ownership integration and multi-BU scoping.
|
||
|
||
#### Syncing Data
|
||
|
||
Click **Sync** (top right) to pull the latest findings from Ivanti. Sync requires Admin or Standard_User group. 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 findings as individual rows in the PostgreSQL `ivanti_findings` table
|
||
5. Detects archived findings (present in previous sync, absent in current) and logs transitions
|
||
6. Records per-BU counts in `ivanti_counts_history_by_bu` for trend analysis
|
||
7. Logs sync anomalies when significant count deltas are detected
|
||
|
||
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.
|
||
|
||
#### Group by Host
|
||
|
||
The **Group by Host** toggle in the toolbar collapses duplicate assets (same hostname + IP) with multiple finding IDs into expandable host rows. Hosts with only one finding remain as flat rows. Toggle between grouped and flat views. This reduces visual clutter when a single host has dozens of findings.
|
||
|
||
#### CARD Ownership Tooltip
|
||
|
||
Hover over any IP address in the findings table to see CARD asset ownership data in an interactive tooltip:
|
||
- Confirmed, unconfirmed, and candidate team assignments with confidence scores
|
||
- Click "Actions" to open the CARD Action Modal for direct confirm/decline/redirect operations
|
||
- Results cached per session — re-hover displays instantly without API calls
|
||
- Quick mode uses CTEC suffix only with 15s timeout to avoid multi-minute waits
|
||
- Timeouts (504) are not cached — re-hover will retry the lookup
|
||
- When Ivanti Host ID is available, uses the faster asset-search path
|
||
|
||
#### Multi-BU Scope
|
||
|
||
The multi-select BU picker at the top of the page replaces the previous binary scope toggle. Select one or more BUs to filter findings:
|
||
- NTS-AEO-ACCESS-ENG
|
||
- NTS-AEO-STEAM
|
||
- NTS-AEO-ACCESS-OPS
|
||
- NTS-AEO-INTELDEV
|
||
|
||
Per-user BU team assignments (`bu_teams` on the user record) determine the default scope.
|
||
|
||
#### 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. Requires Admin or Standard_User group.
|
||
|
||
**CVE Tooltips:** Hover over any CVE badge in the table to see a tooltip with the CVE description and severity (if the CVE exists in the local database). Tooltips appear after a 300ms delay, are cached in memory for the session, and auto-position to stay within the viewport.
|
||
|
||
**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`.
|
||
|
||
**Row visibility:** Hide individual rows by clicking the `EyeOff` icon on any row, or select multiple rows via checkboxes and click **Hide Selected** in the bulk action toolbar. Hidden rows are excluded from the table, the Action Coverage chart, and exports. Use the **Hidden (N)** button in the toolbar to view and restore hidden rows individually or all at once. Hidden row state persists to `localStorage` across sessions. Row hiding is a personal view preference available to all user groups.
|
||
|
||
**Export:** Click **Export** to download the current filtered view as CSV or XLSX. Hidden rows and filtered rows are both excluded from exports. Requires Admin, Standard_User, or Leadership group.
|
||
|
||
---
|
||
|
||
### Ivanti Queue
|
||
|
||
A personal staging list for batch-processing FP, Archer, CARD, GRANITE, DECOM, and Remediate workflows without context-switching into Ivanti mid-review. Requires Admin or Standard_User group.
|
||
|
||
**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** and **GRANITE** items: no vendor entry required — the IP address is captured automatically
|
||
- For **DECOM** items: the finding is flagged for decommission workflow
|
||
- For **Remediate** items: the finding is flagged for remediation tracking
|
||
- Select the workflow type: **FP**, **Archer**, **CARD**, **GRANITE**, **DECOM**, or **Remediate**
|
||
- 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:
|
||
- Collapsible sections per workflow type
|
||
- **CARD** items appear with the IP address displayed
|
||
- **GRANITE** items show IP and hostname for Granite Loader Sheet generation
|
||
- **FP and Archer** items are grouped alphabetically by vendor
|
||
- **DECOM** items auto-note and auto-hide the finding on completion
|
||
- Badges show workflow type colour-coded by category
|
||
|
||
**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 (FK-safe deletion)
|
||
- **Create FP Workflow** — select pending FP items and click to open the FP Workflow modal, which submits a False Positive workflow batch directly to the Ivanti API with form fields, file attachments, and scope override. Attachments can be local file uploads or documents selected from the CVE document library — library documents are read from disk and sent to Ivanti identically to local uploads. Successful submission marks the queue items as complete and records the submission locally.
|
||
- **Create Jira Ticket** — select multiple items and use the consolidation modal to create a single Jira ticket covering all selected findings. Ticket links are displayed on completed items.
|
||
- **Loader Sheet** — select GRANITE items to generate a Granite Loader Sheet with CARD enrichment
|
||
- **Archer Template** — select Archer items to open the template selector for pre-filling Archer Risk Acceptance forms
|
||
|
||
**Redirecting items:**
|
||
- Pending items can be redirected to a different workflow type without duplication
|
||
- Completed items show a redirect button (↱) next to the delete icon
|
||
- Click redirect to open a modal where you select the target workflow type and vendor (required for FP/Archer)
|
||
- Redirecting creates a new pending queue item with the same finding data under the new workflow type — the original item is preserved
|
||
- This is useful when a CARD inventory fix is done but the finding still needs an FP or Archer workflow, or when an item was assigned to the wrong workflow initially
|
||
|
||
Queue items are stored in the database, are **personal to your login**, and persist across sessions and page refreshes.
|
||
|
||
---
|
||
|
||
### FP Workflow Submission
|
||
|
||
Submit False Positive workflows directly to the Ivanti API with attachments and full lifecycle tracking. Accessible from the Ivanti Queue when FP items are selected.
|
||
|
||
**Submission workflow:**
|
||
1. Select pending FP items in the queue
|
||
2. Fill in workflow name, reason, description, expiration date, and scope override
|
||
3. Attach supporting documents (local uploads or library documents, 10MB limit per file)
|
||
4. Submit — the workflow batch is created in Ivanti via API and recorded locally
|
||
|
||
**Lifecycle tracking:** Each submission tracks its status through the lifecycle:
|
||
- `submitted` — initial state after successful API submission
|
||
- `approved` — FP workflow approved by Ivanti team
|
||
- `rejected` — FP workflow rejected, requires rework
|
||
- `rework` — submission is being edited for resubmission
|
||
- `resubmitted` — edited submission resubmitted to Ivanti
|
||
|
||
**Managing submissions:**
|
||
- View submission history with attachment results and lifecycle status
|
||
- Edit and resubmit rejected workflows (fields, findings, and attachments)
|
||
- Dismiss rejected submissions (sets `dismissed_at` timestamp)
|
||
- Re-queue findings from rejected submissions into the todo queue under a different workflow type
|
||
- Auto-clear approved submissions from the active list
|
||
- Collapsible submissions panel with per-user filtering
|
||
- Sync lifecycle status from Ivanti `currentState` on fetch
|
||
|
||
---
|
||
|
||
### 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
|
||
|
||
Admin and Standard_User groups 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 backend extracts the xlsx schema and runs a **drift check** against the parser configuration (`compliance_config.json`). If structural drift is detected, a drift review phase is shown before the diff preview:
|
||
- **Breaking** findings (red) — missing core columns or detail sheets — block the upload until the config is updated
|
||
- **Silent-miss** findings (amber) — unknown metrics or sheets that will be miscategorised — warn but allow proceeding
|
||
- **Cosmetic** findings (muted) — new columns or stale config entries — informational only
|
||
- Admins can click **Reconcile Config** to auto-patch the parser configuration and re-run the check
|
||
3. If no breaking drift exists, the **diff preview** is shown — new violations, resolved items, and recurring items since the last upload
|
||
4. Click **Confirm Upload** to commit. The upload is recorded and the device table updates immediately.
|
||
|
||
The report date is extracted automatically from the filename.
|
||
|
||
**Upload rollback:** Admins can roll back the most recent upload via `POST /api/compliance/rollback/:uploadId`. Rolling back deletes new items introduced by that upload, re-activates items it resolved, and decrements seen counts on recurring items.
|
||
|
||
#### 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 can be deleted by the author or an Admin — deleting a multi-metric note removes it from all linked metrics. Requires Admin or Standard_User group.
|
||
|
||
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.
|
||
|
||
---
|
||
|
||
### CCP Metrics — Multi-Vertical Compliance
|
||
|
||
The CCP Metrics page provides executive-level visibility into VCL (Vulnerability Compliance Level) compliance posture across multiple verticals (organisational units). It supports multi-file upload, forecast burndown charts, sub-team drill-down, and data management.
|
||
|
||
**Upload workflow:**
|
||
1. Click **Upload** and select 1–14 xlsx files (naming convention: `<VERTICAL>_YYYY_MM_DD.xlsx`)
|
||
2. Each file is parsed, the vertical is extracted from the filename, and a scoped diff is computed against existing data for that vertical
|
||
3. The preview shows per-file new/recurring/resolved counts and unrecognised files
|
||
4. Click **Commit** to persist all files in a single transaction with vertical-scoped resolution
|
||
|
||
**Executive overview:**
|
||
- Aggregated stats across all verticals: total devices, compliant, non-compliant, compliance percentage vs target
|
||
- Donut chart: blocked (no resolution date) vs in-progress (has resolution date)
|
||
- Per-vertical breakdown cards with compliance percentage, burndown forecast, and last upload date
|
||
- Cross-vertical metric breakdown table sorted by non-compliant count
|
||
- LIVE and LAST REPORT badges showing data freshness
|
||
|
||
**Metric drill-down:**
|
||
- Click a metric ID to see per-vertical breakdown with sub-team data
|
||
- Per-metric summary statistics and donut breakdown
|
||
- Per-metric monthly compliance trend with linear regression forecast (3 months forward when 3+ months of history)
|
||
- Device list per vertical/metric with optional team filtering
|
||
- Compliant/total counts on metric summary cards
|
||
- Non-Compliant stat clickable with metric breakdown buttons
|
||
|
||
**Burndown forecasts:**
|
||
- Per-vertical burndown: deduplicates devices by hostname, projects monthly resolution based on `resolution_date` fields
|
||
- Aggregated burndown: cross-vertical forecast with projected clear date
|
||
- Per-metric forecast burndown chart
|
||
|
||
**Sub-team drill-down:**
|
||
- Each metric shows sub-team rows (e.g., STEAM, ACCESS-OPS, ACCESS-ENG) beneath the rollup row
|
||
- Intermediate view between overview and device list
|
||
- Team filtering on the device list endpoint
|
||
|
||
**Data management (Admin only):**
|
||
- Delete a specific vertical (all items, uploads, summary, snapshots)
|
||
- Rollback a specific upload (must be the most recent for that vertical)
|
||
- Reset all multi-vertical data (nuclear option)
|
||
|
||
**VCL metric calculations:** See `docs/guides/vcl-metric-calculations.md` for formula reference.
|
||
|
||
---
|
||
|
||
### CARD Asset Ownership
|
||
|
||
CARD API integration for managing asset ownership — confirm, decline, and redirect ownership for network assets directly from the dashboard.
|
||
|
||
**Owner lookup:**
|
||
- Hover over any IP address in the findings table to see CARD ownership data in a tooltip (confirmed/unconfirmed/candidate teams)
|
||
- Tooltip uses quick mode (CTEC suffix only, 15s timeout) for performance
|
||
- Results cached per session for instant re-display — timeouts (504) are not cached and will retry on re-hover
|
||
- When Ivanti Host ID is available, uses the CARD asset-search endpoint for faster resolution
|
||
|
||
**Direct actions (no queue item required):**
|
||
- Click "Actions" in the CARD tooltip to open the CARD Action Modal
|
||
- Modal displays full owner context: confirmed, unconfirmed, declined, and candidate teams
|
||
- Confirm, decline, or redirect ownership via the CARD API
|
||
- Bare IPs are auto-resolved to CARD asset IDs (via host_id fast path or suffix guessing: CTEC, NATL, CHTR, COML, RESI, WIFI, VOIP)
|
||
- IP address validation before mutation operations
|
||
|
||
**Queue-based actions:**
|
||
- Add findings to the queue with workflow type CARD
|
||
- Confirm, decline, or redirect from the queue panel
|
||
- Queue items are marked complete on successful CARD action
|
||
- update_token handling for safe concurrent operations
|
||
|
||
**Team assets endpoint:**
|
||
- Paginated team asset lookup by disposition (confirmed, unconfirmed, candidate)
|
||
- Used by the Granite enrichment batch endpoint for full data
|
||
|
||
---
|
||
|
||
### Granite Loader Sheet
|
||
|
||
Generate Granite Loader Sheets with CARD enrichment for network device workflows.
|
||
|
||
**Generation workflow:**
|
||
1. Add findings to the queue with workflow type GRANITE
|
||
2. Click **Loader Sheet** in the queue panel (or use the Loader Sheet button on the Reporting page)
|
||
3. The modal fetches CARD data for each IP/host_id to enrich with NCIM, Qualys, and Netops Granite fields
|
||
4. Review and edit per-row data with searchable picklists
|
||
5. Export as formatted XLSX
|
||
|
||
**CARD enrichment fields:**
|
||
- `equip_inst_id` — NCIM equipment instance ID
|
||
- `hostname` — resolved hostname from CARD
|
||
- `site_name` — NCIM site name
|
||
- `mgmt_ip_asn` — management IP ASN
|
||
- `responsible_team` — NCIM responsible team
|
||
- `equipment_class` — equipment classification
|
||
- `equip_template` — equipment template
|
||
- `equip_status` — equipment status
|
||
- `serial_number` — device serial number
|
||
|
||
**Features:**
|
||
- Searchable picklists for teams, statuses, operation types (defined in `graniteLoaderPicklists.js`)
|
||
- Column groups with configurable visibility (defined in `graniteLoaderConfig.js`)
|
||
- Per-row inline editing before export
|
||
- Batch enrichment: accepts up to 200 IPs or host_ids per request
|
||
- When no team is specified, searches both NTS-AEO-STEAM and NTS-AEO-ACCESS-ENG
|
||
|
||
---
|
||
|
||
### Atlas Action Plans
|
||
|
||
Atlas InfoSec action plan tracking with per-host vulnerability mapping. Provides visibility into which hosts have active remediation, risk acceptance, or compensating control plans.
|
||
|
||
**AtlasBadge component:**
|
||
- Appears on finding rows in the Reporting table when a host has one or more action plans
|
||
- Badge colour indicates plan type: remediation, risk acceptance, or compensating control
|
||
- Reads from local cache (`atlas_action_plans_cache` table) for instant rendering without API round-trips
|
||
|
||
**Slide-out panel:**
|
||
- Click the AtlasBadge to open the Atlas detail panel
|
||
- Shows all plans for the host with type, status, and metadata
|
||
- Qualys vulnerability mapping per host (resolved via `/hosts/vulnerabilities` endpoint)
|
||
|
||
**Cache management:**
|
||
- Local cache stores plan existence, count, and full plan JSON per host_id
|
||
- Manual refresh triggers a re-fetch from the Atlas API and updates the cache
|
||
- `atlas_known` flag indicates whether the host has been checked (avoids re-querying hosts with no plans)
|
||
|
||
**Plan operations (Admin/Standard_User):**
|
||
- Create action plans: remediation, risk_acceptance, or compensating_control
|
||
- Update existing plans (PATCH)
|
||
- Refresh cache per host
|
||
|
||
---
|
||
|
||
### Finding Archive Tracking
|
||
|
||
Automatic detection of findings that disappear between Ivanti syncs, with lifecycle state tracking and anomaly logging.
|
||
|
||
**How it works:**
|
||
- On each sync, findings present in the previous sync but absent from the current sync are classified as archived
|
||
- If a previously archived finding reappears, it transitions to RETURNED
|
||
- Findings that remain absent are eventually classified as CLOSED or CLOSED_GONE
|
||
|
||
**Lifecycle states:**
|
||
| State | Meaning |
|
||
|-------|---------|
|
||
| ARCHIVED | Finding disappeared from Ivanti (first detection) |
|
||
| RETURNED | Previously archived finding reappeared in a subsequent sync |
|
||
| CLOSED | Finding confirmed closed by Ivanti (workflow completed) |
|
||
| CLOSED_GONE | Finding disappeared and is confirmed gone (no workflow, long absence) |
|
||
|
||
**Anomaly detection:**
|
||
- Each sync logs open/closed count deltas, newly archived count, and returned count
|
||
- Significance threshold triggers the AnomalyBanner component on the Reporting page
|
||
- Classification JSON tracks the breakdown of archive reasons
|
||
|
||
**BU reassignment tracking:**
|
||
- Findings that change BU ownership between syncs are logged in `ivanti_finding_bu_history`
|
||
- `IVANTI_MANAGED_BUS` env var defines which BUs are "managed" — findings leaving these BUs are classified as `bu_reassignment`
|
||
- Return classification distinguishes between original finding restoration and new duplicates
|
||
|
||
**UI components:**
|
||
- **AnomalyBanner** — alert bar on the Reporting page when significant sync anomalies are detected
|
||
- **ArchiveSummaryBar** — state distribution summary (ARCHIVED / RETURNED / CLOSED counts)
|
||
- Archive view with transition history per finding
|
||
|
||
---
|
||
|
||
### 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 (Admin/Standard_User)
|
||
- View documents inline in the browser (PDFs render in a sandboxed iframe; Markdown files render as sanitized HTML)
|
||
- Download any document
|
||
- Filter and browse by category
|
||
- Admin can delete any article; Standard_User can delete articles they created
|
||
- 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. Available to Admin, Standard_User, and Leadership groups. Read_Only users cannot access the Exports page.
|
||
|
||
---
|
||
|
||
### Jira Tickets
|
||
|
||
A dedicated page for managing Jira Data Center tickets linked to CVE/vendor pairs and Ivanti queue items. Accessible from the navigation drawer. Requires a configured Jira API connection (see [Configuration](#configuration)).
|
||
|
||
**Ticket list**
|
||
- View all tracked Jira tickets with status, CVE ID, vendor, summary, and Jira key
|
||
- Filter by status or search by keyword
|
||
- Click a Jira key to open the issue in Jira Data Center
|
||
- Raw Jira status display — shows the actual Jira status field (no Open/In Progress/Closed mapping)
|
||
|
||
**Jira API operations (Admin/Standard_User)**
|
||
- **Lookup** — search for any Jira issue by key and view its current status, assignee, and summary
|
||
- **Create in Jira** — create a new Jira issue directly from the dashboard with project key, issue type, summary, and description; the resulting ticket is automatically linked to a CVE/vendor pair in the local database. CVE/Vendor fields are optional — tickets can be created with source context tracking only.
|
||
- **Multi-item creation** — from the Ivanti Queue consolidation modal, create a single Jira ticket covering multiple findings
|
||
- **Save to Dashboard** — save a Jira issue found via lookup to the local database
|
||
- **Sync** — refresh a single ticket's status and summary from Jira, or bulk-sync all tracked tickets via JQL search
|
||
- **Create / Edit / Delete** — manage local ticket records linking Jira keys to CVE/vendor pairs
|
||
|
||
**Vendor-specific issue types:** The Issue Type dropdown in the creation modal is context-aware. When the Project Key field matches a recognized vendor project key (e.g., `AA_VECIMA`, `AA_CISCO`, `AA_ADTRAN`), the dropdown switches to vendor-specific issue types (Epic, Story, Task, Defect, Production Defect/Incident Fix, New Feature, Spike, Release Candidate, Documentation). For all other project keys — including the default from `JIRA_PROJECT_KEY` — the dropdown shows STEAM issue types (Story, Epic, Program, Project, Reservation, Automation Maintenance). Matching is case-insensitive and trims whitespace. Changing the project key such that the context switches (STEAM to vendor or vice versa) resets the selected issue type. The same behavior applies when creating a Jira ticket from the Ivanti Queue. The list of recognized vendor project keys is defined in `VENDOR_PROJECT_KEYS` in `frontend/src/components/pages/JiraPage.js`.
|
||
|
||
**Connection test (Admin)** — verify Jira API credentials and connectivity from the page header.
|
||
|
||
**Rate limit monitoring (Admin)** — view current burst and daily rate limit usage against Charter's posted limits (60/minute burst, 1 440/day).
|
||
|
||
All Jira API calls are proxied through the backend. Credentials are never exposed to the browser. Rate limits are enforced client-side with inter-request delays (1s for GETs, 2s for writes). See `docs/api/jira-api-use-cases.md` for the full API compliance summary.
|
||
|
||
---
|
||
|
||
### 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
|
||
- Admin/Standard_User can create, edit, and delete tickets (Standard_User delete subject to ownership and compliance linkage checks)
|
||
|
||
---
|
||
|
||
### Archer Template Library
|
||
|
||
Template management system for Archer Risk Acceptance forms. Stores static content (Environment Overview, Segmentation, Mitigating Controls, and 5 additional sections) organised by Vendor, Platform, and Model hierarchy.
|
||
|
||
**Capabilities:**
|
||
- Full CRUD — create, view, update, and delete templates
|
||
- Clone existing templates into new vendor/platform/model combinations
|
||
- Search/filter by vendor, platform, or model (case-insensitive)
|
||
- Hierarchy browsing endpoints: list vendors, platforms per vendor, models per vendor+platform
|
||
- Per-section copy-to-clipboard buttons in the inline view panel
|
||
- Template selector integrated into the Ivanti Queue for Archer workflow items
|
||
- Accessible from nav drawer (Template Mgr) and from Archer queue items
|
||
|
||
**Template sections (8 content fields, each max 10,000 chars):**
|
||
- Environment Overview
|
||
- Segmentation
|
||
- Mitigating Controls
|
||
- Additional Info
|
||
- Charter Network Banner
|
||
- Data Classification
|
||
- Charter Network
|
||
- Additional Access List
|
||
|
||
**Hierarchy:** Vendor > Platform > Model (unique constraint on the combination). Templates are sorted alphabetically by vendor, platform, model.
|
||
|
||
---
|
||
|
||
### In-App Notifications
|
||
|
||
Native notification system providing per-user alerts for system events without external dependencies (replaces previous Webex bot integration).
|
||
|
||
**Notification types:**
|
||
- `issue_resolved` — GitLab feedback issue closed and deployed
|
||
|
||
**UI:**
|
||
- NotificationBell component in the header with unread count badge
|
||
- Click to view list of unread notifications (newest first, limited to 50)
|
||
- Mark individual notifications as read, or mark all as read
|
||
- Only the owning user can mark their own notifications
|
||
|
||
**Storage:** Notifications are stored in the `notifications` table with `user_id`, `username`, `type`, `title`, `message`, `issue_number`, and `read` flag.
|
||
|
||
---
|
||
|
||
### Feedback — GitLab Integration
|
||
|
||
In-app bug reports and feature requests submitted directly to the GitLab project as issues. Keeps the GitLab PAT server-side so credentials are never exposed to the browser.
|
||
|
||
**Submission workflow:**
|
||
1. Click the feedback button in the nav drawer
|
||
2. Select type: Bug Report or Feature Request
|
||
3. Fill in title and description
|
||
4. Optionally attach up to 3 screenshots (PNG, JPG, GIF, WebP — 5MB each)
|
||
5. Submit — creates a GitLab issue with labels (`bug` or `enhancement`) and formatted description
|
||
|
||
**Issue lifecycle:**
|
||
- GitLab webhook receiver (`POST /api/webhooks/gitlab`) listens for issue close events
|
||
- When a feedback issue is closed, the submitter username is parsed from the issue description
|
||
- An in-app notification is created for the submitter: "Your bug report has been resolved and deployed"
|
||
- Webhook secret validation prevents unauthorized requests
|
||
|
||
**Configuration:**
|
||
- `GITLAB_URL` — GitLab instance URL
|
||
- `GITLAB_PROJECT_ID` — numeric project ID
|
||
- `GITLAB_PAT` — project access token with `api` scope
|
||
- `GITLAB_WEBHOOK_SECRET` — shared secret for webhook validation (set same value in GitLab webhook settings)
|
||
|
||
---
|
||
|
||
### Admin Panel
|
||
|
||
The Admin Panel is a full-page, tabbed interface accessible only to Admin-group users. It replaces the previous inline modal rendering and follows the dashboard's dark tactical intelligence theme. Three tabs provide consolidated access to administrative functions:
|
||
|
||
**User Management** — the default tab. Displays a themed user table with group badges (Admin in red, Standard_User in accent blue, Leadership in amber, Read_Only in muted grey). Admins can create, edit, and delete users, change group assignments, and toggle active status — all through inline forms styled to match the dashboard. Admins cannot demote themselves or deactivate their own account. Deactivating a user immediately invalidates all their active sessions. All group changes are audit-logged with previous and new group values.
|
||
|
||
**Audit Log** — a paginated, filterable log table showing every state-changing action with timestamp, username, action type, entity type, entity ID, details, and IP address. Action types are colour-coded: login in green, delete in red, create in accent blue, update in amber. Filter by username, action type, entity type, and date range. Results are paginated at 25 per page.
|
||
|
||
**System Info** — stat cards showing total user count, active user count, total audit log entries, and users who logged in within the last 7 days. A "Recent Activity" section lists the 10 most recent audit log entries.
|
||
|
||
The `UserMenu` quick-access links ("Manage Users", "Audit Log") continue to open the existing modal components for fast access without navigating to the admin 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 metric categories, core columns, and skip sheets from `compliance_config.json` (shared with the drift checker).
|
||
|
||
- Reads all detail sheets; skips sheets listed in `skip_sheets`
|
||
- 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/extract_xlsx_schema.py`
|
||
|
||
Called by the preview endpoint before parsing. Extracts the structural schema of an xlsx file as JSON — sheet names, first-row column headers per sheet, and unique metric values from the Summary sheet. The Node.js drift checker compares this schema against `compliance_config.json` to detect breaking, silent-miss, and cosmetic drift.
|
||
|
||
**Dependencies:** `openpyxl>=3.0.0`
|
||
|
||
### `backend/scripts/compliance_config.json`
|
||
|
||
Shared parser configuration file — the single source of truth for `metric_categories` (metric ID → category mapping), `core_cols` (columns that become main item fields), and `skip_sheets` (sheets excluded from parsing). Read by both `parse_compliance_xlsx.py` and the Node.js `driftChecker.js` module. Admins can auto-patch this file via the **Reconcile Config** button in the upload modal.
|
||
|
||
---
|
||
|
||
## API Reference
|
||
|
||
All endpoints are prefixed with `/api`. All endpoints except `/api/auth/login` and `/api/auth/logout` require a valid session cookie. Group requirements are listed per endpoint.
|
||
|
||
### Auth
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/auth/login` | Public | Log in, receive session cookie (rate-limited: 20/15min) |
|
||
| POST | `/api/auth/logout` | Public | Invalidate session |
|
||
| GET | `/api/auth/me` | Any | Get current user info (returns `group` field) |
|
||
| POST | `/api/auth/cleanup-sessions` | Admin | Delete expired sessions |
|
||
|
||
### CVEs
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/cves` | Any | List CVEs; query params: `search`, `vendor`, `severity`, `status` |
|
||
| POST | `/api/cves` | Admin, Standard_User | Create a new CVE entry |
|
||
| PUT | `/api/cves/:id` | Admin, Standard_User | Update a CVE entry by row ID |
|
||
| PATCH | `/api/cves/:cveId/status` | Admin, Standard_User | Update status for all vendor rows matching a CVE ID |
|
||
| DELETE | `/api/cves/:id` | Admin, Standard_User | Delete a single CVE vendor entry (ownership + cascade check for Standard_User) |
|
||
| DELETE | `/api/cves/by-cve-id/:cveId` | Admin, Standard_User | Delete all vendor entries for a CVE ID (ownership + cascade check for Standard_User) |
|
||
| GET | `/api/cves/check/:cveId` | Any | Quick check: existence and status of a CVE |
|
||
| GET | `/api/cves/distinct-ids` | Any | All distinct CVE IDs (used by NVD sync) |
|
||
| GET | `/api/cves/:cveId/vendors` | Any | All vendor entries for a specific CVE ID |
|
||
| GET | `/api/cves/compliance` | Any | Document compliance status view |
|
||
| GET | `/api/cves/:cveId/tooltip` | Any | Get CVE description and severity for tooltip display (truncated to 300 chars) |
|
||
|
||
### Documents
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/cves/:cveId/documents` | Any | List documents for a CVE; optional `?vendor=` filter |
|
||
| POST | `/api/cves/:cveId/documents` | Admin, Standard_User | Upload a document for a CVE/vendor pair |
|
||
| DELETE | `/api/documents/:id` | Admin | Delete a document and its file from disk |
|
||
|
||
### NVD
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/nvd/lookup/:cveId` | Any | Look up a single CVE in the NVD 2.0 API |
|
||
| POST | `/api/cves/nvd-sync` | Admin, Standard_User | Bulk update CVE metadata from NVD |
|
||
|
||
### JIRA Tickets
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/jira-tickets` | Any | List tickets; optional filters: `cve_id`, `vendor`, `status` |
|
||
| POST | `/api/jira-tickets` | Admin, Standard_User | Create a JIRA ticket |
|
||
| PUT | `/api/jira-tickets/:id` | Admin, Standard_User | Update a JIRA ticket |
|
||
| DELETE | `/api/jira-tickets/:id` | Admin, Standard_User | Delete a JIRA ticket (ownership + compliance check for Standard_User) |
|
||
| GET | `/api/jira-tickets/connection-test` | Admin | Test Jira API connectivity and credentials |
|
||
| GET | `/api/jira-tickets/rate-limit` | Admin | Get current burst and daily rate limit usage |
|
||
| GET | `/api/jira-tickets/lookup/:issueKey` | Any | Look up a single Jira issue by key |
|
||
| POST | `/api/jira-tickets/search` | Any | JQL search for Jira issues |
|
||
| POST | `/api/jira-tickets/create-in-jira` | Admin, Standard_User | Create an issue in Jira and link it locally |
|
||
| POST | `/api/jira-tickets/sync-all` | Admin | Bulk-sync all tracked tickets via JQL |
|
||
| POST | `/api/jira-tickets/:id/sync` | Admin, Standard_User | Sync a single ticket's status from Jira |
|
||
|
||
### Ivanti — Host Findings
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/findings` | Any | Get cached findings with notes and overrides merged in |
|
||
| POST | `/api/ivanti/findings/sync` | Admin, Standard_User | Trigger an immediate findings sync from Ivanti |
|
||
| GET | `/api/ivanti/findings/counts` | Any | Open vs closed finding totals |
|
||
| GET | `/api/ivanti/findings/fp-workflow-counts` | Any | FP workflow state breakdown |
|
||
| PUT | `/api/ivanti/findings/:findingId/override` | Admin, Standard_User | Override `hostName` or `dns`; empty value clears the override |
|
||
| PUT | `/api/ivanti/findings/:findingId/note` | Admin, Standard_User | Save or update a finding note (max 255 chars) |
|
||
|
||
### Ivanti — Workflows
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/workflows` | Any | Get cached workflow data |
|
||
| POST | `/api/ivanti/workflows/sync` | Admin, Standard_User | Trigger an immediate workflow sync |
|
||
|
||
### Ivanti — FP Workflow Submission
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/fp-workflow/documents/search` | Any | Search the CVE document library by name, CVE ID, or vendor; returns up to 50 matches |
|
||
| POST | `/api/ivanti/fp-workflow` | Admin, Standard_User | Submit an FP workflow batch to Ivanti API (multipart/form-data with local attachments and/or `libraryDocIds`) |
|
||
| GET | `/api/ivanti/fp-workflow/submissions` | Any | List FP submissions for the current user |
|
||
| PUT | `/api/ivanti/fp-workflow/submissions/:id` | Admin, Standard_User | Update an FP submission (edit form fields) |
|
||
| POST | `/api/ivanti/fp-workflow/submissions/:id/findings` | Admin, Standard_User | Add or remove findings on an existing submission |
|
||
| POST | `/api/ivanti/fp-workflow/submissions/:id/attachments` | Admin, Standard_User | Upload additional attachments (local files and/or `libraryDocIds`) to an existing submission |
|
||
| PATCH | `/api/ivanti/fp-workflow/submissions/:id/status` | Admin, Standard_User | Update submission lifecycle status |
|
||
| PATCH | `/api/ivanti/fp-workflow/submissions/:id/dismiss` | Admin, Standard_User | Dismiss a rejected submission (sets `dismissed_at` timestamp) |
|
||
| POST | `/api/ivanti/fp-workflow/submissions/:id/requeue` | Admin, Standard_User | Re-queue findings from a rejected submission into the todo queue under a new workflow type |
|
||
|
||
### Ivanti — Todo Queue
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/todo-queue` | Any | Get all queue items for the current user |
|
||
| POST | `/api/ivanti/todo-queue` | Admin, Standard_User | Add a finding to the queue |
|
||
| POST | `/api/ivanti/todo-queue/batch` | Admin, Standard_User | Batch-add multiple findings to the queue |
|
||
| PUT | `/api/ivanti/todo-queue/:id` | Admin, Standard_User | Update a queue item (mark complete, edit vendor/type) |
|
||
| POST | `/api/ivanti/todo-queue/:id/redirect` | Admin, Standard_User | Redirect a completed item to a different workflow type |
|
||
| DELETE | `/api/ivanti/todo-queue/:id` | Admin, Standard_User | Delete a single queue item |
|
||
| DELETE | `/api/ivanti/todo-queue/completed` | Admin, Standard_User | Delete all completed queue items |
|
||
|
||
### Ivanti — Archive
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/ivanti/archive` | Any | List archive records; optional filters: `state` (ACTIVE/ARCHIVED/RETURNED/CLOSED), `teams` (comma-separated BU names) |
|
||
| GET | `/api/ivanti/archive/anomalies` | Any | Sync anomaly log (significant count deltas and classification data) |
|
||
| GET | `/api/ivanti/archive/transitions/:archiveId` | Any | Transition history for a specific archive record |
|
||
|
||
### Compliance
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/compliance/preview` | Admin, Standard_User | Parse an xlsx upload, run drift check, and return drift report + diff + temp file path |
|
||
| POST | `/api/compliance/commit` | Admin, Standard_User | Commit a previewed upload to the database |
|
||
| POST | `/api/compliance/reconcile-config` | Admin | Auto-patch `compliance_config.json` to resolve breaking and silent-miss drift findings |
|
||
| POST | `/api/compliance/rollback/:uploadId` | Admin | Roll back the most recent upload (deletes new items, re-activates resolved items) |
|
||
| GET | `/api/compliance/uploads` | Any | List all compliance upload records |
|
||
| GET | `/api/compliance/summary` | Any | Metric health summary; `?team=STEAM` |
|
||
| GET | `/api/compliance/items` | Any | Device list; `?team=STEAM&status=active` |
|
||
| GET | `/api/compliance/items/:hostname` | Any | Full detail for a device (metrics + notes) |
|
||
| GET | `/api/compliance/notes/:hostname/:metricId` | Any | Notes for a specific hostname/metric |
|
||
| POST | `/api/compliance/notes` | Admin, Standard_User | Add a note for a hostname/metric; accepts `metric_ids` array for multi-metric notes |
|
||
| DELETE | `/api/compliance/notes/:id` | Admin, Standard_User | Delete a note by ID; `?group=true` deletes all notes sharing the same `group_id`. Author or Admin only. |
|
||
|
||
### Knowledge Base
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/knowledge-base/upload` | Admin, Standard_User | Upload a new knowledge base document |
|
||
| GET | `/api/knowledge-base` | Any | List all articles |
|
||
| GET | `/api/knowledge-base/:id` | Any | Get article metadata |
|
||
| GET | `/api/knowledge-base/:id/content` | Any | Get file content for inline display |
|
||
| GET | `/api/knowledge-base/:id/download` | Any | Download the file |
|
||
| DELETE | `/api/knowledge-base/:id` | Admin, Standard_User | Delete article and file (Standard_User: own articles only) |
|
||
|
||
### Archer Tickets
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/archer-tickets` | Any | List tickets; optional filters: `cve_id`, `vendor`, `status` |
|
||
| GET | `/api/archer-tickets/status-trend` | Any | Ticket counts by date and status for pipeline chart |
|
||
| POST | `/api/archer-tickets` | Admin, Standard_User | Create a new Archer ticket |
|
||
| PUT | `/api/archer-tickets/:id` | Admin, Standard_User | Update an Archer ticket |
|
||
| DELETE | `/api/archer-tickets/:id` | Admin, Standard_User | Delete an Archer ticket (ownership + compliance check for Standard_User) |
|
||
|
||
### Archer Templates
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/archer-templates` | Any | List templates; optional filters: `search`, `vendor`, `platform`, `model` |
|
||
| GET | `/api/archer-templates/:id` | Any | Get a single template by ID |
|
||
| POST | `/api/archer-templates` | Admin, Standard_User | Create a new template |
|
||
| PUT | `/api/archer-templates/:id` | Admin, Standard_User | Update a template (partial update supported) |
|
||
| DELETE | `/api/archer-templates/:id` | Admin, Standard_User | Delete a template |
|
||
| POST | `/api/archer-templates/:id/clone` | Admin, Standard_User | Clone a template with new vendor/platform/model |
|
||
| GET | `/api/archer-templates/hierarchy/vendors` | Any | List distinct vendor names |
|
||
| GET | `/api/archer-templates/hierarchy/platforms` | Any | List platforms for a vendor; query: `vendor` |
|
||
| GET | `/api/archer-templates/hierarchy/models` | Any | List models for vendor+platform; query: `vendor`, `platform` |
|
||
|
||
### CARD Asset Ownership
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/card/status` | Any | Check if CARD API is configured |
|
||
| GET | `/api/card/teams` | Admin, Standard_User | List all CARD teams |
|
||
| GET | `/api/card/teams/:teamName/assets` | Admin, Standard_User | Paginated team assets by disposition |
|
||
| GET | `/api/card/owner/:assetId` | Admin, Standard_User | Get owner record for an asset |
|
||
| GET | `/api/card/owner-lookup/:ip` | Admin, Standard_User | Resolve IP to asset and return owner data; `?quick=1` for tooltip mode, `?hostId=N` for fast path |
|
||
| POST | `/api/card/owner/:assetId/confirm` | Admin, Standard_User | Direct confirm ownership (no queue item) |
|
||
| POST | `/api/card/owner/:assetId/decline` | Admin, Standard_User | Direct decline ownership (no queue item) |
|
||
| POST | `/api/card/owner/:assetId/redirect` | Admin, Standard_User | Direct redirect between teams (no queue item) |
|
||
| GET | `/api/card/asset-search/:hostId` | Admin, Standard_User | Search CARD by Ivanti Host ID (deep_search) |
|
||
| POST | `/api/card/enrich-batch` | Admin, Standard_User | Batch lookup IPs/host_ids for Granite loader fields (max 200) |
|
||
| POST | `/api/card/queue/:queueItemId/confirm` | Admin, Standard_User | Confirm ownership for a queue item |
|
||
| POST | `/api/card/queue/:queueItemId/decline` | Admin, Standard_User | Decline ownership for a queue item |
|
||
| POST | `/api/card/queue/:queueItemId/redirect` | Admin, Standard_User | Redirect ownership for a queue item |
|
||
|
||
### Atlas Action Plans
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/atlas/hosts/:hostId/plans` | Any | Get action plans for a host (from cache or API) |
|
||
| PUT | `/api/atlas/hosts/:hostId/plans` | Admin, Standard_User | Create a new action plan |
|
||
| PATCH | `/api/atlas/hosts/:hostId/plans` | Admin, Standard_User | Update an existing plan |
|
||
| POST | `/api/atlas/hosts/:hostId/refresh` | Admin, Standard_User | Force cache refresh from Atlas API |
|
||
|
||
### VCL Multi-Vertical (CCP Metrics)
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/compliance/vcl-multi/preview` | Admin, Standard_User | Parse xlsx files, extract verticals, compute scoped diffs |
|
||
| POST | `/api/compliance/vcl-multi/commit` | Admin, Standard_User | Commit previewed files in a single transaction |
|
||
| GET | `/api/compliance/vcl-multi/stats` | Any | Aggregated cross-vertical executive summary |
|
||
| GET | `/api/compliance/vcl-multi/trend` | Any | Monthly compliance trend with linear regression forecast |
|
||
| GET | `/api/compliance/vcl-multi/burndown` | Any | Aggregated cross-vertical burndown forecast |
|
||
| GET | `/api/compliance/vcl-multi/verticals` | Any | List known verticals |
|
||
| GET | `/api/compliance/vcl-multi/metrics` | Any | All metrics aggregated across verticals |
|
||
| GET | `/api/compliance/vcl-multi/metrics-list` | Any | Distinct metrics with active non-compliant device counts |
|
||
| GET | `/api/compliance/vcl-multi/metric/:id/stats` | Any | Per-metric summary statistics and donut breakdown |
|
||
| GET | `/api/compliance/vcl-multi/metric/:id/verticals` | Any | Per-vertical breakdown for a metric with sub-teams |
|
||
| GET | `/api/compliance/vcl-multi/metric/:id/trend` | Any | Per-metric monthly trend with forecast |
|
||
| GET | `/api/compliance/vcl-multi/vertical/:code/metrics` | Any | Per-metric breakdown for a specific vertical |
|
||
| GET | `/api/compliance/vcl-multi/vertical/:code/metric/:metricId/devices` | Any | Device list for a vertical+metric; `?team=X` |
|
||
| GET | `/api/compliance/vcl-multi/vertical/:code/burndown` | Any | Burndown forecast for a specific vertical |
|
||
| GET | `/api/compliance/vcl-multi/uploads` | Any | Upload history (most recent 100) |
|
||
| DELETE | `/api/compliance/vcl-multi/vertical/:code` | Admin | Delete all data for a vertical |
|
||
| DELETE | `/api/compliance/vcl-multi/upload/:uploadId` | Admin | Rollback a specific upload (must be most recent for that vertical) |
|
||
| DELETE | `/api/compliance/vcl-multi/all` | Admin | Delete all multi-vertical data |
|
||
|
||
### Notifications
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/notifications` | Any | Get unread notifications for current user (max 50) |
|
||
| GET | `/api/notifications/count` | Any | Get unread count for badge display |
|
||
| PATCH | `/api/notifications/:id/read` | Any | Mark a single notification as read (own only) |
|
||
| POST | `/api/notifications/read-all` | Any | Mark all notifications as read |
|
||
|
||
### Feedback
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/feedback` | Any | Submit bug report or feature request to GitLab (multipart with optional screenshots) |
|
||
|
||
### Webhooks
|
||
|
||
| Method | Path | Group | Description |
|
||
|---|---|---|---|
|
||
| POST | `/api/webhooks/gitlab` | Public | GitLab issue webhook receiver (validated by `x-gitlab-token` header) |
|
||
|
||
### Users (Admin only)
|
||
|
||
| Method | Path | Group | 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 | Group | 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 | Group | Description |
|
||
|---|---|---|---|
|
||
| GET | `/api/vendors` | Any | List all distinct vendor names |
|
||
| GET | `/api/stats` | Any | Dashboard statistics |
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
cve-dashboard/
|
||
├── start-servers.sh # Start backend + frontend via systemd
|
||
├── stop-servers.sh # Stop both systemd services
|
||
├── configure.js # Interactive configuration wizard
|
||
├── docker-compose.yml # PostgreSQL 16 container definition
|
||
├── package.json # Root package.json (backend dependencies)
|
||
├── scripts/
|
||
│ ├── deploy-postgres.sh # One-time deployment: container, schema, migration
|
||
│ └── reset-and-migrate.sh # Dev utility: reset DB and re-run migrations
|
||
├── deploy/
|
||
│ ├── cve-backend-production.service
|
||
│ ├── cve-backend-staging.service
|
||
│ └── setup-staging.sh
|
||
├── systemd/ # systemd unit files for auto-start on boot
|
||
│ ├── cve-backend.service
|
||
│ └── cve-frontend.service
|
||
│
|
||
├── backend/
|
||
│ ├── server.js # Express app — routes, middleware, security headers
|
||
│ ├── db.js # PostgreSQL connection pool (pg)
|
||
│ ├── db-schema.sql # Complete DDL for fresh Postgres setup
|
||
│ ├── setup-postgres.js # Schema initializer (runs db-schema.sql)
|
||
│ ├── setup.js # One-time DB init + default admin creation
|
||
│ ├── 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, rate limiting
|
||
│ │ ├── 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
|
||
│ │ ├── archerTemplates.js # Archer template library CRUD + clone + hierarchy
|
||
│ │ ├── ivantiWorkflows.js # Ivanti workflow batch sync and cache
|
||
│ │ ├── ivantiFindings.js # Ivanti host findings sync, notes, overrides, FP counts
|
||
│ │ ├── ivantiTodoQueue.js # Ivanti Queue — personal staging list
|
||
│ │ ├── ivantiArchive.js # Finding archive tracking, transitions, anomaly log
|
||
│ │ ├── ivantiFpWorkflow.js # FP workflow submission to Ivanti API + lifecycle
|
||
│ │ ├── compliance.js # AEO compliance upload, diff, device tracking, notes
|
||
│ │ ├── vclMultiVertical.js # VCL/CCP multi-vertical compliance reporting
|
||
│ │ ├── atlas.js # Atlas action plan proxy + cache
|
||
│ │ ├── jiraTickets.js # Jira ticket CRUD + Jira REST API integration
|
||
│ │ ├── cardApi.js # CARD ownership proxy, mutation, asset-search, enrich
|
||
│ │ ├── notifications.js # In-app notification system
|
||
│ │ ├── feedback.js # Bug reports/feature requests to GitLab
|
||
│ │ └── webhooks.js # GitLab webhook receiver for issue lifecycle
|
||
│ ├── middleware/
|
||
│ │ └── auth.js # requireAuth and requireGroup middleware
|
||
│ ├── helpers/
|
||
│ │ ├── auditLog.js # logAudit helper (fire-and-forget)
|
||
│ │ ├── cardApi.js # CARD API — OAuth token, owner, confirm/decline/redirect, asset-search
|
||
│ │ ├── ivantiApi.js # Ivanti API HTTP helpers (multipart, JSON, form POST)
|
||
│ │ ├── atlasApi.js # Atlas action plan API (Basic auth)
|
||
│ │ ├── jiraApi.js # Jira Data Center REST API helpers (Basic/PAT auth, rate limiting)
|
||
│ │ ├── driftChecker.js # Schema drift detection: compareSchemaToDrift(), loadConfig(), reconcileConfig()
|
||
│ │ ├── vclHelpers.js # VCL metric calculation helpers (burndown, forecast, dedup)
|
||
│ │ └── teams.js # Team validation helpers
|
||
│ ├── migrations/ # Sequential migration scripts (idempotent)
|
||
│ │ └── run-all.js # Run all migrations in order
|
||
│ └── scripts/
|
||
│ ├── migrate-to-postgres.js # One-time SQLite → Postgres data migration
|
||
│ ├── compliance_config.json # Shared parser config (metric_categories, core_cols, skip_sheets)
|
||
│ ├── extract_xlsx_schema.py # Extracts xlsx structure as JSON for drift checking
|
||
│ ├── 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, group helpers)
|
||
├── utils/
|
||
│ ├── graniteLoaderConfig.js # Granite column definitions and groups
|
||
│ ├── graniteLoaderExport.js # XLSX generation logic
|
||
│ └── graniteLoaderPicklists.js # Searchable dropdown options
|
||
└── components/
|
||
├── LoginForm.js # Login page
|
||
├── NavDrawer.js # Side navigation drawer
|
||
├── UserMenu.js # User dropdown (shows group badge)
|
||
├── UserProfilePanel.js # User profile and password change
|
||
├── CalendarWidget.js # Due-date calendar with finding indicators
|
||
├── UserManagement.js # Admin user management modal
|
||
├── AuditLog.js # Admin audit log modal
|
||
├── NvdSyncModal.js # Bulk NVD sync dialog
|
||
├── KnowledgeBaseModal.js # Knowledge base upload/list modal
|
||
├── KnowledgeBaseViewer.js # Inline document viewer
|
||
├── CardOwnerTooltip.js # CARD ownership hover tooltip
|
||
├── CardDetailModal.js # CARD asset detail (from reporting)
|
||
├── CardActionModal.js # CARD confirm/decline/redirect (from queue)
|
||
├── RedirectModal.js # Queue item redirect modal
|
||
├── LoaderModal.js # Granite Loader Sheet generator
|
||
├── SearchableSelect.js # Reusable searchable dropdown
|
||
├── AtlasBadge.js # Atlas action plan indicator badge
|
||
├── AtlasIcon.js # Atlas icon component
|
||
├── AtlasSlideOutPanel.js # Atlas plan detail panel
|
||
├── AdminScopeToggle.js # BU scope toggle
|
||
├── ConfirmModal.js # Themed confirmation dialog
|
||
├── ConsolidationModal.js # Multi-item Jira ticket consolidation
|
||
├── CveTooltip.js # Hover tooltip for CVE badges
|
||
├── DeleteConfirmModal.js # Delete confirmation with details
|
||
├── FeedbackModal.js # Bug report/feature request submission
|
||
├── NotificationBell.js # Notification bell with unread count
|
||
├── RemediationModal.js # Remediation plan editing
|
||
├── TemplateFormModal.js # Archer template create/edit form
|
||
├── TemplateSelector.js # Archer template picker for queue items
|
||
└── pages/
|
||
├── AdminPage.js # Admin panel: users, audit log, system info
|
||
├── ReportingPage.js # Host findings: charts, table, queue, export
|
||
├── IvantiTodoQueuePage.js # Full-page queue view
|
||
├── IvantiCountsChart.js # Ivanti counts history chart
|
||
├── AnomalyBanner.js # Sync anomaly alert banner
|
||
├── ArchiveSummaryBar.js # Finding archive state distribution
|
||
├── CompliancePage.js # AEO compliance: metric cards, device table
|
||
├── ComplianceUploadModal.js # xlsx upload with drift check + diff preview
|
||
├── ComplianceDetailPanel.js # Per-device metrics, history, notes
|
||
├── ComplianceChartsPanel.js # Compliance trend charts
|
||
├── CCPMetricsPage.js # CCP Metrics: multi-vertical executive view
|
||
├── VCLReportPage.js # VCL exec report page
|
||
├── MetricInfoPanel.js # Metric detail drill-down panel
|
||
├── BulkUploadModal.js # Bulk VCL upload
|
||
├── MultiVerticalUploadModal.js # Multi-vertical upload modal
|
||
├── ArcherPage.js # Archer tickets management
|
||
├── ArcherTemplatePage.js # Archer template library
|
||
├── JiraPage.js # Jira ticket management + API integration
|
||
├── KnowledgeBasePage.js # Knowledge base page
|
||
└── ExportsPage.js # Exports page (group-gated)
|
||
```
|
||
|
||
---
|
||
|
||
## Database Schema
|
||
|
||
All tables are defined in `backend/db-schema.sql` and created by `setup-postgres.js`. The database runs in a PostgreSQL 16 Docker container (`steam-postgres`) on port 5433.
|
||
|
||
### Core tables
|
||
|
||
**`cves`** — One row per CVE/vendor pair. `UNIQUE(cve_id, vendor)`. Includes `created_by` column for ownership tracking.
|
||
|
||
**`documents`** — Files attached to a CVE/vendor pair. Foreign key to `cves(cve_id)` with `ON DELETE CASCADE`.
|
||
|
||
**`required_documents`** — Vendor-specific document requirements.
|
||
|
||
**`users`** — Accounts with group-based access control. `user_group` column with values: `Admin`, `Standard_User`, `Leadership`, `Read_Only`. Includes `bu_teams` column for multi-BU tenancy scoping.
|
||
|
||
**`sessions`** — Active sessions with 24-hour expiry.
|
||
|
||
**`audit_logs`** — Append-only log of all state-changing actions.
|
||
|
||
### Feature tables
|
||
|
||
**`knowledge_base`** — Document library entries with title, slug, category, description, file metadata, and `created_by`.
|
||
|
||
**`archer_tickets`** — Archer EXC exception tickets linked to CVE/vendor pairs. `UNIQUE(exc_number)`. Includes `created_by` for ownership tracking. Foreign key to `cves(cve_id, vendor)` with `ON DELETE CASCADE`.
|
||
|
||
**`jira_tickets`** — JIRA tickets linked to CVE/vendor pairs. Includes `created_by`. Foreign key to `cves(cve_id, vendor)` with `ON DELETE CASCADE`.
|
||
|
||
**`ivanti_sync_state`** — Single-row cache for Ivanti workflow batch data.
|
||
|
||
**`ivanti_findings`** — One row per Ivanti host finding. Indexed on `state`, `bu_ownership`, `severity`, and `(state, bu_ownership)`. Replaces the old single-row JSON blob with queryable individual rows. Includes `state` (`open`/`closed`), `workflow_id`, `workflow_state`, `note`, and `override_host_name`/`override_dns` columns.
|
||
|
||
**`ivanti_counts_history`** — Historical open/closed/FP counts per sync for trend charts.
|
||
|
||
**`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, CARD, GRANITE, DECOM, or Remediate processing. Keyed by `(user_id, finding_id)`. Workflow type constraint: `FP`, `Archer`, `CARD`, `GRANITE`, `DECOM`. Completed items can be redirected to a different workflow type via `POST /:id/redirect`, which creates a new pending item preserving the original finding data.
|
||
|
||
**`ivanti_fp_submissions`** — Record of FP workflow submissions to the Ivanti API. Tracks user, workflow batch ID, form fields, finding IDs, queue item IDs, attachment results, and submission status (success/partial/failed). Lifecycle status tracks the workflow through: submitted, approved, rejected, rework, resubmitted. Rejected submissions can be dismissed (`dismissed_at`) or re-queued to the todo queue under a different workflow type (`requeued_at`).
|
||
|
||
**`ivanti_fp_submission_history`** — Edit history for FP submissions. Tracks change_type (created, fields_updated, findings_added, attachments_added, status_changed) with change_details_json.
|
||
|
||
**`ivanti_finding_archives`** — Archived finding records with lifecycle state tracking. States: ARCHIVED, RETURNED, CLOSED, CLOSED_GONE. Tracks severity at time of archival and transition timestamps.
|
||
|
||
**`ivanti_archive_transitions`** — State transition history for archived findings. Records from_state, to_state, severity_at_transition, and reason for each transition.
|
||
|
||
**`ivanti_sync_anomaly_log`** — Sync anomaly detection log. Records count deltas, newly archived/returned counts, classification breakdown, and significance flag per sync.
|
||
|
||
**`ivanti_finding_bu_history`** — BU reassignment history per finding. Records previous_bu, new_bu, and detection timestamp.
|
||
|
||
**`ivanti_counts_history_by_bu`** — Per-BU historical open/closed counts, enabling per-BU trend lines on the findings chart.
|
||
|
||
**`atlas_action_plans_cache`** — Cached Atlas action plan data for badge rendering. Stores host_id, has_action_plan flag, plan_count, plans_json, and atlas_known flag. Indexed on host_id.
|
||
|
||
**`archer_templates`** — Archer template library. Vendor/platform/model hierarchy with 8 section content fields (environment_overview, segmentation, mitigating_controls, additional_info, charter_network_banner, data_classification, charter_network, additional_access_list). `UNIQUE(vendor, platform, model)`.
|
||
|
||
**`notifications`** — In-app notifications. Per-user notifications with type, title, message, issue_number, and read flag. Used by the NotificationBell component.
|
||
|
||
**`vcl_multi_vertical_summary`** — Per-metric summary data from VCL multi-vertical uploads. Stores metric_id, metric_desc, category, team, priority, non_compliant, compliant, total, compliance_pct, target, and status per upload per vertical. Used for executive reporting without recalculating from items.
|
||
|
||
**`compliance_snapshots`** — Monthly compliance snapshots per vertical. Used for trend charts and linear regression forecasting.
|
||
|
||
**`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. `group_id` column links notes created in the same multi-metric submission. 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
|
||
|
||
### Authentication
|
||
|
||
- Cookie-based sessions with `httpOnly: true`, `sameSite: lax`, `secure: true` (in production)
|
||
- Sessions expire after 24 hours
|
||
- Login rate-limited to 20 attempts per 15-minute window via `express-rate-limit`
|
||
- `SESSION_SECRET` is required — server refuses to start without it
|
||
|
||
### Group-based access control
|
||
|
||
Four groups with distinct permission boundaries enforced server-side via `requireGroup` middleware:
|
||
|
||
| Capability | Admin | Standard_User | Leadership | Read_Only |
|
||
|---|---|---|---|---|
|
||
| View all data | ✓ | ✓ | ✓ | ✓ |
|
||
| Create/edit resources | ✓ | ✓ | ✗ | ✗ |
|
||
| Delete own resources | ✓ | ✓ (restricted) | ✗ | ✗ |
|
||
| Delete any resource | ✓ | ✗ | ✗ | ✗ |
|
||
| Export (CSV/XLSX) | ✓ | ✓ | ✓ | ✗ |
|
||
| Admin panel / user management | ✓ | ✗ | ✗ | ✗ |
|
||
|
||
Standard_User delete restrictions are enforced at the API level: ownership check, finding state check, compliance linkage check, and cascade impact check for CVEs.
|
||
|
||
### 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
|
||
- Content-Disposition headers sanitize filenames to prevent header injection
|
||
|
||
### 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
|
||
|
||
### Content security
|
||
|
||
- Knowledge base PDF iframe uses `sandbox="allow-same-origin"` to prevent script execution
|
||
- Markdown rendering uses `rehype-sanitize` to strip dangerous HTML
|
||
- CSP `frame-ancestors` header derived from `CORS_ORIGINS` environment variable
|
||
|
||
### 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`
|
||
- User group validated against: `Admin`, `Standard_User`, `Leadership`, `Read_Only` (enforced by DB triggers and app-level validation)
|
||
- Hostname format validated with `/^[a-zA-Z0-9._-]+$/` in compliance notes
|
||
- All database operations use parameterized queries (`$1, $2, ...` placeholders) — no string interpolation in SQL
|
||
|
||
### 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=()`
|
||
|
||
---
|
||
|
||
## Upgrading an Existing Deployment
|
||
|
||
This procedure updates the application code and schema while preserving all existing data.
|
||
|
||
```bash
|
||
# 1. Stop the running servers
|
||
cd /home/cve-dashboard
|
||
./stop-servers.sh
|
||
|
||
# 2. Pull latest code
|
||
git pull origin master
|
||
|
||
# 3. Install backend dependencies (picks up any new packages)
|
||
npm install
|
||
|
||
# 4. Install frontend dependencies
|
||
cd frontend
|
||
npm install
|
||
cd ..
|
||
|
||
# 5. Ensure SESSION_SECRET and DATABASE_URL are set in backend/.env
|
||
# If missing:
|
||
# echo "SESSION_SECRET=$(openssl rand -base64 32)" >> backend/.env
|
||
# echo "DATABASE_URL=postgresql://steam:<password>@localhost:5433/cve_dashboard" >> backend/.env
|
||
|
||
# 6. Apply any schema changes (idempotent — safe to re-run)
|
||
node backend/setup-postgres.js
|
||
|
||
# 7. Rebuild the frontend
|
||
cd frontend
|
||
npm run build
|
||
cd ..
|
||
|
||
# 8. Start servers
|
||
./start-servers.sh
|
||
```
|
||
|
||
After upgrading, clear your browser cookies and log in fresh — session format changes between versions will invalidate old sessions.
|
||
|
||
> **Migrating from SQLite:** If this is the first upgrade after the Postgres migration, run `scripts/deploy-postgres.sh` instead of the manual steps above. It handles the full cutover including data migration. See [Postgres Migration Plan](docs/guides/postgres-migration-plan.md) for details.
|
||
|
||
> **Do not re-run `node setup.js`** on an existing deployment. The legacy SQLite setup script is retained for reference only. Use `setup-postgres.js` for schema initialization.
|
||
|
||
> **NODE_ENV reminder:** If you are running over plain HTTP (no TLS), make sure `NODE_ENV` is **not** set to `production` in `backend/.env`. See [Troubleshooting](#troubleshooting) for details.
|
||
|
||
---
|
||
|
||
## Migrations
|
||
|
||
> **Note:** The migration scripts in `backend/migrations/` are used for both PostgreSQL and legacy SQLite deployments. Run them via `node migrations/run-all.js` which executes all migrations in order. All are idempotent and safe to re-run.
|
||
|
||
```bash
|
||
cd backend
|
||
node migrations/run-all.js
|
||
```
|
||
|
||
For manual execution or debugging, the individual scripts in order:
|
||
|
||
```bash
|
||
cd backend
|
||
node migrations/add_user_groups.js
|
||
node migrations/add_user_ivanti_identity.js
|
||
node migrations/add_user_bu_teams.js
|
||
node migrations/add_knowledge_base_table.js
|
||
node migrations/add_ivanti_sync_table.js
|
||
node migrations/add_ivanti_findings_tables.js
|
||
node migrations/add_ivanti_findings_ipv6_columns.js
|
||
node migrations/add_ivanti_counts_history_table.js
|
||
node migrations/add_ivanti_todo_queue_table.js
|
||
node migrations/add_todo_queue_hostname.js
|
||
node migrations/add_todo_queue_ip_address.js
|
||
node migrations/add_granite_workflow_type.js
|
||
node migrations/add_card_workflow_type.js
|
||
node migrations/add_decom_workflow_type.js
|
||
node migrations/add_remediate_workflow_type.js
|
||
node migrations/add_compliance_tables.js
|
||
node migrations/add_compliance_notes_group_id.js
|
||
node migrations/add_compliance_history_metric_id.js
|
||
node migrations/add_compliance_item_history.js
|
||
node migrations/add_fp_submissions_table.js
|
||
node migrations/add_fp_submission_editing.js
|
||
node migrations/add_fp_submissions_dismissed.js
|
||
node migrations/add_fp_submissions_requeued_at.js
|
||
node migrations/add_archer_tickets_table.js
|
||
node migrations/add_archer_tickets_timestamps.js
|
||
node migrations/add_archer_templates_table.js
|
||
node migrations/add_atlas_action_plans_cache.js
|
||
node migrations/add_atlas_known_column.js
|
||
node migrations/add_finding_archive_tables.js
|
||
node migrations/add_closed_gone_state.js
|
||
node migrations/add_return_classification.js
|
||
node migrations/add_sync_anomaly_tables.js
|
||
node migrations/add_flexible_jira_ticket_creation.js
|
||
node migrations/add_multi_item_jira_ticket.js
|
||
node migrations/add_jira_sync_columns.js
|
||
node migrations/add_jira_sync_columns_pg.js
|
||
node migrations/drop_jira_status_check_constraint.js
|
||
node migrations/add_notifications_table.js
|
||
node migrations/add_created_by_columns.js
|
||
node migrations/add_queue_remediation_notes_table.js
|
||
node migrations/add_vcl_multi_vertical.js
|
||
node migrations/add_vcl_reporting_columns.js
|
||
node migrations/add_vcl_vertical_metadata.js
|
||
node migrations/backfill_anomaly_log.js
|
||
node migrations/backfill_return_classification.js
|
||
node migrations/reclassify_bu_roundtrips.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.
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Login succeeds but all pages show "Error Loading" / 401 Unauthorized
|
||
|
||
**Symptom:** You can log in successfully, but the dashboard shows "Error Loading CVEs", "Failed to fetch", and the browser console shows 401 on every API call.
|
||
|
||
**Cause:** The session cookie has the `Secure` flag set (because `NODE_ENV=production` in `backend/.env`), but the application is being accessed over plain HTTP. Browsers silently refuse to send `Secure` cookies over non-HTTPS connections, so every request after login arrives without a session cookie.
|
||
|
||
**Fix:** Either:
|
||
1. Remove `NODE_ENV=production` from `backend/.env` (or set it to `development`) and restart the backend, **or**
|
||
2. Set up HTTPS (e.g., via nginx reverse proxy with TLS termination) and access the app over `https://`
|
||
|
||
### Login fails with "Too many login attempts"
|
||
|
||
**Cause:** The login endpoint is rate-limited to 20 attempts per 15-minute window. Wait 15 minutes or restart the backend to reset the counter.
|
||
|
||
### Server refuses to start: "SESSION_SECRET environment variable must be set"
|
||
|
||
**Fix:** Add a `SESSION_SECRET` to `backend/.env`:
|
||
```bash
|
||
echo "SESSION_SECRET=$(openssl rand -base64 32)" >> backend/.env
|
||
```
|
||
|
||
### After upgrading: "user_group" errors or missing group data
|
||
|
||
**Fix:** Run the group migration:
|
||
```bash
|
||
cd backend
|
||
node migrations/add_user_groups.js
|
||
node migrations/add_created_by_columns.js
|
||
```
|
||
This maps existing roles to groups automatically (admin→Admin, editor→Standard_User, viewer→Read_Only).
|