Add CARD asset-search by Ivanti Host ID for faster lookups

Integrate CARD's new v2 asset-search endpoint that accepts Ivanti Asset ID
integers directly, eliminating the slow suffix-guessing resolution flow.

Changes:
- Add searchByIvantiHostId() helper to cardApi.js
- Add GET /api/card/asset-search/:hostId endpoint
- Update CARD queue confirm/decline/redirect to try host_id fast path first
- Update owner-lookup to accept optional hostId query param for fast resolution
- Pass hostId through CardOwnerTooltip and ReportingPage for tooltip lookups
- Join ivanti_findings in todo queue GET to expose host_id on queue items
- Update CardActionModal to pass host_id for faster owner-lookup
This commit is contained in:
Jordan Ramos
2026-06-09 11:57:13 -06:00
parent 2396a828cc
commit a8d3909798
6 changed files with 188 additions and 22 deletions

View File

@@ -63,7 +63,7 @@ export default function CardActionModal({ isOpen, onClose, item, initialAction,
setOwnerData(null);
setExecError(null);
fetch(`${API_BASE}/card/owner-lookup/${encodeURIComponent(item.ip_address)}`, { credentials: 'include' })
fetch(`${API_BASE}/card/owner-lookup/${encodeURIComponent(item.ip_address)}${item.host_id ? '?hostId=' + encodeURIComponent(item.host_id) : ''}`, { credentials: 'include' })
.then(r => {
if (!r.ok) return r.json().then(d => { throw new Error(d.error || `HTTP ${r.status}`); });
return r.json();

View File

@@ -43,7 +43,7 @@ function calcPosition(anchorRect, tooltipHeight, viewportHeight) {
// ---------------------------------------------------------------------------
// Main exported component
// ---------------------------------------------------------------------------
export default function CardOwnerTooltip({ ip, anchorRect, cache, cardConfigured, onAction, onMouseEnter, onMouseLeave }) {
export default function CardOwnerTooltip({ ip, hostId, anchorRect, cache, cardConfigured, onAction, onMouseEnter, onMouseLeave }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
@@ -76,13 +76,14 @@ export default function CardOwnerTooltip({ ip, anchorRect, cache, cardConfigured
return;
}
// Fetch
// Fetch — include hostId for fast-path resolution when available
const controller = new AbortController();
setLoading(true);
setData(null);
setError(null);
fetch(`${API_BASE}/card/owner-lookup/${encodeURIComponent(ip)}?quick=1`, {
const hostIdParam = hostId ? `&hostId=${encodeURIComponent(hostId)}` : '';
fetch(`${API_BASE}/card/owner-lookup/${encodeURIComponent(ip)}?quick=1${hostIdParam}`, {
credentials: 'include',
signal: controller.signal,
})
@@ -123,7 +124,7 @@ export default function CardOwnerTooltip({ ip, anchorRect, cache, cardConfigured
});
return () => controller.abort();
}, [ip, cache, cardConfigured]);
}, [ip, hostId, cache, cardConfigured]);
if (!ip || !anchorRect) return null;
if (!loading && !data && !error) return null;

View File

@@ -1265,7 +1265,7 @@ function TableCell({ colKey, finding, canWrite, onCveMouseEnter, onCveMouseLeave
return (
<td
style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', color: '#94A3B8', fontFamily: 'monospace', fontSize: '0.72rem', cursor: finding.ipAddress ? 'help' : 'default' }}
onMouseEnter={onIpMouseEnter && finding.ipAddress ? (e) => onIpMouseEnter(finding.ipAddress, e) : undefined}
onMouseEnter={onIpMouseEnter && finding.ipAddress ? (e) => onIpMouseEnter(finding.ipAddress, e, finding.hostId) : undefined}
onMouseLeave={onIpMouseLeave || undefined}
>
{finding.ipAddress || '—'}
@@ -5937,6 +5937,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
// CARD owner tooltip state & refs
const [cardTooltipIp, setCardTooltipIp] = useState(null);
const [cardTooltipAnchorRect, setCardTooltipAnchorRect] = useState(null);
const [cardTooltipHostId, setCardTooltipHostId] = useState(null);
const cardTooltipCacheRef = useRef(new Map());
const cardHoverTimerRef = useRef(null);
@@ -6033,12 +6034,13 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
}, []);
// CARD owner tooltip hover handlers (with hover bridge — stays open when hovering tooltip)
const handleIpMouseEnter = useCallback((ip, e) => {
const handleIpMouseEnter = useCallback((ip, e, hostId) => {
if (!ip) return;
clearTimeout(cardHoverTimerRef.current);
cardHoverTimerRef.current = setTimeout(() => {
setCardTooltipIp(ip);
setCardTooltipAnchorRect(e.target.getBoundingClientRect());
setCardTooltipHostId(hostId || null);
}, 400);
}, []);
@@ -6048,6 +6050,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
cardHoverTimerRef.current = setTimeout(() => {
setCardTooltipIp(null);
setCardTooltipAnchorRect(null);
setCardTooltipHostId(null);
}, 150);
}, []);
@@ -6061,6 +6064,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
clearTimeout(cardHoverTimerRef.current);
setCardTooltipIp(null);
setCardTooltipAnchorRect(null);
setCardTooltipHostId(null);
}, []);
// CARD action — open CardActionModal from tooltip
@@ -6073,6 +6077,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
// Close the tooltip
setCardTooltipIp(null);
setCardTooltipAnchorRect(null);
setCardTooltipHostId(null);
}, []);
const applyState = (data) => {
@@ -7738,6 +7743,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
/>
<CardOwnerTooltip
ip={cardTooltipIp}
hostId={cardTooltipHostId}
anchorRect={cardTooltipAnchorRect}
cache={cardTooltipCacheRef}
cardConfigured={cardConfigured}