Popover now flips above the row when it would overflow the bottom of the
viewport, and clamps horizontally to stay within the window.
Adds CARD as a third workflow type (for out-of-team asset disposition in
CARD) alongside FP and Archer. CARD is styled in green (#10B981) across
the popover toggle and queue panel badge.
DB: new migration (add_card_workflow_type.js) recreates ivanti_todo_queue
with an updated CHECK constraint to allow 'CARD'; run manually on dev.
App-level validation in the route is updated to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a persistent per-user staging queue so analysts can tag findings
during review and batch-process Ivanti workflows in one focused session.
Backend:
- New ivanti_todo_queue table (user-scoped, vendor, workflow_type, status)
- Table auto-created on server startup via idempotent CREATE IF NOT EXISTS
- New route /api/ivanti/todo-queue: GET, POST, PUT/:id, DELETE/:id,
DELETE/completed — all scoped to req.user.id
Frontend (ReportingPage):
- Fixed checkbox column on findings table; clicking opens an add-to-queue
popover (portal) with vendor input and FP/Archer toggle
- Already-queued rows show checked/disabled checkbox
- Queue slide-out panel (420px fixed, CSS transition) with items grouped
by vendor, per-item complete toggle + delete, Clear Completed footer
- Queue button in header with live pending-count badge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the placeholder with a fully functional exports page.
Backend:
- Add GET /api/cves/compliance endpoint reading from cve_document_status view
Frontend (ExportsPage.js):
1. Ivanti Host Findings — 4 sub-exports:
- Full dump (all findings, all columns)
- Pending Action (no FP# and no EXC in notes)
- Overdue SLA (past due date or OVERDUE SLA status)
- By Business Unit (multi-sheet XLSX, one sheet per BU)
2. FP Workflow Summary — one row per unique FP# ticket ID with state,
finding count, affected hosts, BUs, and CVEs
3. CVE Database — status filter dropdown + CSV and XLSX format options
4. Archer Tickets — full EXC ticket list with linked CVEs and URLs
5. Document Compliance Report — per CVE/vendor doc coverage with
"missing only" toggle to generate a gap list
All exports are lazy (data fetched on click), per-button loading states,
global dismissable error banner, auto-fit column widths in XLSX outputs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Renamed the existing FP chart to "FP Finding Status" (counts findings per
workflow state) and added a new "FP Workflow Status" chart that counts
unique FP# ticket IDs per state — so 10 findings under one FP# ticket
counts as 1 ticket, not 10.
Backend: extractFPWorkflow now returns { id, state }; syncFPWorkflowCounts
builds both a finding-count map and a deduped FP# ID map, storing them in
separate columns (fp_workflow_counts_json, fp_id_counts_json). The endpoint
returns findingCounts/findingTotal and idCounts/idTotal.
Frontend: FPWorkflowDonut accepts a centerLabel prop; both donuts share the
same component fed with their respective data slices from the single fetch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The FP Workflow Status donut was reading from the in-memory open findings
array, so Approved FPs (which close the finding and remove it from the
open cache) were invisible.
Backend: during each sync, compute FP workflow state counts from open
findings then sweep all pages of closed findings to capture Approved
(and any other closed-state) FP workflows. Counts are stored in a new
fp_workflow_counts_json column on ivanti_counts_cache and exposed via
GET /api/ivanti/findings/fp-workflow-counts.
Frontend: FPWorkflowDonut now receives counts/total props from the new
endpoint (fetched on load and refreshed after manual sync) instead of
deriving them from the findings prop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If a finding ID from the CSV isn't in ivanti_findings_cache it is now
silently skipped (resolved or outdated) rather than stored. Also aborts
early with a clear message if the cache is empty, prompting the user to
run a Sync first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads a CSV with ID and NOTES columns, matches finding IDs against
the cache, and upserts notes into ivanti_finding_notes. Supports
--dry-run for previewing changes, warns on unknown IDs, truncates
notes over 255 chars, and skips unchanged rows.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- New ivanti_finding_overrides table (finding_id, field, value) with
UNIQUE(finding_id, field) — same survival-across-sync pattern as notes
- PUT /api/ivanti/findings/:id/override (editor/admin only) — saves or
clears a field override; empty value = revert to Ivanti
- Overrides merged into findings at read time via readOverrides()
- Whitelisted fields: hostName, dns
Frontend:
- OverrideCell component — click to edit inline (editor/admin only),
Enter/blur to save, Escape to cancel
- Amber dot indicator on cells with an active local override
- Hover tooltip shows original Ivanti value when overridden
- RotateCcw button reverts cell back to Ivanti value in one click
- canWrite() gating via useAuth — viewers see the value, can't edit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: adds ivanti_counts_cache table, fetches Closed count (page 0,
size 1) from Ivanti after each Open sync, and exposes GET /counts endpoint.
Frontend: replaces the Metrics placeholder with an SVG donut chart showing
Open vs Closed proportions with counts and percentages. Counts are fetched
on mount and refreshed after manual sync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: only extract FP# workflows; SYS# auto-generated tickets
are no longer stored or shown (not actionable for triage purposes).
Findings with no FP# ticket show blank in the workflow column.
- Frontend: recolor workflow badges by action urgency —
Expired/Rejected = red (act now), Reworked/Actionable = amber
(resubmit), Requested = blue (waiting on approval).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: extractFinding now flattens all workflowDistribution buckets
and prioritises FP# (False Positive) tickets over SYS# workflows.
Falls back to workflowGeneratedNames for FP# IDs not yet in distribution.
- Frontend: Add Workflow column (sortable, filterable) with state-coloured
badge (green=Approved, blue=Requested, amber=Reworked/Actionable,
red=Rejected, grey=Expired/unknown).
- Bump localStorage key to v2 so the new column appears on all clients
without needing a manual cache clear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend extracts cves[] array from f.vulnerabilities.vulnInfoList[].cve
- Frontend shows up to 2 CVE badges (purple) with "+N more" overflow tooltip
- Filter is multi-value aware: selecting a CVE matches any finding containing it
- FilterDropdown expands multi-value arrays into individual checkbox options
- Sort by CVE count (number of associated CVEs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buOwnership field extracted from assetCustomAttributes['1550_host_1'][0]
and stored in SQLite cache; badge-styled cell (sky=STEAM, amber=ACCESS-ENG)
- All columns except Notes get a funnel filter button in the header
- FilterDropdown uses ReactDOM.createPortal + fixed positioning to escape
overflowX:auto clipping; shows unique value checkboxes with search input,
Select All, Clear, and a selected/total count footer
- Severity filter groups by vrrGroup label (CRITICAL/HIGH) not numeric value
- columnFilters state gates a useMemo filtered array before sorting
- Active filter count shown in panel header with amber badge; Clear Filters
button appears in the toolbar when any filters are active
- Empty Set filter (Clear All) hides all rows, consistent with Excel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Extract dueDate from statusEmbedded.dueDate (strip time portion)
- Remove discoveredOn and source from extractFinding (not needed)
Frontend:
- Add Due Date column (color-coded: red=past due, amber=within 30d, gray=future)
- Remove Discovered and Source columns
- ColumnManager component: gear button opens popover with drag-to-reorder and
eye toggle per column; column state persisted to localStorage
- Column order/visibility survives page refresh and syncs
- SortIcon, TableCell, NoteCell all driven by current visible column list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New panel below Archer tickets showing workflow count and list
- Backend proxies platform4.risksense.com workflowBatch/search via x-api-key
- SQLite cache table (ivanti_sync_state) stores latest sync result
- Auto-syncs on server startup if >24h stale, then every 24h via setInterval
- POST /api/ivanti/workflows/sync for on-demand sync with spinner feedback
- GET /api/ivanti/workflows returns cached data instantly (no live API call)
- Displays id.value, name, currentState, type, createdOn per workflow
- Shows last-synced timestamp and error messages inline
- IVANTI_SKIP_TLS flag for Charter SSL proxy environments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add archer_tickets table with EXC number, Archer URL, status, CVE, and vendor
- Create backend routes for CRUD operations on Archer tickets
- Add right panel section displaying active Archer tickets
- Implement modals for creating and editing Archer tickets
- Validate EXC number format (EXC-XXXX)
- Support statuses: Draft, Open, Under Review, Accepted
- Purple theme (#8B5CF6) to distinguish from JIRA tickets
- Role-based access control for create/edit/delete operations
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements a comprehensive system for uploading and processing weekly
vulnerability reports that automatically splits multiple CVE IDs in a
single cell into separate rows for easier filtering and analysis.
Backend Changes:
- Add weekly_reports table with migration
- Create Excel processor helper using Python child_process
- Implement API routes for upload, list, download, delete
- Mount routes in server.js after multer initialization
- Move split_cve_report.py to backend/scripts/
Frontend Changes:
- Add WeeklyReportModal component with phase-based UI
- Add "Weekly Report" button next to NVD Sync
- Integrate modal into App.js with state management
- Display existing reports with current report indicator
- Download buttons for original and processed files
Features:
- Upload .xlsx files (editor/admin only)
- Automatic CVE ID splitting via Python script
- Store metadata in database + files on filesystem
- Auto-archive previous reports (mark one as current)
- Download both original and processed versions
- Audit logging for all operations
- Security: file validation, auth checks, path sanitization
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- New jira_tickets table (migration script included)
- CRUD API endpoints for tickets with validation and audit logging
- Dashboard section showing all open vendor tickets
- JIRA tickets section within CVE vendor cards
- Tickets linked to CVE + vendor with status tracking (Open/In Progress/Closed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Creates interactive setup script that configures .env files for both
frontend and backend with the correct server IP address. Features:
- Auto-detects current server IP
- Prompts for custom IP if needed
- Checks for existing .env files before overwriting
- Configures REACT_APP_API_BASE, REACT_APP_API_HOST, and CORS settings
This prevents the issue where React apps start with localhost fallback
when .env files are missing or created after server startup.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Changed CVEs table constraint from UNIQUE(cve_id) to UNIQUE(cve_id, vendor)
- Added vendor column to documents table for proper file organization
- Updated backend INSERT statements to include vendor field in both CVE and document creation
- Fixed document retrieval to filter by vendor
- Created corrected setup.js that includes multi-vendor support from initial setup
- Added migration scripts for existing databases
Resolves#1: Users can now add the same CVE-ID with multiple different vendors, each maintaining separate document storage organized as CVE-ID/Vendor/files