// ===================================================== // SentinelAI - Real API Service (Operational Mode) // Free public APIs: ipinfo.io, NVD CVE, Google DNS // ===================================================== const SentinelAPI = { // ---- IP Geolocation via ipinfo.io (free, no key) ---- async lookupIP(query) { try { const isIP = /^(\d{1,3}\.){3}\d{1,3}/.test(query); const url = query.trim() ? `https://ipinfo.io/${query}/json` : `https://ipinfo.io/json`; const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); if (data.bogon) return { error: 'Private/bogon IP', ip: query }; const [lat, lng] = (data.loc || '0,0').split(','); return { ip: data.ip, hostname: data.hostname || query, city: data.city || 'Unknown', region: data.region || 'Unknown', country: data.country || 'Unknown', loc: data.loc || '0,0', lat: parseFloat(lat) || 0, lng: parseFloat(lng) || 0, org: data.org || 'Unknown', timezone: data.timezone || 'Unknown', asn: data.asn || {}, }; } catch (e) { console.error('[SentinelAPI] ipinfo.io error:', e); return { error: e.message, ip: query }; } }, // ---- NVD CVE Search (free, public) ---- async searchCVEs(keyword, limit = 20) { try { const url = `https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=${encodeURIComponent(keyword)}&resultsPerPage=${limit}`; const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); return (data.vulnerabilities || []).map(v => { const cve = v.cve; const descriptions = cve.descriptions || []; const desc = descriptions.find(d => d.lang === 'en')?.value || 'No description'; const metrics = cve.metrics || {}; const cvssV31 = metrics.cvssMetricV31?.[0]?.cvssData; const cvssV2 = metrics.cvssMetricV2?.[0]?.cvssData; const cvss = cvssV31 || cvssV2 || {}; const severity = cvss.baseSeverity || 'N/A'; const score = cvss.baseScore || 0; const references = (cve.references || []).map(r => r.url).slice(0, 5); const weaknesses = (cve.weaknesses || []).flatMap(w => (w.description || []).map(d => d.value) ).slice(0, 5); const published = cve.published || ''; const lastModified = cve.lastModified || ''; return { id: cve.id, description: desc.length > 200 ? desc.substring(0, 200) + '...' : desc, severity: severity.toLowerCase(), score, published, lastModified, references, weaknesses, source: 'NVD', technique: weaknesses[0] || 'T1190', tactic: this._mapTactic(weaknesses[0]), }; }); } catch (e) { console.error('[SentinelAPI] NVD error:', e); return []; } }, // ---- Get specific CVE ---- async getCVE(cveId) { try { const url = `https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=${encodeURIComponent(cveId)}`; const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); const cve = data.vulnerabilities?.[0]?.cve; if (!cve) return null; const desc = (cve.descriptions || []).find(d => d.lang === 'en')?.value || ''; const metrics = cve.metrics || {}; const cvss = metrics.cvssMetricV31?.[0]?.cvssData || metrics.cvssMetricV2?.[0]?.cvssData || {}; return { id: cve.id, description: desc, severity: (cvss.baseSeverity || 'N/A').toLowerCase(), score: cvss.baseScore || 0, vector: cvss.vectorString || '', published: cve.published || '', lastModified: cve.lastModified || '', references: (cve.references || []).map(r => r.url), weaknesses: (cve.weaknesses || []).flatMap(w => (w.description || []).map(d => d.value)), configs: (cve.configurations || []).flatMap(c => (c.nodes || []).flatMap(n => (n.cpeMatch || []).map(m => ({ criteria: m.criteria, vulnerable: m.vulnerable, }))) ).slice(0, 10), }; } catch (e) { console.error('[SentinelAPI] NVD CVE error:', e); return null; } }, // ---- DNS Resolution via Google DNS (free) ---- async resolveDomain(domain) { try { const res = await fetch(`https://dns.google/resolve?name=${encodeURIComponent(domain)}&type=A`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); const answers = data.Answer || []; return { domain, ips: answers.filter(a => a.type === 1).map(a => a.data), answers: answers.map(a => ({ name: a.name, type: a.type, ttl: a.TTL, data: a.data })), status: data.Status === 0 ? 'resolved' : 'failed', }; } catch (e) { console.error('[SentinelAPI] DNS error:', e); return { domain, ips: [], answers: [], status: 'error', error: e.message }; } }, // ---- Map CWE to MITRE tactic ---- _mapTactic(cwe) { if (!cwe) return 'Initial Access'; const map = { 'CWE-79': 'Execution', 'CWE-89': 'Initial Access', 'CWE-78': 'Execution', 'CWE-22': 'Discovery', 'CWE-352': 'Initial Access', 'CWE-434': 'Execution', 'CWE-502': 'Execution', 'CWE-190': 'Execution', 'CWE-476': 'Execution', 'CWE-306': 'Initial Access', 'CWE-862': 'Privilege Escalation', 'CWE-863': 'Authorization', 'CWE-287': 'Credential Access', 'CWE-522': 'Credential Access', 'CWE-312': 'Collection', 'CWE-327': 'Defense Evasion', 'CWE-732': 'Privilege Escalation', 'CWE-250': 'Privilege Escalation', 'CWE-610': 'Exfiltration', 'CWE-400': 'Impact', 'CWE-918': 'Initial Access', 'CWE-94': 'Persistence', 'CWE-416': 'Execution', }; return map[cwe] || 'Initial Access'; }, // ---- Batch IP lookup ---- async batchLookup(ips) { const results = []; for (const ip of ips.slice(0, 5)) { const data = await this.lookupIP(ip); results.push(data); await new Promise(r => setTimeout(r, 300)); // Rate limit } return results; }, };