File size: 7,305 Bytes
ad2db33 4c2069d ad2db33 4c2069d ad2db33 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | // =====================================================
// 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;
},
}; |