Files

8.1 KiB
Raw Permalink Blame History

Implementation Plan: Batch Finding Disposition

Overview

Add multi-select capability to the Vulnerability Triage findings table with a batch-add-to-queue API endpoint. The backend gets a new POST /api/ivanti/todo-queue/batch route in ivantiTodoQueue.js. The frontend gets selection state, checkbox dual-mode logic, a SelectionToolbar component, shift-click range select, select-all, and Escape-to-clear — all within ReportingPage.js.

Tasks

  • 1. Add POST /api/ivanti/todo-queue/batch endpoint

    • 1.1 Add batch route handler to backend/routes/ivantiTodoQueue.js
      • Add POST /batch route inside createIvantiTodoQueueRouter, before the POST / route
      • Apply requireAuth(db) and requireGroup('Admin', 'Standard_User') middleware
      • Validate request body: findings array (1200 items), each with non-empty finding_id string
      • Validate workflow_type is one of FP, Archer, CARD
      • Validate vendor: required non-empty string ≤200 chars for FP/Archer; ignored for CARD
      • If any validation fails, return 400 with descriptive error message and reject entire batch
      • Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.8, 3.10
    • 1.2 Implement transactional batch insert with SQLite
      • Use db.serialize() with BEGIN TRANSACTION / COMMIT to insert all findings atomically
      • For each finding: insert row into ivanti_todo_queue with user_id, finding_id, finding_title, cves_json, ip_address, vendor, workflow_type
      • On success: fetch all inserted rows, parse cves_json back to arrays, return 201 with { items: [...] }
      • On any DB error: ROLLBACK the transaction and return 500
      • Requirements: 3.7, 3.8, 3.9, 3.11
    • 1.3 Add audit logging for batch additions
      • After successful commit, call logAudit(db, { ... }) with action 'batch_add_to_queue', entityType 'ivanti_todo_queue', and details including the count and workflow_type
      • Import logAudit from ../helpers/auditLog
      • Requirements: 3.7
  • 2. Checkpoint — Verify backend endpoint

    • Ensure the batch endpoint is syntactically correct and the route file has no errors. Ask the user if questions arise.
  • 3. Add multi-select state and checkbox dual-mode logic to ReportingPage.js

    • 3.1 Add selection state variables to VulnerabilityTriagePage
      • Add selectedIds (new Set()), lastClickedId (null), batchSubmitting (false), batchError (null), batchWorkflowType ('FP'), batchVendor ('') as new useState hooks
      • Requirements: 1.1, 2.1
    • 3.2 Implement checkbox dual-mode click handler
      • Replace the existing <td> onClick in the checkbox cell with new logic:
        • If finding is already queued → no-op (existing behavior)
        • If selectedIds.size === 0 AND not shift-click → open AddToQueuePopover (preserves single-select flow)
        • If shift-click AND lastClickedId exists → range-select all visible non-queued findings between lastClickedId and current finding in the sorted array
        • Otherwise → toggle finding.id in selectedIds
      • Always update lastClickedId when toggling selection
      • Requirements: 1.1, 1.2, 5.1, 5.2, 6.1
    • 3.3 Add visual highlighting for selected rows
      • When a finding's ID is in selectedIds, apply a highlighted background (e.g. rgba(14,165,233,0.12)) to the row
      • Override the existing alternating row background and hover for selected rows
      • Requirements: 1.3
    • 3.4 Disable checkbox for already-queued findings
      • Keep existing behavior: queued findings show checked + disabled checkbox, preventing re-selection
      • Ensure queued findings are excluded from shift-click range select and select-all
      • Requirements: 1.5
  • 4. Implement Select All / Deselect All in column header

    • Modify the checkbox column <th> to render a clickable "Select All" checkbox when selectedIds.size > 0 or when the user interacts with it
    • Click behavior: if not all visible non-queued findings are selected → select all visible non-queued; if all are selected → deselect all
    • Requirements: 1.6, 1.7
  • 5. Add selection pruning on filter changes

    • Add a useEffect that watches filtered (the filtered findings array) and prunes selectedIds to only include IDs still present in the filtered set
    • This ensures selection stays consistent when columnFilters, actionFilter, or excFilter change
    • Requirements: 1.4
  • 6. Implement SelectionToolbar component

    • 6.1 Create the SelectionToolbar inline component in ReportingPage.js
      • Render between the panel header controls and the <table> element, only when selectedIds.size > 0
      • Use position: sticky with appropriate top value to stay visible during scroll
      • Follow the dark theme design system: monospace fonts, dark gradient background, accent-colored borders
      • Requirements: 2.1, 2.8
    • 6.2 Add toolbar controls: count badge, Clear Selection, workflow toggles, vendor input, submit button
      • Display selected count badge (e.g. "12 selected")
      • "Clear Selection" button that empties selectedIds and hides toolbar
      • Workflow type toggle buttons (FP / Archer / CARD) using existing color scheme: FP = amber (#F59E0B), Archer = blue (#0EA5E9), CARD = green (#10B981)
      • Vendor text input (hidden when CARD is selected, show "No vendor required" indicator for CARD)
      • "Add to Queue" submit button — enabled only when workflow_type is CARD, or vendor is non-empty
      • Requirements: 2.2, 2.3, 2.4, 2.5, 2.6, 2.7
  • 7. Implement batch submission flow

    • 7.1 Add submitBatch async function to VulnerabilityTriagePage
      • Build request payload from selectedIds (map each ID to its finding object from sorted/filtered for finding_id, finding_title, cves, ip_address), plus batchWorkflowType and batchVendor
      • POST to ${API_BASE}/ivanti/todo-queue/batch with credentials: 'include'
      • Set batchSubmitting = true before request, false after
      • Requirements: 4.1, 4.2
    • 7.2 Handle batch success response
      • On 201: merge returned items into queueItems state (sorted by vendor then id, matching existing pattern)
      • Clear selectedIds, reset batchWorkflowType to 'FP', reset batchVendor to '', clear batchError
      • The newly queued findings will automatically show as checked+disabled via the existing isQueued() helper
      • Requirements: 4.3, 4.4, 4.6
    • 7.3 Handle batch error response
      • On 4xx/5xx: parse error message from response JSON, set batchError to display in toolbar
      • On network failure: set batchError to "Network error — please try again"
      • Keep selection intact on error so user can retry
      • Requirements: 4.5
  • 8. Add Escape key handler to clear selection

    • Add a useEffect with a keydown listener for Escape that clears selectedIds when the SelectionToolbar is visible (i.e. selectedIds.size > 0)
    • Ensure it doesn't conflict with the existing Escape handler on AddToQueuePopover
    • Requirements: 6.3
  • 9. Ensure keyboard Tab accessibility for SelectionToolbar

    • Verify all interactive elements in the toolbar (workflow buttons, vendor input, submit button, clear button) are focusable via Tab key
    • Use native <button> and <input> elements (which are inherently tabbable) rather than <div> with onClick
    • Requirements: 6.2
  • 10. Final checkpoint — Full integration verification

    • Ensure all files have no syntax errors or diagnostic issues
    • Verify the checkbox dual-mode logic: no selection → popover, existing selection → toggle
    • Verify the SelectionToolbar renders/hides correctly based on selection state
    • Verify batch submit wires through to the backend endpoint and updates queue state
    • Ensure all tests pass, ask the user if questions arise.

Notes

  • No new database migration needed — batch insert reuses the existing ivanti_todo_queue schema
  • The batch endpoint must be registered before POST / in the router to avoid Express route conflicts
  • All testing is done on the dev server after push — no local test tasks included
  • Each task references specific acceptance criteria from the requirements document for traceability