From 835fbf26e7f5e4e4454d5c8a622de8c7fe38a305 Mon Sep 17 00:00:00 2001 From: jramos Date: Mon, 13 Apr 2026 12:39:47 -0600 Subject: [PATCH] fix: revert clickable workflow badges, fix migration default, auto-sync submission lifecycle status from Ivanti findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Revert workflow badge to static (non-clickable) — queue panel is the entry point - Fix migration: use DEFAULT NULL for updated_at (SQLite disallows CURRENT_TIMESTAMP in ALTER TABLE) - Add useMemo enrichment to cross-reference submission lifecycle_status with actual Ivanti workflow state from findings data --- .../src/components/pages/ReportingPage.js | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/pages/ReportingPage.js b/frontend/src/components/pages/ReportingPage.js index 3acf5cc..8a4800e 100644 --- a/frontend/src/components/pages/ReportingPage.js +++ b/frontend/src/components/pages/ReportingPage.js @@ -1061,42 +1061,22 @@ function TableCell({ colKey, finding, canWrite, onCveMouseEnter, onCveMouseLeave const wf = finding.workflow; if (!wf || !wf.id) return —; const ws = workflowStyle(wf.state); - const isEditable = ['reworked', 'rejected', 'expired'].includes((wf.state || '').toLowerCase()); - - const handleBadgeClick = isEditable ? () => { - const numericId = parseInt(String(wf.id).replace(/\D/g, ''), 10); - const sub = fpSubmissions.find(s => s.ivanti_workflow_batch_id === numericId); - if (sub) onEditSubmission(sub); - } : undefined; - return ( { - e.currentTarget.style.borderColor = ws.text; - e.currentTarget.style.background = ws.bg.replace('0.12', '0.2'); - } : undefined} - onMouseLeave={isEditable ? (e) => { - e.currentTarget.style.borderColor = ws.border; - e.currentTarget.style.background = ws.bg; - } : undefined} > {wf.id} {wf.state} - {isEditable && } ); @@ -3159,9 +3139,33 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) { const [fpModalItems, setFpModalItems] = useState([]); // FP Submission editing state - const [fpSubmissions, setFpSubmissions] = useState([]); + const [fpSubmissionsRaw, setFpSubmissions] = useState([]); const [editSubmission, setEditSubmission] = useState(null); + // Enrich submissions with actual Ivanti workflow state from findings data + const fpSubmissions = useMemo(() => { + if (fpSubmissionsRaw.length === 0 || findings.length === 0) return fpSubmissionsRaw; + const stateMap = { + 'reworked': 'rework', 'rejected': 'rejected', 'expired': 'rejected', + 'approved': 'approved', 'requested': 'submitted', 'actionable': 'submitted', + }; + return fpSubmissionsRaw.map(sub => { + let findingIds; + try { findingIds = JSON.parse(sub.finding_ids_json || '[]'); } catch { return sub; } + if (findingIds.length === 0) return sub; + const matchedFinding = findings.find(f => + f.workflow && findingIds.includes(String(f.id)) + ); + if (!matchedFinding || !matchedFinding.workflow) return sub; + const ivantiState = (matchedFinding.workflow.state || '').toLowerCase(); + const mappedStatus = stateMap[ivantiState]; + if (mappedStatus && mappedStatus !== sub.lifecycle_status) { + return { ...sub, lifecycle_status: mappedStatus }; + } + return sub; + }); + }, [fpSubmissionsRaw, findings]); + // Queue API helpers const fetchQueue = useCallback(async () => { setQueueLoading(true);