diff --git a/backend/routes/ivantiFpWorkflow.js b/backend/routes/ivantiFpWorkflow.js index a3b32f4..b2180b9 100644 --- a/backend/routes/ivantiFpWorkflow.js +++ b/backend/routes/ivantiFpWorkflow.js @@ -1004,27 +1004,53 @@ function createIvantiFpWorkflowRouter() { let queueItemIds = []; try { queueItemIds = JSON.parse(submission.queue_item_ids_json || '[]'); - } catch (e) { - return res.status(400).json({ error: 'Could not parse queue_item_ids_json.' }); + } catch (e) { /* ignore parse errors */ } + + // Parse finding IDs (always available, even for submissions created outside dashboard) + let findingIds = []; + try { + findingIds = JSON.parse(submission.finding_ids_json || '[]'); + } catch (e) { /* ignore */ } + + if ((!Array.isArray(queueItemIds) || queueItemIds.length === 0) && + (!Array.isArray(findingIds) || findingIds.length === 0)) { + return res.status(400).json({ error: 'No findings associated with this submission.' }); } - if (!Array.isArray(queueItemIds) || queueItemIds.length === 0) { - return res.status(400).json({ error: 'No queue items associated with this submission.' }); + // Fetch original queue items to get finding data (if they still exist) + let findingsToQueue = []; + if (queueItemIds.length > 0) { + const { rows: originalItems } = await pool.query( + `SELECT finding_id, finding_title, cves_json, ip_address, hostname FROM ivanti_todo_queue WHERE id = ANY($1)`, + [queueItemIds] + ); + findingsToQueue = originalItems; } - // Fetch original queue items to get finding data - const { rows: originalItems } = await pool.query( - `SELECT finding_id, finding_title, cves_json, ip_address, hostname FROM ivanti_todo_queue WHERE id = ANY($1)`, - [queueItemIds] - ); + // Fallback: if original queue items were deleted or never existed, + // use finding_ids_json to look up finding data from ivanti_findings + if (findingsToQueue.length === 0 && findingIds.length > 0) { + const { rows: findings } = await pool.query( + `SELECT id AS finding_id, title AS finding_title, cves AS cves_json, ip_address, host_name AS hostname FROM ivanti_findings WHERE id = ANY($1)`, + [findingIds.map(String)] + ); + findingsToQueue = findings; - if (originalItems.length === 0) { - return res.status(400).json({ error: 'No original queue items found for this submission.' }); + // Last resort: create items with just the finding IDs (minimal data) + if (findingsToQueue.length === 0) { + findingsToQueue = findingIds.map(id => ({ + finding_id: String(id), + finding_title: null, + cves_json: null, + ip_address: null, + hostname: null, + })); + } } // INSERT new pending queue items for each finding const newItems = []; - for (const item of originalItems) { + for (const item of findingsToQueue) { const { rows: inserted } = await pool.query( `INSERT INTO ivanti_todo_queue (user_id, finding_id, finding_title, cves_json, ip_address, hostname, vendor, workflow_type) @@ -1042,12 +1068,12 @@ function createIvantiFpWorkflowRouter() { ); // Audit log (fire-and-forget) - const finding_ids = originalItems.map(i => i.finding_id).filter(Boolean); + const auditFindingIds = findingsToQueue.map(i => i.finding_id).filter(Boolean); logAudit({ userId: req.user.id, username: req.user.username, action: 'fp_submission_requeued', entityType: 'ivanti_fp_submissions', entityId: String(submission.id), - details: { target_workflow_type: workflow_type, items_created: newItems.length, finding_ids }, + details: { target_workflow_type: workflow_type, items_created: newItems.length, finding_ids: auditFindingIds }, ipAddress: req.ip });