Commit Graph

201 Commits

Author SHA1 Message Date
Jordan Ramos
56bd5ca148 Restructure CCP Metrics to metric-first hierarchy, fix Jira cross-project sync
CCP Metrics View Restructure:
- Add GET /metrics endpoint (aggregated across verticals)
- Add GET /metric/:id/verticals endpoint (per-vertical breakdown)
- Replace VerticalTable with MetricTable on overview (one row per metric)
- Add MetricDetailView for metric-first drill-down
- Restructure navigation: Metric → Vertical → Subteam → Devices
- Remove By Vertical table from AggregatedBurndownChart

Jira Sync Fix:
- Remove hardcoded project filter from getIssue() and searchIssuesByKeys()
- Issue keys are globally unique; project filter broke cross-project tickets
- Fixes 502 Bad Gateway when syncing tickets from non-STEAM projects
2026-05-20 13:30:22 -06:00
Jordan Ramos
64d5e0cb40 Fix CCP Metrics page crash for non-Admin users
CCPMetricsPage called isEditor() which does not exist in AuthContext.
Admin users were unaffected due to JS short-circuit evaluation on
isAdmin() || isEditor(). Standard_User accounts hit TypeError because
isEditor was undefined.

Replaced isEditor() with canWrite() which is the correct auth helper
for write-capable users (Admin + Standard_User).

Closes #15
2026-05-20 11:41:40 -06:00
Jordan Ramos
0c99420f17 Fix CCP Metrics crash when donut chart has zero non-compliant devices
Recharts PieChart throws internally when all data segments are zero.
Guard against this by rendering a friendly message instead of passing
all-zero data to the chart component.

Affects users whose vertical data has no non-compliant items.
2026-05-19 14:59:08 -06:00
Jordan Ramos
f00a1ce7bb Replace Webex bot with in-app notification system
Org blocks external Webex bots, so replaced the DM approach with an in-app
notification bell. GitLab webhook still fires on issue close, but now writes
to a notifications table instead of calling Webex API.

- New: notifications table + migration
- New: GET/PATCH/POST /api/notifications endpoints
- New: NotificationBell component (bell icon + badge + dropdown)
- Removed: backend/helpers/webexBot.js (org-blocked)
- Removed: WEBEX_BOT_TOKEN from .env
2026-05-18 17:15:05 -06:00
Jordan Ramos
00bf92a2a1 Add screenshot uploads to feedback modal, Webex bot DM on issue close
- Feedback modal now supports up to 3 image attachments (PNG/JPG/GIF/WebP, 5MB
  each) with thumbnail previews. Images are uploaded to GitLab project uploads
  and embedded as markdown in the issue description.
- New webhook endpoint (POST /api/webhooks/gitlab) receives issue close events,
  parses the submitter from the description, looks up their email, and sends a
  Webex DM via the Patches O'Houlihan bot.
- New helper: backend/helpers/webexBot.js (fire-and-forget DM sender).
- Requires WEBEX_BOT_TOKEN and GITLAB_WEBHOOK_SECRET in backend/.env.
2026-05-18 16:54:00 -06:00
Jordan Ramos
492780fd90 Add aggregated burndown forecast to CCP Metrics overview page 2026-05-15 17:08:55 -06:00
Jordan Ramos
4d255209fd Group history entries together, remove (optional) from change reason
1. History entries saved at the same time by the same user now display
   as a single grouped entry (resolution date + remediation plan together)
2. Removed '(optional)' from the change reason placeholder — engineers
   should treat it as expected, even though the backend allows empty
3. Save button now saves both resolution date AND remediation plan in one
   call (removed the onBlur auto-save on the date field) so they share
   a timestamp and group correctly in history
2026-05-15 15:31:56 -06:00
Jordan Ramos
1fe6c1f84c Add remediation plan and resolution date history tracking
New table compliance_item_history stores an append-only audit trail of
changes to resolution_date and remediation_plan. The current values remain
on compliance_items for fast VCL reporting queries (no double-counting).

Backend:
- Migration: creates compliance_item_history with indexes
- PATCH /items/:hostname/metadata: records old→new in history before updating,
  accepts optional change_reason field (max 500 chars)
- GET /items/:hostname: returns history array (last 10 entries, newest first)
- POST /vcl/bulk-commit: records history for each changed field per hostname

Frontend:
- ComplianceDetailPanel: added change reason input below Save button
- Added Change History section showing field changes with timestamps,
  usernames, old→new values, and reasons
- Re-fetches detail after save to show updated history immediately

Tests updated to match new transaction-based PATCH flow.
2026-05-15 10:53:14 -06:00
Jordan Ramos
b808d0e38e Color metric card percentage green/yellow/red based on target, keep NC count always red 2026-05-14 15:30:43 -06:00
Jordan Ramos
a72300475b Clean up metric breakdown panel — compact grid, top 8 with show-all toggle
Replaced the large flex-wrap button cards with a tight CSS grid of compact
cells (130px min). Each cell shows metric ID, current %, and NC count only.
Category text and target removed to reduce noise.

Capped to top 8 metrics by default with a 'Show all N' toggle for the rest.
Removes visual clutter while keeping the data accessible.
2026-05-14 15:29:20 -06:00
Jordan Ramos
7577ab1219 Make Non-Compliant stat clickable — reveals metric breakdown buttons
Clicking the Non-Compliant card on the CCP Metrics overview now toggles a
panel of metric buttons below it, each showing the metric ID, category,
non-compliant count, and compliance % vs target. Styled like the compliance
page's MetricHealthCard pattern.

Backend: added metric_breakdown to the /stats response — aggregated
cross-vertical metric totals (ALL: rows only, grouped by metric_id).

Also updated tech steering file to document the single-port Express
architecture and the requirement to run npm run build after frontend changes.
2026-05-14 15:24:10 -06:00
Jordan Ramos
a2bc1ff564 Add metric sub-team intermediate drill-down view
Clicking a metric now shows a sub-team breakdown page with totals per team
(compliant, non-compliant, total, %) instead of jumping directly to a flat
device list. Clicking a sub-team then shows the device list filtered to
that team only.

Navigation flow: Overview → Vertical → Metric (sub-team totals) → Team (devices)

Backend: added optional ?team= query param to the device list endpoint for
filtered queries.

Frontend: added MetricSubTeamView component with metric-level stats bar and
clickable sub-team table. Updated navigation state to include selectedTeam.

Also updated design brief to reflect the new drill-down hierarchy.
2026-05-14 14:53:41 -06:00
Jordan Ramos
61d7e00d4f Add sub-team level display to CCP Metrics vertical drill-down
Backend: restructured /vertical/:code/metrics endpoint to return metrics
with nested sub_teams arrays. Each metric now has the ALL: rollup as the
primary row and individual team breakdowns (ACCESS-OPS, STEAM, etc.) as
sub_teams. Also returns a teams array for the filter UI.

Frontend: VerticalDetailView now supports two interaction modes:
- Expand/collapse: click the arrow on any metric row to reveal sub-team
  breakdown inline (teal-highlighted rows beneath the parent)
- Team filter: click a team button to filter the entire table to show
  only that team's numbers per metric

Both modes avoid double-counting by using the ALL: rollup for totals
and only showing sub-team data as supplementary detail.
2026-05-14 12:27:46 -06:00
Jordan Ramos
408aaa7012 Add data management panel with delete vertical, rollback upload, and reset all
Backend:
- DELETE /api/compliance/vcl-multi/vertical/:code — wipe a single vertical
- DELETE /api/compliance/vcl-multi/upload/:uploadId — rollback most recent upload
- DELETE /api/compliance/vcl-multi/all — nuclear reset of all multi-vertical data
- All delete operations are Admin-only and audit-logged

Frontend:
- Manage button (red, Admin-only) in CCP Metrics header
- DataManagementPanel modal showing upload history grouped by vertical
- Per-vertical delete button
- Per-upload rollback button (most recent only)
- Reset All button with confirmation dialog
- Success/error messaging
2026-05-14 11:54:58 -06:00
Jordan Ramos
232eedce70 Remove unused icon imports to fix ESLint warning count 2026-05-14 10:00:51 -06:00
Jordan Ramos
0ca2fe99e9 Remove unused imports to satisfy ESLint max-warnings threshold 2026-05-14 10:00:00 -06:00
Jordan Ramos
04360cc4bc Add CCP Metrics page with multi-vertical VCL upload and cross-org reporting
New feature: multi-file per-vertical compliance xlsx upload with scoped
resolution logic, executive-level aggregated reporting, and drill-down
by vertical and metric. Supports daily upload cadence and batch commit.

Backend:
- Migration: add vertical column to compliance_items/uploads, create
  vcl_multi_vertical_summary table
- New route module: routes/vclMultiVertical.js with preview, commit,
  stats, trend, metric drill-down, device list, and burndown endpoints
