- Add shared ivantiApi.js helper (ivantiPost + ivantiMultipartPost) - Add ivantiFpWorkflow.js backend route with validation, Ivanti API workflow creation, attachment uploads, submission tracking, and audit - Add add_fp_submissions_table.js migration - Wire route into server.js at /api/ivanti/fp-workflow - Add FpWorkflowModal component in ReportingPage.js with form fields, drag-and-drop file upload, progress indicator, and result views - Add Create FP Workflow button to QueuePanel footer (editor/admin only) - Refactor ivantiWorkflows.js and ivantiFindings.js to use shared helper
8.8 KiB
Implementation Plan: Ivanti FP Workflow Submission
Overview
Implement the ability to select FP-type items from the Ivanti Queue and submit False Positive workflows to the Ivanti/RiskSense API, with file attachment support, local submission tracking, and audit logging. The implementation follows existing codebase conventions: factory-pattern Express routes, Multer for file uploads, inline React component styles with the dark tactical theme, and the ivantiPost() HTTP helper for Ivanti API calls.
Tasks
-
1. Database migration and shared helpers
-
1.1 Create migration script
backend/migrations/add_fp_submissions_table.js- Create
ivanti_fp_submissionstable with columns: id, user_id, username, ivanti_workflow_batch_id, ivanti_generated_id, workflow_name, reason, description, expiration_date, scope_override, finding_ids_json, queue_item_ids_json, attachment_count, attachment_results_json, status (success/partial/failed), error_message, created_at - Add indexes on user_id and ivanti_generated_id
- Follow existing migration pattern from
add_ivanti_todo_queue_table.js - Requirements: 6.1
- Create
-
1.2 Extract shared Ivanti API helpers into
backend/helpers/ivantiApi.js- Move the
ivantiPost()function fromivantiWorkflows.jsinto a shared module - Add
ivantiMultipartPost(urlPath, fileBuffer, fileName, apiKey, skipTls)for attachment uploads using Node.jshttpsmodule with multipart/form-data boundary construction - Export both functions; update
ivantiWorkflows.jsandivantiFindings.jsto import from the shared module - Requirements: 4.1, 4.2
- Move the
-
-
2. Backend route — validation and payload construction
-
2.1 Create
backend/routes/ivantiFpWorkflow.jswith validation and payload builder- Export
createIvantiFpWorkflowRouter(db, requireAuth)factory function - Implement
POST /route withrequireAuth(db)andrequireGroup('Admin', 'Standard_User')middleware - Configure Multer for up to 10 file uploads, 10MB each, with allowed extensions: .pdf, .png, .jpg, .jpeg, .gif, .doc, .docx, .xlsx, .csv, .txt, .zip
- Implement
validateFpWorkflowForm(body)— returns error map for invalid fields (name required max 255, reason required, description max 2000, expirationDate required and must be future date) - Implement
buildIvantiPayload(formData, findingIds)— constructs the Ivanti API request body with type "FALSE_POSITIVE", scopeOverrideAuthorization mapping, and hostFindingIds as integers - Implement
isAllowedFileExtension(filename)— checks against the allowed extensions list (case-insensitive) - Verify all queueItemIds belong to the requesting user, are FP-type, and have pending status
- Requirements: 2.4, 2.5, 3.3, 3.4, 3.5, 4.1, 7.1
- Export
-
* 2.2 Write property tests for validation and payload construction
- Property 3: Form Validation Correctness — For any form state, validation passes iff all required fields present and expiration date is future; error map keys match invalid fields only
- Property 4: File Extension Validation — For any filename, acceptance returns true iff extension is in the allowed set (case-insensitive)
- Property 5: API Payload Construction — For any valid form input, the constructed payload contains correct type, name, reason, expirationDate, scopeOverrideAuthorization, and hostFindingIds as integers
- Use
fast-checklibrary with minimum 100 iterations per property - Validates: Requirements 2.4, 2.5, 3.3, 4.1
-
-
3. Backend route — Ivanti API submission and local persistence
-
3.1 Implement the submission flow in
ivantiFpWorkflow.js- Call Ivanti API
POST /client/{clientId}/workflowBatchto create the FP workflow batch - If attachments present, upload each via
ivantiMultipartPost()to/client/{clientId}/workflowBatch/{id}/attachment - Handle Ivanti API error responses: 401 (invalid key), 419 (insufficient privileges), 429 (rate limited), other errors
- On success: insert submission record into
ivanti_fp_submissions, calllogAudit()with action "ivanti_fp_workflow_created" - On failure: call
logAudit()with action "ivanti_fp_workflow_failed" - Mark associated queue items as complete via
UPDATE ivanti_todo_queue SET status='complete' - Handle partial failures (workflow created but attachment upload failed) — save with status "partial"
- Return structured response with workflowBatchId, generatedId, attachmentResults, queueItemsUpdated
- Requirements: 4.1, 4.2, 4.5, 4.6, 4.7, 4.8, 5.1, 6.1, 6.2, 6.3
- Call Ivanti API
-
* 3.2 Write property tests for queue item completion and submission persistence
- Property 6: Queue Items Marked Complete on Success — For any set of queue item IDs after successful submission, all items have status "complete"
- Property 7: Post-Submission Persistence Completeness — For any successful submission, the record contains all required fields (ivanti_workflow_batch_id, workflow_name, user_id, finding_ids_json, created_at) and audit entry has correct action/entity_type/details
- Use in-memory SQLite for test isolation
- Validates: Requirements 5.1, 6.1, 6.2
-
-
4. Wire backend route into server.js
- 4.1 Register the new route in
backend/server.js- Add
const createIvantiFpWorkflowRouter = require('./routes/ivantiFpWorkflow'); - Mount at
app.use('/api/ivanti/fp-workflow', createIvantiFpWorkflowRouter(db, requireAuth)); - Place near the existing Ivanti route registrations
- Requirements: 7.1
- Add
- 4.1 Register the new route in
-
5. Checkpoint — Backend complete
- Ensure all tests pass, ask the user if questions arise.
-
6. Frontend — FP Workflow Modal component
-
6.1 Implement
FpWorkflowModalinfrontend/src/components/pages/ReportingPage.js- Add the modal component inline in ReportingPage.js following the existing pattern (QueuePanel, AddToQueuePopover are in the same file)
- Props: open, onClose, selectedItems (FP queue items), onSuccess
- Form fields: workflow name (text input, required), reason (textarea, required), description (textarea, optional), expiration date (date input, required), scope override toggle (Authorized/None, default Authorized)
- Display selected findings summary: finding_id, finding_title, CVEs for each item
- File upload area: drag-and-drop zone, file list with name/size/remove button, validate extensions and 10MB limit client-side
- Submit button with progress indicator (creating workflow → uploading attachment N of M)
- Error display: inline validation errors, API error messages with form state preservation
- Success display: workflow batch ID (e.g., "FP#12345") with close/done action
- Style with inline style objects matching the dark tactical theme from DESIGN_SYSTEM.md
- Icons from lucide-react (Upload, FileText, X, Check, AlertTriangle, Loader)
- Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 3.1, 3.2, 3.3, 3.4, 3.5, 4.3, 4.4, 4.7, 4.8
-
* 6.2 Write property tests for frontend validation helpers
- Property 1: FP Workflow Button Enabled State — For any set of queue items and selection, button enabled iff selection contains at least one pending FP item
- Property 2: FP-Only Item Filtering — For any mixed-type selection, filtered result contains only FP items
- Property 8: Role-Based UI Visibility — For any user role, button visible iff role is editor or admin
- Extract
isCreateFpButtonEnabled,filterFpItems,shouldShowFpButtonas testable pure functions - Use
fast-checkwith minimum 100 iterations - Validates: Requirements 1.1, 1.2, 7.2
-
-
7. Frontend — QueuePanel integration
- 7.1 Add "Create FP Workflow" button and modal wiring in QueuePanel
- Add "Create FP Workflow" button in QueuePanel footer, styled with amber/FP accent color
- Button enabled only when selectedIds contains at least one pending FP-type item
- Disabled state shows tooltip: "Select pending FP items to create a workflow"
- Hide button entirely for viewer role users (check via useAuth context)
- On click: filter selected items to FP-only, open FpWorkflowModal with filtered items
- Wire onSuccess callback to trigger queue refresh (call existing fetch function from parent)
- Requirements: 1.1, 1.2, 1.3, 1.4, 5.2, 7.2, 7.3
- 7.1 Add "Create FP Workflow" button and modal wiring in QueuePanel
-
8. Final checkpoint — Full integration
- Ensure all tests pass, ask the user if questions arise.
Notes
- Tasks marked with
*are optional and can be skipped for faster MVP - Each task references specific requirements for traceability
- Property tests use
fast-checklibrary — install vianpm install --save-dev fast-checkin both backend and frontend - The shared Ivanti API helper (task 1.2) updates existing imports in ivantiWorkflows.js and ivantiFindings.js — test those routes still work after the refactor
- Multer is already a project dependency (used for document uploads in server.js)