/** * Pure utility functions for consolidating multiple Ivanti queue items * into a single Jira ticket's summary, description, CVE, and vendor fields. */ /** * Generate a consolidated summary for a multi-item Jira ticket. * Format: "[N findings] vendor - first_finding_title", truncated to 255 chars. * * @param {Array} items - Array of queue item objects * @returns {string} Generated summary, at most 255 characters */ export function generateConsolidatedSummary(items) { const count = items.length; const vendors = [...new Set(items.map(i => i.vendor).filter(Boolean))]; const vendorLabel = vendors.length === 1 ? vendors[0] : 'Multiple Vendors'; const firstTitle = items[0]?.finding_title || 'Untitled'; const raw = `[${count} findings] ${vendorLabel} - ${firstTitle}`; return raw.slice(0, 255); } /** * Generate a structured description grouped by vendor for a consolidated Jira ticket. * * @param {Array} items - Array of queue item objects * @returns {string} Structured description with header and vendor-grouped items */ export function generateConsolidatedDescription(items) { const header = `Consolidated Jira ticket covering ${items.length} Ivanti queue findings.\n\n`; // Group by vendor const grouped = {}; for (const item of items) { const vendor = item.vendor || 'Unknown Vendor'; if (!grouped[vendor]) grouped[vendor] = []; grouped[vendor].push(item); } let body = ''; for (const [vendor, vendorItems] of Object.entries(grouped)) { body += `== ${vendor} ==\n`; for (const item of vendorItems) { let cves = 'None'; if (item.cves_json) { try { const parsed = JSON.parse(item.cves_json); if (Array.isArray(parsed) && parsed.length > 0) { cves = parsed.join(', '); } } catch (e) { cves = 'None'; } } body += `- ${item.finding_title}\n`; body += ` CVEs: ${cves}\n`; body += ` Host: ${item.hostname || 'N/A'} (${item.ip_address || 'N/A'})\n\n`; } } return header + body; } /** * Extract the first CVE from the first item that has a non-empty cves_json array. * * @param {Array} items - Array of queue item objects * @returns {string} First CVE ID found, or empty string if none */ export function extractFirstCve(items) { for (const item of items) { if (item.cves_json) { try { const cves = JSON.parse(item.cves_json); if (Array.isArray(cves) && cves.length > 0) return cves[0]; } catch (e) { // Skip items with invalid JSON } } } return ''; } /** * Extract the common vendor if all items share the same vendor. * * @param {Array} items - Array of queue item objects * @returns {string} Common vendor name if all items share it, empty string otherwise */ export function extractCommonVendor(items) { const vendors = [...new Set(items.map(i => i.vendor).filter(Boolean))]; return vendors.length === 1 ? vendors[0] : ''; }