- New helpers: parseVerticalFilename(), computeVerticalBurndown()
- Vertical-scoped resolution: uploading one vertical never resolves
  items from other verticals

Frontend:
- CCPMetricsPage with stats bar, trend chart, donut, vertical table
- Drill-down: vertical -> metrics by category -> device list
- Per-vertical burndown forecast chart
- MultiVerticalUploadModal: multi-file drag-drop, batch preview, commit
- Nav entry: CCP Metrics (Building2 icon)

Docs:
- Design brief for stakeholder meeting (docs/vcl-multi-vertical-design-brief.md)
2026-05-14 09:49:59 -06:00
Jordan Ramos
0fefd2a707 Add re-queue findings from rejected FP submissions
New feature: users can re-queue findings from a rejected FP submission
back into the Ivanti todo queue under a different workflow type (FP,
Archer, CARD, GRANITE, or DECOM). Primary use case is when an FP is
rejected with a recommendation to submit an Archer risk acceptance.

Backend:
- New migration: add requeued_at column to ivanti_fp_submissions
- New endpoint: POST /api/ivanti/fp-workflow/submissions/:id/requeue
  - Validates workflow_type and vendor (required for FP/Archer/DECOM)
  - Creates new pending queue items from original finding data
  - Marks submission as requeued (prevents double re-queue)
  - Audit logs the action

Frontend (ReportingPage.js):
- RequeueConfirmDialog component with workflow type selector and vendor input
- Re-queue Findings button in Edit FP Modal header (rejected submissions only)
- Already re-queued label when submission.requeued_at is set
- Success notification on completion
2026-05-13 16:46:49 -06:00
Jordan Ramos
828e7cc45d Sync FP submission lifecycle_status from Ivanti currentState on fetch
When GET /submissions enriches submissions with Ivanti API data, it now
checks if batch.currentState (APPROVED, REJECTED, REWORK) differs from
the local lifecycle_status and updates the DB accordingly. This ensures
approved submissions get filtered out of the queue panel as intended.

Also changed safeText() to return null for non-string Ivanti note values
(arrays/objects) instead of JSON-stringifying them. The notes array
filters nulls via .filter(Boolean) so non-string data is simply hidden.
2026-05-13 14:36:05 -06:00
Jordan Ramos
870c0e247a Fix History tab crash: coerce Ivanti note fields to strings before rendering
PostgreSQL + Ivanti API enrichment can return non-string values
(objects/arrays) for currentStateUserNotes and similar fields.
React crashes silently (blank page, no console error) when trying
to render non-string values as children. Same root cause pattern
as Bug 3 in ivanti-panel-bugs-2026-05-12.

Added safeText() wrapper that coerces any non-string truthy value
to a JSON string before rendering in the History tab notes section.
2026-05-13 12:01:52 -06:00
Jordan Ramos
9eec63ea42 Add VCL vertical metadata: inline-editable team fields, JSDoc on compliance routes, stats query rewrite 2026-05-13 07:57:41 -06:00
Jordan Ramos
f3d7f2ac1d Fix archive bar chart: fmtDate now handles ISO datetime strings from PostgreSQL date columns 2026-05-12 14:57:15 -06:00
Jordan Ramos
8c93e86fe0 Fix Ivanti panel bugs: Invalid Date, wrong workflow count, crash on archive click, BU scope filtering 2026-05-12 14:21:46 -06:00
Jordan Ramos
d093a3d113 Add VCL compliance reporting: exec report page, device metadata fields, bulk upload 2026-05-11 15:48:10 -06:00
Jordan Ramos
7245352496 Add FP submissions cleanup: auto-clear approved, dismiss rejected, collapsible section 2026-05-11 14:29:50 -06:00
Jordan Ramos
cda1eaadc9 Add DECOM workflow type, auto-note/hide on decom, show CVEs on CARD queue items, auto-run migrations in pipeline
- Add DECOM to queue workflow types (red badge, inventory-style display)
- When findings are added as DECOM, auto-set note to 'DECOM' and hide row
- Hidden rows are excluded from donut charts (removes from pending count)
- Show CVEs on CARD/GRANITE/DECOM queue items (was previously omitted)
- Add backend/migrations/run-all.js for CI/CD auto-migration execution
- Pipeline now runs migrations before service restart on both staging and prod
- Add add_decom_workflow_type.js migration (updates CHECK constraint)
2026-05-08 14:51:05 -06:00
Jordan Ramos
f76996a161 Fix CI: add express/pg devDeps for atlas test, allow lint warnings, drop forceExit 2026-05-08 13:25:58 -06:00
Jordan Ramos
1b0fc072cc Track package-lock.json files for deterministic CI installs 2026-05-08 13:05:20 -06:00
Jordan Ramos
de2c5f245e Add CI/CD pipeline, feedback modal, Atlas qualys_id fallback, and health endpoint
- Rewrite .gitlab-ci.yml with proper stages, blocking tests, staging
  environment on dev box, and SSH-based production deploy to 71.85.90.6
