const localDb = require("../../../local-db-client"); const { fetchRipeStatCached } = require("../../services/ripe-stat"); // Resolve empty AS names — all in parallel, with 3s timeout. Mutates entries // in place (matches original control flow). async function resolveEmptyNames(neighbourLists) { const emptyNameNeighbours = neighbourLists.flat().filter(n => !n.name); if (emptyNameNeighbours.length === 0) return; const resolvePromise = Promise.all( emptyNameNeighbours.map(n => fetchRipeStatCached("https://stat.ripe.net/data/as-overview/data.json?resource=AS" + n.asn, { timeout: 3000 }) .then(r => { if (r?.data?.holder) n.name = r.data.holder; }) .catch(() => {}) ) ); await Promise.race([resolvePromise, new Promise(r => setTimeout(r, 3000))]); } // Threat Intelligence Enrichment for Neighbors: enrich neighbor data with // threat status from the local threat_intel table. Batch lookups (cap at 50 // to avoid overwhelming DB), 4s timeout. async function buildThreatMap(neighbors) { const threatMap = {}; const toCheck = neighbors.slice(0, 50); const threatPromises = toCheck.map(async (n) => { try { const asNum = String(n.asn); const threat = await localDb.getThreatIntel(asNum); if (threat) { threatMap[n.asn] = { threat_level: threat.threat_level, confidence_score: threat.confidence_score, source: threat.source, cached_at: threat.cached_at, }; } } catch (e) { // Gracefully skip on error console.error(`[Threat Lookup] Error checking ASN ${n.asn}:`, e.message); } }); await Promise.race([ Promise.all(threatPromises), new Promise(r => setTimeout(r, 4000)), ]); return threatMap; } function attachThreat(n, threatMap) { return { ...n, threat_level: threatMap[n.asn]?.threat_level || null, threat_confidence: threatMap[n.asn]?.confidence_score || null, threat_source: threatMap[n.asn]?.source || null, }; } // Full enrichment pipeline: resolve empty names, then attach threat intel to // upstreams/downstreams/peers. Returns new arrays (does not mutate the input // arrays themselves, though resolveEmptyNames does mutate entry.name). async function enrichNeighbours(upstreams, downstreams, peers) { await resolveEmptyNames([upstreams, downstreams, peers]); const threatMap = await buildThreatMap([...upstreams, ...downstreams, ...peers]); return { upstreams: upstreams.map(n => attachThreat(n, threatMap)), downstreams: downstreams.map(n => attachThreat(n, threatMap)), peers: peers.map(n => attachThreat(n, threatMap)), }; } module.exports = { enrichNeighbours };