Add VCL reporting guide, update reference manual and config wizard; untrack .kiro/steering/workflow.md
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
# Workflow & Context Gathering
|
|
||||||
|
|
||||||
## Specs First
|
|
||||||
|
|
||||||
Before making changes to any feature area, **always check `.kiro/specs/` for related spec folders first**. Specs contain the original requirements, design decisions, architecture diagrams, data models, and task breakdowns that informed the implementation. They provide critical context about:
|
|
||||||
|
|
||||||
- Why a feature was built a certain way
|
|
||||||
- What data models and API contracts were agreed upon
|
|
||||||
- What correctness properties must hold
|
|
||||||
- What edge cases were considered
|
|
||||||
|
|
||||||
Even if the code has evolved since the spec was written, the spec is the starting point for understanding intent.
|
|
||||||
|
|
||||||
## Spec Folder Structure
|
|
||||||
|
|
||||||
Each spec folder typically contains:
|
|
||||||
|
|
||||||
- `requirements.md` — user stories and acceptance criteria
|
|
||||||
- `design.md` — architecture, data models, API contracts, error handling
|
|
||||||
- `tasks.md` — implementation task breakdown with completion status
|
|
||||||
|
|
||||||
## When to Check Specs
|
|
||||||
|
|
||||||
- Fixing bugs in a feature area — check the spec to understand intended behavior
|
|
||||||
- Adding to an existing feature — check the spec to understand design constraints
|
|
||||||
- Investigating unexpected behavior — the spec documents what "correct" looks like
|
|
||||||
- Refactoring — the spec documents which properties must be preserved
|
|
||||||
33
configure.js
33
configure.js
@@ -104,9 +104,9 @@ const VARIABLE_DESCRIPTORS = [
|
|||||||
{
|
{
|
||||||
name: 'CORS_ORIGINS',
|
name: 'CORS_ORIGINS',
|
||||||
group: 'Core Settings',
|
group: 'Core Settings',
|
||||||
required: true,
|
required: false,
|
||||||
default: null, // derived from frontend port at runtime
|
default: null, // derived from frontend port at runtime
|
||||||
description: 'Comma-separated list of allowed CORS origins for the backend',
|
description: 'Allowed CORS origins (only needed if frontend dev server runs on a separate port)',
|
||||||
docUrl: null,
|
docUrl: null,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
validator: 'validateCorsOrigins'
|
validator: 'validateCorsOrigins'
|
||||||
@@ -1229,6 +1229,21 @@ async function main() {
|
|||||||
let confirmedPort = null;
|
let confirmedPort = null;
|
||||||
|
|
||||||
for (const group of GROUP_ORDER) {
|
for (const group of GROUP_ORDER) {
|
||||||
|
// Auto-derive Frontend Settings from PORT and API_HOST — no need to prompt
|
||||||
|
if (group === 'Frontend Settings') {
|
||||||
|
const port = confirmedPort || '3001';
|
||||||
|
const host = values.get('API_HOST') || 'localhost';
|
||||||
|
const apiBase = `http://${host}:${port}/api`;
|
||||||
|
const apiHost = `http://${host}:${port}`;
|
||||||
|
values.set('REACT_APP_API_BASE', apiBase);
|
||||||
|
values.set('REACT_APP_API_HOST', apiHost);
|
||||||
|
console.log(`\n=== Frontend Settings ===`);
|
||||||
|
console.log(` Auto-configured from your backend settings:`);
|
||||||
|
console.log(` REACT_APP_API_BASE = ${apiBase}`);
|
||||||
|
console.log(` REACT_APP_API_HOST = ${apiHost}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
printGroupHeader(group);
|
printGroupHeader(group);
|
||||||
|
|
||||||
// For skippable groups, ask if user wants to configure
|
// For skippable groups, ask if user wants to configure
|
||||||
@@ -1267,14 +1282,6 @@ async function main() {
|
|||||||
if (confirmedPort !== null) {
|
if (confirmedPort !== null) {
|
||||||
currentValue = null; // Will use derived default below
|
currentValue = null; // Will use derived default below
|
||||||
}
|
}
|
||||||
} else if (descriptor.name === 'REACT_APP_API_BASE') {
|
|
||||||
if (confirmedPort !== null) {
|
|
||||||
currentValue = null; // Will use derived default below
|
|
||||||
}
|
|
||||||
} else if (descriptor.name === 'REACT_APP_API_HOST') {
|
|
||||||
if (confirmedPort !== null) {
|
|
||||||
currentValue = null; // Will use derived default below
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1292,12 +1299,6 @@ async function main() {
|
|||||||
};
|
};
|
||||||
} else if (descriptor.name === 'CORS_ORIGINS' && currentValue === null && descriptor.default === null) {
|
} else if (descriptor.name === 'CORS_ORIGINS' && currentValue === null && descriptor.default === null) {
|
||||||
effectiveDescriptor = { ...descriptor, default: 'http://localhost:3000' };
|
effectiveDescriptor = { ...descriptor, default: 'http://localhost:3000' };
|
||||||
} else if (descriptor.name === 'REACT_APP_API_BASE' && currentValue === null && descriptor.default === null) {
|
|
||||||
const port = confirmedPort || '3001';
|
|
||||||
effectiveDescriptor = { ...descriptor, default: `http://localhost:${port}/api` };
|
|
||||||
} else if (descriptor.name === 'REACT_APP_API_HOST' && currentValue === null && descriptor.default === null) {
|
|
||||||
const port = confirmedPort || '3001';
|
|
||||||
effectiveDescriptor = { ...descriptor, default: `http://localhost:${port}` };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await promptVariable(rl, effectiveDescriptor, currentValue);
|
const result = await promptVariable(rl, effectiveDescriptor, currentValue);
|
||||||
|
|||||||
@@ -684,6 +684,8 @@ All endpoints are prefixed with `/api`. All endpoints except `/api/auth/login` a
|
|||||||
| 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/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 |
|
| 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/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
|
### Ivanti — Todo Queue
|
||||||
|
|
||||||
@@ -889,7 +891,7 @@ All tables are defined in `backend/db-schema.sql` and created by `setup-postgres
|
|||||||
|
|
||||||
**`ivanti_todo_queue`** — Personal per-user queue of findings staged for FP, Archer, or CARD processing. Keyed by `(user_id, finding_id)`. 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_todo_queue`** — Personal per-user queue of findings staged for FP, Archer, or CARD processing. Keyed by `(user_id, finding_id)`. 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).
|
**`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). Rejected submissions can be dismissed (`dismissed_at`) or re-queued to the todo queue under a different workflow type (`requeued_at`).
|
||||||
|
|
||||||
**`compliance_uploads`** — Record of each compliance xlsx upload: filename, report date, uploader, timestamp, and new/resolved/recurring counts.
|
**`compliance_uploads`** — Record of each compliance xlsx upload: filename, report date, uploader, timestamp, and new/resolved/recurring counts.
|
||||||
|
|
||||||
@@ -1040,6 +1042,8 @@ node migrations/add_fp_submissions_table.js
|
|||||||
node migrations/add_user_groups.js
|
node migrations/add_user_groups.js
|
||||||
node migrations/add_created_by_columns.js
|
node migrations/add_created_by_columns.js
|
||||||
node migrations/add_fp_submission_editing.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_granite_workflow_type.js
|
node migrations/add_granite_workflow_type.js
|
||||||
node migrations/add_compliance_notes_group_id.js
|
node migrations/add_compliance_notes_group_id.js
|
||||||
```
|
```
|
||||||
|
|||||||
164
docs/guides/kb-vcl-reporting-guide.md
Normal file
164
docs/guides/kb-vcl-reporting-guide.md
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
# VCL Executive Reporting — How It Works
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The VCL (Vulnerability Compliance Level) Report page generates an executive-level compliance summary from device-level data already tracked in the STEAM Security Dashboard. It aggregates individual device findings into team-level metrics, burndown projections, and compliance percentages — the same data leadership uses in the VCL deck for quarterly reporting.
|
||||||
|
|
||||||
|
The report is not a separate data source. It reads from the same `compliance_items` table that the AEO Compliance page uses. The difference is the view: Compliance shows device-level detail, VCL shows team-level aggregation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Metrics Explained
|
||||||
|
|
||||||
|
### Stats Bar
|
||||||
|
|
||||||
|
| Metric | What It Means | What Feeds It |
|
||||||
|
|---|---|---|
|
||||||
|
| Total Devices | Count of unique hostnames across all compliance items (active + resolved) | Weekly compliance xlsx upload |
|
||||||
|
| In-Scope | Same as Total Devices (all tracked devices are considered in-scope) | Weekly compliance xlsx upload |
|
||||||
|
| Compliant | Devices with NO active findings (all their findings are resolved) | Compliance upload resolves findings when devices drop off the report |
|
||||||
|
| Non-Compliant | Devices with at least one active finding | Compliance upload adds new findings; devices stay non-compliant until all findings resolve |
|
||||||
|
| Remediations Required | Same as Non-Compliant (each non-compliant device needs remediation) | Same as Non-Compliant |
|
||||||
|
| Current % | `(Compliant / In-Scope) * 100`, rounded to whole number | Computed from the counts above |
|
||||||
|
| Target % | Organization-defined compliance target (default 95%) | Set via `VCL_TARGET_PCT` environment variable on the backend |
|
||||||
|
|
||||||
|
### Status of Non-Compliant Assets (Donut Chart)
|
||||||
|
|
||||||
|
| Segment | What It Means | What Feeds It |
|
||||||
|
|---|---|---|
|
||||||
|
| Blocked | Non-compliant devices with NO resolution date set — the team has not committed to a remediation timeline | Devices without a `resolution_date` value |
|
||||||
|
| In-Progress | Non-compliant devices WITH a resolution date set — the team has a target fix date | Devices with a `resolution_date` value |
|
||||||
|
|
||||||
|
**How to move devices from Blocked to In-Progress:** Set a resolution date on the device, either by clicking into it on the Compliance page and entering a date, or by using the Bulk Upload with a "Resolution Date" column.
|
||||||
|
|
||||||
|
### Heavy Hitters Table
|
||||||
|
|
||||||
|
| Column | What It Means | What Feeds It |
|
||||||
|
|---|---|---|
|
||||||
|
| Vertical / Team | The team responsible for the non-compliant devices | `team` field on compliance items (set during xlsx upload) |
|
||||||
|
| Non-Compliant | Count of unique hostnames with active findings for that team | Computed from compliance_items |
|
||||||
|
| Compliance Date | The team's stated target for full remediation (e.g., "Q3 2026") | Manually entered on this page (click to edit) |
|
||||||
|
| Notes | Team-level summary of their remediation approach | Manually entered on this page (click to edit) |
|
||||||
|
|
||||||
|
### Vertical Breakdown Table
|
||||||
|
|
||||||
|
| Column | What It Means | What Feeds It |
|
||||||
|
|---|---|---|
|
||||||
|
| Vertical | Team name | `team` field on compliance items |
|
||||||
|
| Compliance % | `(Compliant devices in team / Total devices in team) * 100` | Computed from compliance_items |
|
||||||
|
| Team | Same as Vertical | Same |
|
||||||
|
| Non-Compliant | Count of non-compliant devices for that team | Computed from compliance_items |
|
||||||
|
| Forecast Burndown (monthly columns) | How many devices are expected to be remediated each month | Grouped by the `resolution_date` month on individual devices |
|
||||||
|
| Blockers | Non-compliant devices with NO resolution date (no committed timeline) | Count of devices where `resolution_date` is NULL |
|
||||||
|
| RAs | Risk Acceptances — count of approved exceptions for that team | Manually entered on this page (click to edit) |
|
||||||
|
| Notes | Team-level remediation narrative | Manually entered on this page (click to edit) |
|
||||||
|
|
||||||
|
### Compliance Overview Trend Chart
|
||||||
|
|
||||||
|
| Element | What It Means | What Feeds It |
|
||||||
|
|---|---|---|
|
||||||
|
| Green bars | Count of compliant devices for each month | Monthly snapshots (created automatically on each compliance upload) |
|
||||||
|
| Solid teal line | Actual compliance percentage for each month | Monthly snapshots |
|
||||||
|
| Dashed teal line | Forecasted compliance percentage (projected forward) | Linear regression on the last 3+ months of actual data |
|
||||||
|
| Amber horizontal line | Target compliance threshold | `VCL_TARGET_PCT` environment variable |
|
||||||
|
|
||||||
|
> The trend chart requires at least one compliance upload to create the first snapshot. After 3+ monthly uploads, the forecast line appears.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Feeds the Data
|
||||||
|
|
||||||
|
### Automatic (from compliance uploads)
|
||||||
|
|
||||||
|
These values update automatically when a new weekly compliance xlsx is uploaded:
|
||||||
|
|
||||||
|
- Total Devices, In-Scope, Compliant, Non-Compliant counts
|
||||||
|
- Current Compliance %
|
||||||
|
- Per-team compliance percentages
|
||||||
|
- Monthly trend snapshots (one snapshot per upload)
|
||||||
|
- Devices moving between active/resolved status
|
||||||
|
|
||||||
|
### Manual (entered by engineers or BIs)
|
||||||
|
|
||||||
|
These values are entered by users and persist until changed:
|
||||||
|
|
||||||
|
| Field | Where to Enter It | Scope |
|
||||||
|
|---|---|---|
|
||||||
|
| Resolution Date | Compliance page → click device → Resolution Date field | Per device |
|
||||||
|
| Remediation Plan | Compliance page → click device → Remediation Plan field | Per device |
|
||||||
|
| Compliance Date | VCL Report → Heavy Hitters table → click the cell | Per team |
|
||||||
|
| Notes | VCL Report → Heavy Hitters or Vertical Breakdown → click the cell | Per team |
|
||||||
|
| RAs (Risk Acceptances) | VCL Report → Vertical Breakdown → click the cell | Per team |
|
||||||
|
|
||||||
|
### Bulk Upload
|
||||||
|
|
||||||
|
For updating many devices at once (e.g., 1000 devices), use the **Bulk Upload** button on the VCL Report page:
|
||||||
|
|
||||||
|
1. Prepare an xlsx file with columns: `Hostname`, `Resolution Date`, `Remediation Plan`, `Notes`
|
||||||
|
2. Click Bulk Upload and select the file
|
||||||
|
3. Review the diff preview (shows matched/unmatched/changed/invalid counts)
|
||||||
|
4. Confirm to commit changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How Metrics Adjust Over Time
|
||||||
|
|
||||||
|
### Weekly Compliance Upload Cycle
|
||||||
|
|
||||||
|
Each weekly xlsx upload triggers these changes:
|
||||||
|
|
||||||
|
1. **New findings** appear as active items → Non-Compliant count increases
|
||||||
|
2. **Resolved findings** (devices no longer on the report) get marked resolved → Compliant count increases
|
||||||
|
3. **A monthly snapshot** is created/updated in `compliance_snapshots` → feeds the trend chart
|
||||||
|
4. **Stats bar** reflects the new totals immediately
|
||||||
|
|
||||||
|
### As Teams Set Resolution Dates
|
||||||
|
|
||||||
|
When resolution dates are added to devices:
|
||||||
|
|
||||||
|
1. **Donut chart shifts** — devices move from "Blocked" (red) to "In-Progress" (amber)
|
||||||
|
2. **Forecast burndown columns populate** — showing expected remediations per month per team
|
||||||
|
3. **Blockers count decreases** — fewer devices without a committed timeline
|
||||||
|
|
||||||
|
### As Devices Get Remediated
|
||||||
|
|
||||||
|
When a device drops off the weekly compliance report (finding resolved):
|
||||||
|
|
||||||
|
1. **Non-Compliant count decreases**
|
||||||
|
2. **Compliant count increases**
|
||||||
|
3. **Current % improves**
|
||||||
|
4. **Team compliance % improves**
|
||||||
|
5. **The device's resolution_date no longer contributes to forecast** (it's done)
|
||||||
|
|
||||||
|
### Trend Chart Over Months
|
||||||
|
|
||||||
|
After 3+ monthly compliance uploads:
|
||||||
|
|
||||||
|
1. The trend chart shows actual compliance % per month (solid line)
|
||||||
|
2. A linear regression projects the trend forward 3 months (dashed line)
|
||||||
|
3. You can see whether the organization is on track to hit the target % (amber line)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary: Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Weekly xlsx upload
|
||||||
|
→ compliance_items (active/resolved findings per device)
|
||||||
|
→ compliance_snapshots (monthly aggregate for trend chart)
|
||||||
|
→ Stats bar, donut, heavy hitters, vertical breakdown auto-update
|
||||||
|
|
||||||
|
Engineers set resolution_date on devices (manual or bulk upload)
|
||||||
|
→ Donut shifts from Blocked to In-Progress
|
||||||
|
→ Forecast burndown columns populate
|
||||||
|
→ Blockers count decreases
|
||||||
|
|
||||||
|
BIs edit team-level fields on VCL Report page
|
||||||
|
→ Compliance Date, Notes, RAs saved per team
|
||||||
|
→ Displayed in Heavy Hitters and Vertical Breakdown tables
|
||||||
|
|
||||||
|
Devices remediated (drop off next weekly upload)
|
||||||
|
→ Compliance % improves
|
||||||
|
→ Trend chart shows upward movement
|
||||||
|
→ Forecast adjusts based on new regression
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user