- Add POST /api/health endpoint for pipeline verification
- Add POST /atlas/hosts/:hostId/refresh-cache for Atlas cache staleness
- AtlasSlideOutPanel: auto-resolve qualys_id from Atlas vulnerabilities,
  prefer qualys_id over active_host_findings_id, retry on failure
- Add FeedbackModal component with bug report button in header and
  feature request in UserMenu, creates GitLab issues via /api/feedback
- Fix all frontend test failures (ESM transforms, TextDecoder polyfill,
  fast-check resolution, App.test.js boilerplate replacement)
- Fix root package.json test script to run jest
- Add deploy/ directory with staging systemd service and setup script
2026-05-08 12:50:11 -06:00
Jordan Ramos
cf43e85c38 fix: scope FP workflow counts donut by BU
- Rewrite /fp-workflow-counts endpoint to query ivanti_findings table
  directly with optional teams ILIKE filter (replaces pre-computed JSON blob)
- Frontend passes getActiveTeamsParam() to FP counts fetch
- FP counts refresh on scope toggle change alongside open/closed counts
- Both FP Finding Status and FP Workflow Status donuts now respect BU scope
2026-05-06 15:19:34 -06:00
Jordan Ramos
573903a885 feat: per-BU trend lines in counts history chart
- Create ivanti_counts_history_by_bu table (bu_ownership, state, count per sync)
- Sync writes per-BU snapshot alongside global history on each sync
- Seed table with current counts for immediate first data point
- GET /counts/history accepts ?teams param — queries per-BU table when filtered
- IvantiCountsChart accepts teamsParam prop, re-fetches on scope change
- ReportingPage passes getActiveTeamsParam() to the chart
- Historical per-BU data accumulates from this point forward
- Global history (no filter) still uses the original aggregate table
2026-05-06 13:38:38 -06:00
Jordan Ramos
8cd73c126e feat(postgres): data migration + per-BU closed counts in frontend
- Create backend/scripts/migrate-to-postgres.js (one-time SQLite→Postgres copy)
- Successfully migrated: 6 users, 21 CVEs, 6307 findings, 20965 compliance items,
  138 archives, 67 atlas plans, all notes/overrides merged
- All 22 tables verified with matching row counts
- Frontend StatusDonut now uses server-provided per-BU counts (no more N/A)
- Counts endpoint called with teams param on scope change
- Re-fetch counts when admin scope toggle changes
2026-05-06 12:26:54 -06:00
Jordan Ramos
5cdca09f40 docs: add Postgres migration plan and Kiro spec
- docs/guides/postgres-migration-plan.md: full migration manual with
  phases, port allocation, rollback plan, and timeline
