10 KiB
Design Document: Ivanti Queue Redirect
Overview
The Ivanti Queue Redirect feature adds an optional redirect action to completed queue items, allowing users to create a new pending queue item under a different workflow type from an existing completed item. This supports the common scenario where a CARD inventory fix is done but the finding still needs FP or Archer processing, or where an item was assigned to the wrong workflow initially.
The feature consists of three parts:
- A new backend API endpoint (
POST /api/ivanti/todo-queue/:id/redirect) added to the existingivantiTodoQueue.jsroute module - A redirect modal component in the frontend for collecting target workflow type and vendor
- A redirect button on completed queue items in the existing QueuePanel
The redirect operation creates a new row in ivanti_todo_queue — it does not modify or delete the original completed item. This preserves the audit trail and allows the original item to remain visible as completed.
Architecture
The feature follows the existing patterns in the codebase:
sequenceDiagram
participant U as User
participant QP as QueuePanel
participant RM as RedirectModal
participant API as POST /todo-queue/:id/redirect
participant DB as SQLite (ivanti_todo_queue)
participant AL as Audit Log
U->>QP: Clicks redirect button on completed item
QP->>RM: Opens modal with item context
U->>RM: Selects target workflow type + vendor
RM->>API: POST /api/ivanti/todo-queue/:id/redirect
API->>DB: SELECT original item (verify ownership + complete status)
API->>DB: INSERT new pending item with target workflow_type
API->>AL: logAudit (fire-and-forget)
API-->>RM: 201 + new item JSON
RM->>QP: Adds new item to list, closes modal, shows success
No new database tables or schema changes are required. The redirect creates a standard ivanti_todo_queue row using the existing schema. The only backend change outside the new endpoint is fixing the PUT validation message (Requirement 5).
Components and Interfaces
Backend: Redirect Endpoint
Added to backend/routes/ivantiTodoQueue.js inside the existing createIvantiTodoQueueRouter factory function.
POST /api/ivanti/todo-queue/:id/redirect
Request body:
{
"workflow_type": "FP" | "Archer" | "CARD",
"vendor": "string (required for FP/Archer, omitted for CARD)"
}
Success response (201):
{
"id": 42,
"user_id": 1,
"finding_id": "12345",
"finding_title": "...",
"cves_json": "[...]",
"ip_address": "10.0.0.1",
"hostname": "host.example.com",
"vendor": "Cisco",
"workflow_type": "FP",
"status": "pending",
"created_at": "...",
"updated_at": "...",
"cves": ["CVE-2024-1234"]
}
Error responses:
| Status | Condition |
|---|---|
| 400 | Item not in "complete" status |
| 400 | Invalid workflow_type |
| 400 | Missing/invalid vendor for FP/Archer |
| 404 | Item not found or belongs to different user |
| 500 | Database error |
Auth: requireAuth(db), requireGroup('Admin', 'Standard_User')
Backend: PUT Validation Fix
In the existing PUT /:id handler, the error message for invalid workflow_type currently says "workflow_type must be FP or Archer." — this will be updated to "workflow_type must be FP, Archer, or CARD.".
Frontend: RedirectModal Component
A new modal component rendered inside the QueuePanel. It receives the item being redirected and collects:
- Target workflow type (radio buttons or select: FP, Archer, CARD)
- Vendor (text input, shown only when FP or Archer is selected)
The modal displays read-only context: finding title, finding ID, and current workflow type.
Props:
{
item: Object, // The completed queue item being redirected
onClose: Function, // Close the modal
onRedirect: Function // Callback with the new item after successful redirect
}
Frontend: QueuePanel Changes
- Add a redirect button (e.g.,
CornerUpRightorArrowRightLefticon from lucide-react) on each completed item row, next to the existing delete button - Track
redirectItemstate — when set, render the RedirectModal - On successful redirect, append the new item to the queue items list
Frontend: API Call
const res = await fetch(`${API_BASE}/ivanti/todo-queue/${itemId}/redirect`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow_type, vendor })
});
Data Models
No schema changes. The redirect creates a standard ivanti_todo_queue row:
| Column | Source |
|---|---|
| user_id | req.user.id (current user) |
| finding_id | Copied from original item |
| finding_title | Copied from original item |
| cves_json | Copied from original item |
| ip_address | Copied from original item |
| hostname | Copied from original item |
| vendor | From request body (FP/Archer) or empty string (CARD) |
| workflow_type | From request body |
| status | 'pending' (always) |
The original completed item remains unchanged.
Correctness Properties
A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.
Property 1: Redirect preserves finding data
For any completed queue item with arbitrary finding_id, finding_title, cves_json, ip_address, and hostname values, and for any valid target workflow type, redirecting that item SHALL produce a new queue item where finding_id, finding_title, cves_json, ip_address, and hostname are identical to the original, status is "pending", and workflow_type matches the requested target.
Validates: Requirements 1.1, 1.7
Property 2: Vendor requirement is conditional on workflow type
For any redirect request, if the target workflow_type is "FP" or "Archer", the request SHALL be accepted if and only if vendor is a non-empty string of 200 characters or fewer. If the target workflow_type is "CARD", the request SHALL be accepted regardless of whether vendor is provided.
Validates: Requirements 1.2, 1.3
Property 3: Successful redirect produces correct audit entry
For any successful redirect operation, the audit log entry SHALL contain action "queue_item_redirected", entityType "ivanti_todo_queue", the original item's ID as entityId, and details including the original workflow_type, the target workflow_type, the new item's ID, and the vendor.
Validates: Requirements 2.1, 2.2
Error Handling
| Scenario | HTTP Status | Error Message | Behavior |
|---|---|---|---|
| Item not found or belongs to another user | 404 | "Queue item not found." | Consistent with existing DELETE/PUT pattern |
| Item status is not "complete" | 400 | "Only completed queue items can be redirected." | Prevents redirecting pending items |
| Invalid workflow_type | 400 | "workflow_type must be FP, Archer, or CARD." | Same message as batch/single add |
| Missing/invalid vendor for FP/Archer | 400 | "vendor is required for FP and Archer workflows." | Same message as existing endpoints |
| Vendor exceeds 200 chars | 400 | "vendor must be under 200 chars." | Same message as existing endpoints |
| Database insert failure | 500 | "Internal server error." | Consistent with existing error pattern |
| Frontend API error | — | Display error message from API in modal | Modal stays open so user can retry or cancel |
The redirect endpoint reuses the existing isValidVendor() helper and VALID_WORKFLOW_TYPES constant from ivantiTodoQueue.js for consistent validation.
Audit logging uses the existing fire-and-forget pattern — a failed audit log write does not block or fail the redirect response.
Testing Strategy
Unit Tests (Example-Based)
Backend:
- Redirect a completed CARD item to FP with vendor → 201, new item returned
- Redirect a completed FP item to CARD without vendor → 201, new item returned
- Redirect a pending item → 400
- Redirect another user's item → 404
- Redirect with invalid workflow_type → 400
- Redirect to FP without vendor → 400
- Redirect to FP with vendor > 200 chars → 400
- Redirect non-existent item → 404
- PUT with invalid workflow_type returns updated error message text
- Verify audit log is called with correct fields on successful redirect
Frontend:
- Redirect button visible on completed items, hidden on pending items
- Clicking redirect button opens modal with correct item context
- Modal shows vendor field for FP/Archer, hides for CARD
- Modal displays finding title, finding ID, current workflow type
- Successful redirect closes modal, adds new item to list, shows notification
- Failed redirect shows error message, modal stays open
Property-Based Tests
Library: fast-check (JavaScript property-based testing library)
Each property test runs a minimum of 100 iterations.
-
Property 1: Generate random queue item data (finding_id, finding_title, cves_json, ip_address, hostname with varying lengths, special characters, null optionals) and random valid workflow_type. Mock the database layer. Verify the INSERT parameters preserve all finding fields and set status to "pending".
- Tag:
Feature: ivanti-queue-redirect, Property 1: Redirect preserves finding data
- Tag:
-
Property 2: Generate random (workflow_type, vendor) pairs where workflow_type is drawn from valid types and vendor is drawn from a mix of valid strings, empty strings, whitespace, strings of length 200, and strings of length 201. Verify that the validation logic accepts/rejects correctly based on the conditional rule.
- Tag:
Feature: ivanti-queue-redirect, Property 2: Vendor requirement is conditional on workflow type
- Tag:
-
Property 3: Generate random successful redirect scenarios with varying item data and workflow types. Mock logAudit. Verify the audit call contains the correct action, entityType, entityId, and all required detail fields.
- Tag:
Feature: ivanti-queue-redirect, Property 3: Successful redirect produces correct audit entry
- Tag: