110 lines
8.8 KiB
Markdown
110 lines
8.8 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
- [x] 1. Database migration and shared helpers
|
||
|
|
- [x] 1.1 Create migration script `backend/migrations/add_fp_submissions_table.js`
|
||
|
|
- Create `ivanti_fp_submissions` table 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_
|
||
|
|
|
||
|
|
- [x] 1.2 Extract shared Ivanti API helpers into `backend/helpers/ivantiApi.js`
|
||
|
|
- Move the `ivantiPost()` function from `ivantiWorkflows.js` into a shared module
|
||
|
|
- Add `ivantiMultipartPost(urlPath, fileBuffer, fileName, apiKey, skipTls)` for attachment uploads using Node.js `https` module with multipart/form-data boundary construction
|
||
|
|
- Export both functions; update `ivantiWorkflows.js` and `ivantiFindings.js` to import from the shared module
|
||
|
|
- _Requirements: 4.1, 4.2_
|
||
|
|
|
||
|
|
- [x] 2. Backend route — validation and payload construction
|
||
|
|
- [x] 2.1 Create `backend/routes/ivantiFpWorkflow.js` with validation and payload builder
|
||
|
|
- Export `createIvantiFpWorkflowRouter(db, requireAuth)` factory function
|
||
|
|
- Implement `POST /` route with `requireAuth(db)` and `requireGroup('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_
|
||
|
|
|
||
|
|
- [ ]* 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-check` library with minimum 100 iterations per property
|
||
|
|
- **Validates: Requirements 2.4, 2.5, 3.3, 4.1**
|
||
|
|
|
||
|
|
- [x] 3. Backend route — Ivanti API submission and local persistence
|
||
|
|
- [x] 3.1 Implement the submission flow in `ivantiFpWorkflow.js`
|
||
|
|
- Call Ivanti API `POST /client/{clientId}/workflowBatch` to 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`, call `logAudit()` 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_
|
||
|
|
|
||
|
|
- [ ]* 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**
|
||
|
|
|
||
|
|
- [x] 4. Wire backend route into server.js
|
||
|
|
- [x] 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_
|
||
|
|
|
||
|
|
- [x] 5. Checkpoint — Backend complete
|
||
|
|
- Ensure all tests pass, ask the user if questions arise.
|
||
|
|
|
||
|
|
- [x] 6. Frontend — FP Workflow Modal component
|
||
|
|
- [x] 6.1 Implement `FpWorkflowModal` in `frontend/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`, `shouldShowFpButton` as testable pure functions
|
||
|
|
- Use `fast-check` with minimum 100 iterations
|
||
|
|
- **Validates: Requirements 1.1, 1.2, 7.2**
|
||
|
|
|
||
|
|
- [x] 7. Frontend — QueuePanel integration
|
||
|
|
- [x] 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_
|
||
|
|
|
||
|
|
- [x] 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-check` library — install via `npm install --save-dev fast-check` in 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)
|