- .kiro/specs/postgres-migration/: requirements, design, and tasks
- Replaces findings_json blob with individual indexed rows
- Enables per-BU closed counts via SQL queries
- Uses existing Postgres instance (port 5432), new cve_dashboard DB
- Testing on port 3003, cutover to 3001 with 30s downtime
2026-05-05 15:04:14 -06:00
Jordan Ramos
bd5fcccacf perf: client-side BU filtering for instant scope switching
- Fetch ALL findings once on mount (no teams param to backend)
- Filter client-side via scopedFindings useMemo keyed on adminScope
- Eliminates 5-10s round-trip on every scope change
- Open vs Closed donut now uses scopedFindings.length for open count
- Closed count remains global (no per-BU closed data available)
- Action Coverage donut automatically scoped via visibleFindings chain
- Remove server-side teams param from counts fetch (client handles it)
2026-05-05 12:08:01 -06:00
Jordan Ramos
df3173a720 feat: replace binary scope toggle with multi-select BU picker
- Add IVANTI_BU_FILTER to .env with all four BUs (STEAM, ACCESS-ENG, ACCESS-OPS, INTELDEV)
- Rework AdminScopeToggle from binary (My Teams/All) to multi-select dropdown
- Admin can now pick any combination of BUs to view
- Presets: 'All BUs' and 'My Teams' for quick selection
- Individual team checkboxes for custom combinations
- Selection persisted in localStorage as JSON array
- AuthContext updated: adminScope is now an array of selected teams
- getActiveTeamsParam() returns comma-joined selected teams (empty = no filter)
- getAvailableTeams() returns selected teams for compliance selector
2026-05-05 11:31:15 -06:00
Jordan Ramos
9b8ae6cd79 fix: move AdminScopeToggle from NavDrawer to main header bar
Places the scope toggle next to the UserMenu avatar in the top-right
header area so it's always visible without opening the nav drawer.
2026-05-05 11:21:59 -06:00
Jordan Ramos
2656df94d3 feat: add multi-BU tenancy with per-user team scoping (Option B)
- Add bu_teams column to users table (migration + fresh schema)
- Create shared KNOWN_TEAMS constant and validateTeams helper
- Expose user teams in auth middleware, login, and /me responses
- Add bu_teams CRUD to user management routes with audit logging
- Make Ivanti FINDINGS_FILTERS configurable via IVANTI_BU_FILTER env var
- Add query-time team filtering to GET /findings and /findings/counts
- Update AuthContext with teams helpers and admin scope toggle
- Create AdminScopeToggle component (My Teams / All BUs)
- Scope ReportingPage findings fetch by user teams
- Scope CompliancePage team selector by user teams
- Scope ExportsPage findings exports by user teams
- Add BU teams multi-select to UserManagement create/edit forms
- Display team badges in user list table
2026-05-05 11:04:53 -06:00
Jordan Ramos
7f7d3a2977 release: v1.0.0 — clean README, changelog, full reference manual, dead code removal, package metadata 2026-05-01 21:18:31 +00:00
root
5a9df2103f fix: aggregate anomaly data per day instead of taking latest — fixes missing returned bars when multiple syncs per day 2026-05-01 19:29:11 +00:00
root
15abf8bae4 feat: add return classification for archive chart, CARD API integration, compliance charts, systemd services 2026-05-01 17:15:41 +00:00
root
caa1d539cc Add CARD API integration spec, Atlas metrics updates, NavDrawer and server.js cleanup, reference docs 2026-04-28 16:38:18 +00:00
root
b1069b1a05 Add Jira Data Center integration with UAT test script and use case docs 2026-04-28 16:36:54 +00:00
root
1186f9f807 Fix build: remove unused imports, set CI=false for react-scripts build 2026-04-28 14:22:19 +00:00
root
623b57ca06 Fix Atlas vulnerability response parsing — API returns arrays per host, not objects 2026-04-27 16:21:19 +00:00
root
06c6821d85 Add multi-select qualys_id picker to bulk Atlas action plan modal with auto-fetch from Atlas API 2026-04-24 22:07:55 +00:00
root
8da62f0f14 Require qualys_id for risk_acceptance in bulk Atlas action plan modal 2026-04-24 21:58:53 +00:00
root
5a9dc007db Add bulk Atlas action plan creation from row selection toolbar 2026-04-24 21:49:04 +00:00
root
3f9e1da2a3 Fix findings export to use overridden hostname and DNS values 2026-04-24 21:38:43 +00:00
root
00a6f7ae0f Add archive activity sparkline to findings trend chart and update investigation doc 2026-04-24 21:06:35 +00:00
root
6ee68f5521 Add sync anomaly detection, BU drift monitoring, and findings count investigation
- Add BU drift checker that classifies archived findings as BU reassignment,
  severity drift, closure, or decommission via unfiltered Ivanti API queries
- Add post-sync anomaly summary with significance threshold and classification
  breakdown stored in ivanti_sync_anomaly_log table
- Add per-finding BU tracking that detects BU changes across syncs and records
  them in ivanti_finding_bu_history table
- Add drift guard that skips trend history writes when total drops more than 50%
- Add CLOSED_GONE archive state for findings that vanish from the closed set
- Add anomaly banner UI on Vulnerability Triage page for significant sync changes
- Add API endpoints for anomaly latest/history and BU change tracking
- Add diagnostic scripts for drift checking and BU reassignment verification
- Add investigation document and xlsx export for the April 2026 BU reassignment
  incident where 109 findings were moved to SDIT-CSD-ITLS-PIES
- Migrations required: add_closed_gone_state.js, add_sync_anomaly_tables.js
2026-04-24 20:34:34 +00:00