/** * MAGATAMA S2 TEN BGP Enrichment Task * * Enriches MAGATAMA S2 TEN (Cloud) findings with: * 1. BGP status (is the IP/prefix announced?) * 2. RPKI validity (is the announcing ASN authorized?) * 3. Threat intelligence (known malicious IP or ASN?) * * This bridges PeerCortex local intelligence to MAGATAMA security findings. * Called when S2 TEN detects anomalous external traffic. */ const pg = require('pg'); // Initialize PostgreSQL connection pool const pool = new pg.Pool({ host: process.env.DB_HOST || '192.168.178.82', port: parseInt(process.env.DB_PORT || '5432'), database: process.env.DB_NAME || 'llm_gateway', user: process.env.DB_USER || 'llm', password: process.env.DB_PASSWORD || 'llm_secure_2026', max: 10, idleTimeoutMillis: 10000, connectionTimeoutMillis: 5000, }); /** * Get BGP status for an IP address */ async function getBgpStatus(ipAddress) { const client = await pool.connect(); try { const query = ` SELECT prefix, origin_asn, ARRAY_AGG(DISTINCT origin_asn) as origin_asns, visibility_percent, last_seen FROM bgp_routes WHERE prefix >> $1::inet ORDER BY prefix DESC LIMIT 1 `; const result = await client.query(query, [ipAddress]); return result.rows.length > 0 ? result.rows[0] : null; } finally { client.release(); } } /** * Validate RPKI for a prefix + origin ASN pair */ async function validateRpki(prefix, originAsn) { const client = await pool.connect(); try { const query = ` SELECT status, details FROM rpki_roas WHERE prefix >> $1::inet AND origin_asn = $2 LIMIT 1 `; const result = await client.query(query, [prefix, originAsn]); if (result.rows.length > 0) { return { status: result.rows[0].status || 'valid', details: result.rows[0].details, }; } // No ROA found = not-found return { status: 'not-found' }; } finally { client.release(); } } /** * Get threat intelligence for an IP address */ async function getThreatIntel(ipAddress) { const client = await pool.connect(); try { const query = ` SELECT ip_address, threat_level, confidence_score, source, cached_at FROM threat_intel WHERE ip_address = $1::inet LIMIT 1 `; const result = await client.query(query, [ipAddress]); return result.rows.length > 0 ? result.rows[0] : null; } finally { client.release(); } } /** * Determine kill-chain phase based on BGP + RPKI + Threat signals */ function determineKillChainPhase(enriched) { const flags = enriched.security_flags || []; if (flags.some(f => f.flag === 'RPKI_INVALID')) { return 'WEAPONIZATION'; // BGP hijack = active attack infrastructure setup } if (flags.some(f => f.flag === 'MALICIOUS_IP')) { return 'DELIVERY'; // Known malicious IP attempting connection } if (enriched.bgp_intel.announced === false) { return 'EXPLOITATION'; // Unknown IP not in routing table = potentially spoofed } return 'RECONNAISSANCE'; // Announced legitimate IP but with anomalous traffic pattern } /** * Map to MITRE ATT&CK framework */ function mapToMitreAttack(enriched) { const mapping = []; if (enriched.bgp_intel.rpki_valid === false) { mapping.push({ technique: 'T1040', // Network Sniffing (implicit in BGP hijack) tactic: 'Credential Access / Collection', description: 'BGP RPKI validation failed - possible route hijack', }); } if (enriched.threat_intel?.threat_level === 'CRITICAL') { mapping.push({ technique: 'T1566', // Phishing (network variant) tactic: 'Initial Access', description: 'Connection from known malicious IP', }); } return mapping; } /** * Enrich a finding with BGP + RPKI + Threat data * @param {Object} finding - MAGATAMA finding { ip_address, port, protocol, ... } * @returns {Object} Enriched finding with bgp_status, rpki_valid, threat_level */ async function enrichFindingWithBGPIntel(finding) { const enriched = { ...finding, bgp_intel: {} }; const ipAddress = finding.ip_address; if (!ipAddress) { console.log('[S2TEN Enrichment] No IP address in finding, skipping enrichment'); return enriched; } try { // ---- Step 1: BGP Status Lookup ---- // Check if this IP is part of an announced prefix in our local BGP routes const bgpStatus = await getBgpStatus(ipAddress); if (bgpStatus) { enriched.bgp_intel.announced = true; enriched.bgp_intel.prefix = bgpStatus.prefix; enriched.bgp_intel.origin_asn = bgpStatus.origin_asns ? bgpStatus.origin_asns[0] : null; enriched.bgp_intel.origin_asns = bgpStatus.origin_asns || []; enriched.bgp_intel.visibility_percent = bgpStatus.visibility_percent; enriched.bgp_intel.last_seen = bgpStatus.last_seen; // ---- Step 2: RPKI Validity Check ---- // If we know the origin ASN, validate RPKI if (enriched.bgp_intel.origin_asn) { try { const rpkiResult = await validateRpki( bgpStatus.prefix || ipAddress, enriched.bgp_intel.origin_asn ); enriched.bgp_intel.rpki_status = rpkiResult.status; enriched.bgp_intel.rpki_valid = rpkiResult.status === 'valid'; // Alert if RPKI is INVALID (potential hijack!) if (rpkiResult.status === 'invalid') { enriched.security_flags = enriched.security_flags || []; enriched.security_flags.push({ flag: 'RPKI_INVALID', severity: 'CRITICAL', message: `RPKI validation failed: AS${enriched.bgp_intel.origin_asn} is not authorized to announce this prefix`, }); } } catch (e) { console.error(`[S2TEN] RPKI check failed for ${ipAddress}:`, e.message); enriched.bgp_intel.rpki_status = 'unknown'; } } } else { enriched.bgp_intel.announced = false; enriched.bgp_intel.message = 'IP not found in BGP routing table'; } // ---- Step 3: Threat Intelligence Lookup ---- // Check if this IP or its origin ASN is in threat_intel table const threatIntel = await getThreatIntel(ipAddress); if (threatIntel) { enriched.threat_intel = { ip_address: threatIntel.ip_address, threat_level: threatIntel.threat_level, confidence_score: threatIntel.confidence_score, source: threatIntel.source, cached_at: threatIntel.cached_at, }; // Alert if threat level is HIGH or CRITICAL if (['HIGH', 'CRITICAL'].includes(threatIntel.threat_level)) { enriched.security_flags = enriched.security_flags || []; enriched.security_flags.push({ flag: 'MALICIOUS_IP', severity: threatIntel.threat_level, message: `Known malicious IP: threat level ${threatIntel.threat_level} (confidence: ${threatIntel.confidence_score}%)`, source: threatIntel.source, }); } } // ---- Step 4: MAGATAMA Kill-Chain Correlation ---- // Map BGP + RPKI + Threat findings to MITRE ATT&CK kill chain enriched.kill_chain_phase = determineKillChainPhase(enriched); enriched.mitre_attack_mapping = mapToMitreAttack(enriched); return enriched; } catch (e) { console.error(`[S2TEN Enrichment] Fatal error enriching ${ipAddress}:`, e.message); enriched.enrichment_error = e.message; return enriched; } } /** * Batch enrich multiple findings */ async function enrichFindings(findings) { console.log(`[S2TEN Enrichment] Enriching ${findings.length} findings...`); const enriched = []; for (const finding of findings) { try { const result = await enrichFindingWithBGPIntel(finding); enriched.push(result); } catch (e) { console.error(`[S2TEN Enrichment] Error enriching finding:`, e.message); enriched.push({ ...finding, enrichment_error: e.message }); } } return enriched; } module.exports = { enrichFindingWithBGPIntel, enrichFindings, determineKillChainPhase, mapToMitreAttack, pool, getBgpStatus, validateRpki, getThreatIntel, };