# Design Document: VCL Compliance Reporting
## Overview
This feature adds an executive-level VCL (Vulnerability Compliance Level) reporting page to the existing Compliance module, extends device records with remediation tracking fields (resolution date, remediation plan), and introduces a bulk upload mechanism for updating device metadata in batch. The VCL Report Page mirrors the layout of the leadership's existing spreadsheet deck — summary statistics bar, trend chart with forecast, non-compliant asset donut chart, heavy hitters table, and vertical breakdown table with burndown projections.
The implementation builds on the existing `compliance.js` route module, `compliance_items` table, and `CompliancePage.js` frontend component. New backend endpoints compute VCL statistics from existing data plus the new `resolution_date` and `remediation_plan` columns. The frontend adds a new `VCLReportPage.js` component accessible from the Compliance module navigation.
## Architecture
```mermaid
sequenceDiagram
participant U as User
participant FE as React Frontend
participant BE as Express Backend
participant DB as PostgreSQL
Note over FE,DB: Device Metadata Update (single device)
U->>FE: Edit resolution_date / remediation_plan in DetailPanel
FE->>BE: PATCH /api/compliance/items/:hostname/metadata
BE->>DB: UPDATE compliance_items SET resolution_date, remediation_plan WHERE hostname = $1
BE-->>FE: 200 OK { updated: count }
Note over FE,DB: VCL Report Page Load
FE->>BE: GET /api/compliance/vcl/stats
BE->>DB: Aggregate compliance_items (counts, percentages, categorization)
DB-->>BE: Raw counts
BE->>BE: Compute stats, categorization, heavy hitters, vertical breakdown
BE-->>FE: JSON { stats, donut, heavyHitters, verticalBreakdown }
FE->>BE: GET /api/compliance/vcl/trend
BE->>DB: Monthly aggregation from compliance_uploads + compliance_items history
DB-->>BE: Monthly data points
BE->>BE: Compute actuals + forecast
BE-->>FE: JSON { months: [...] }
Note over U,DB: Bulk Upload Flow
U->>FE: Select xlsx file in bulk upload control
FE->>FE: Parse xlsx with 'xlsx' library (client-side)
FE->>FE: Map columns, validate fields, match hostnames
FE->>BE: POST /api/compliance/vcl/bulk-preview { rows: [...] }
BE->>DB: Match hostnames against compliance_items
BE-->>FE: JSON { matched, unmatched, changes, invalid }
FE->>FE: Display Diff_Preview
U->>FE: Confirm changes
FE->>BE: POST /api/compliance/vcl/bulk-commit { changes: [...] }
BE->>DB: BEGIN; UPDATE compliance_items ...; COMMIT;
BE-->>FE: 200 OK { committed: count }
```
### Data Flow Summary
1. **Device metadata** — stored directly on `compliance_items` rows. Updated via PATCH endpoint (single) or bulk commit (batch).
2. **VCL statistics** — computed on-demand from current `compliance_items` state. No separate materialized table needed since the dataset is small (~1000 devices).
3. **Trend data** — derived from `compliance_uploads` history (existing) plus monthly snapshots of compliance percentages stored in a new `compliance_snapshots` table.
4. **Burndown projections** — computed from `resolution_date` values on active non-compliant items, bucketed by month.
## Components and Interfaces
### Backend
#### New Endpoints (added to `backend/routes/compliance.js`)
**`PATCH /api/compliance/items/:hostname/metadata`**
Updates resolution_date and/or remediation_plan for all active items matching a hostname.
- Auth: `requireAuth()`, `requireGroup('Admin', 'Standard_User')`
- Body: `{ resolution_date?: string|null, remediation_plan?: string|null }`
- Validation: resolution_date must be a valid ISO date or null; remediation_plan must be <= 2000 chars
- Response: `{ updated: number }`
**`GET /api/compliance/vcl/stats`**
Returns computed VCL executive summary statistics.
- Auth: `requireAuth()`
- Response:
```json
{
"stats": {
"total_devices": 1200,
"in_scope": 1100,
"compliant": 950,
"non_compliant": 150,
"remediations_required": 150,
"compliance_pct": 86,
"target_pct": 95
},
"donut": {
"blocked": { "count": 45, "pct": 30 },
"in_progress": { "count": 105, "pct": 70 }
},
"heavy_hitters": [
{ "vertical": "Network Ops", "team": "STEAM", "non_compliant": 42, "compliance_date": "2026-06-30", "notes": "..." }
],
"vertical_breakdown": [
{
"vertical": "Network Ops",
"compliance_pct": 82,
"team": "STEAM",
"non_compliant": 42,
"actual_burndown": { "2026-01": 5, "2026-02": 8 },
"forecast_burndown": { "2026-03": 10, "2026-04": 12 },
"blockers": 8,
"risk_acceptances": 3,
"notes": ""
}
]
}
```
**`GET /api/compliance/vcl/trend`**
Returns monthly compliance trend data for the overview chart.
- Auth: `requireAuth()`
- Query params: none
- Response:
```json
{
"months": [
{
"month": "2026-01",
"compliant_count": 900,
"compliance_pct": 82,
"forecast_pct": null,
"target_pct": 95
}
]
}
```
Forecast is computed using linear regression on the last 3+ months of actual data, projected forward.
**`POST /api/compliance/vcl/bulk-preview`**
Accepts parsed bulk upload rows and returns a diff preview.
- Auth: `requireAuth()`, `requireGroup('Admin', 'Standard_User')`
- Body: `{ rows: [{ hostname, resolution_date?, remediation_plan?, notes? }] }`
- Response:
```json
{
"matched": 850,
"unmatched": 12,
"changes": 200,
"invalid": 5,
"details": [
{
"hostname": "srv-001",
"status": "changed",
"fields": {
"resolution_date": { "old": null, "new": "2026-06-15" },
"remediation_plan": { "old": "", "new": "Patch in next window" }
}
}
],
"unmatched_rows": ["unknown-host-1"],
"invalid_rows": [{ "hostname": "srv-bad", "errors": ["resolution_date: invalid date format"] }]
}
```
**`POST /api/compliance/vcl/bulk-commit`**
Commits validated bulk changes in a single transaction.
- Auth: `requireAuth()`, `requireGroup('Admin', 'Standard_User')`
- Body: `{ changes: [{ hostname, resolution_date?, remediation_plan?, notes? }] }`
- Response: `{ committed: number }`
- Audit: logs `compliance_bulk_update` action
#### Pure Helper Functions (exported for testing)
```javascript
// Truncates text to maxLen chars with ellipsis
function truncateText(text, maxLen = 80) { ... }
// Validates remediation_plan length
function validateRemediationPlan(text) { ... }
// Validates a date string (ISO format)
function isValidDateString(str) { ... }
// Computes VCL summary stats from device rows
function computeVCLStats(items, targetPct) { ... }
// Categorizes non-compliant devices into blocked/in-progress
function categorizeNonCompliant(items) { ... }
// Ranks verticals by non-compliant count descending
function rankHeavyHitters(verticalData) { ... }
// Computes forecasted burndown from resolution_date values
function computeForecastBurndown(items) { ... }
// Matches uploaded rows to existing devices by hostname
function matchByHostname(uploadedRows, existingHostnames) { ... }
// Computes diff between uploaded values and current DB values
function computeBulkDiff(matchedRows, currentData) { ... }
// Maps column headers to known field names
function mapColumnHeaders(headers) { ... }
// Formats a decimal as a whole-number percentage string
function formatPct(decimal) { ... }
```
### Frontend
#### New Component: `VCLReportPage.js`
Located at `frontend/src/components/pages/VCLReportPage.js`. Accessible via a tab/button on the existing CompliancePage or as a separate nav entry.
**Sub-components:**
| Component | Purpose |
|-----------|---------|
| `VCLStatsBar` | Horizontal bar with 7 stat cards (Total, In-Scope, Compliant, Non-Compliant, Remediations, Current %, Target %) |
| `ComplianceOverviewChart` | Recharts ComposedChart — bars for compliant count, solid line for actual %, dashed line for forecast %, ReferenceLine for target |
| `NonCompliantDonutChart` | Recharts PieChart (donut) — Blocked vs In-Progress segments |
| `HeavyHittersTable` | Sorted table of top verticals by non-compliant count |
| `VerticalBreakdownTable` | Full breakdown table with burndown columns |
| `BulkUploadModal` | Modal with file picker, column mapping preview, diff display, confirm/cancel |
#### Modified Component: `ComplianceDetailPanel.js`
Add two new fields to the device detail panel:
- **Resolution Date** — `` with save on blur/enter
- **Remediation Plan** — `