| |
| |
| |
| |
|
|
| const SentinelAPI = { |
| |
| 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 }; |
| } |
| }, |
|
|
| |
| 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 []; |
| } |
| }, |
|
|
| |
| 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; |
| } |
| }, |
|
|
| |
| 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 }; |
| } |
| }, |
|
|
| |
| _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'; |
| }, |
|
|
| |
| 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)); |
| } |
| return results; |
| }, |
| }; |