(targetMode)
| 6781 | return `<div class="rounded-lg border ${cls} px-3 py-2 text-sm"> |
| 6782 | <span class="text-xs uppercase tracking-wide opacity-80">${escapeHtml(a.label || a.source)}</span> |
| 6783 | <span class="ml-1">${escapeHtml(a.title || '')}</span> |
| 6784 | <span class="text-xs opacity-60 ml-1">· ${escapeHtml(t)}</span> |
| 6785 | </div>`; |
| 6786 | }).join(''); |
| 6787 | if (srcEl) { |
| 6788 | const srcs = s.sources || []; |
| 6789 | const on = srcs.filter(x => x.present).length; |
| 6790 | srcEl.textContent = srcs.length |
| 6791 | ? `${on}/${srcs.length} watchers logging: ` + srcs.map(x => (x.present ? '● ' : '○ ') + (x.label || x.name)).join(' ') |
| 6792 | : ''; |
| 6793 | } |
| 6794 | } |
| 6795 | async function refreshDashWatchtower() { |
| 6796 | try { renderDashWatchtower(await fetchAPI('/api/net/watchtower?limit=5&min_severity=medium')); } |
| 6797 | catch (e) { /* offline — leave as-is */ } |
| 6798 | refreshIncidents('dash-incidents'); |
| 6799 | } |
| 6800 | |
| 6801 | // ---- ARP Poisoning card (on-demand) --------------------------------------- |
| 6802 | const _ARP_VERDICT_STYLE = { |
| 6803 | clean: ['bg-green-950/40 border-green-900 text-green-400', '✓ No ARP spoofing detected'], |
| 6804 | suspicious: ['bg-amber-950/50 border-amber-800 text-amber-300', '⚠ Possible ARP spoofing'], |
| 6805 | spoofed: ['bg-red-950/60 border-red-800 text-red-300', '🛑 ARP spoofing / MITM detected'], |
| 6806 | unknown: ['bg-slate-800 border-slate-700 text-slate-400', '— Could not determine (no gateway / no ARP reply)'], |
| 6807 | }; |
| 6808 | function _arpFillIfaces() { |
| 6809 | const sel = document.getElementById('arp-check-iface'); |
| 6810 | if (!sel || sel.dataset.filled === '1') return Promise.resolve(); |
| 6811 | return fetchAPI('/api/net/interfaces').then(x => { |
| 6812 | (x.interfaces || []).forEach(i => { |
| 6813 | const o = document.createElement('option'); |
| 6814 | o.value = i.name; |
| 6815 | const tag = i.type === 'wifi' ? ' (WiFi)' : i.type === 'ethernet' ? ' (LAN)' : (i.type ? ' (' + i.type + ')' : ''); |
| 6816 | o.textContent = i.name + tag; |
| 6817 | sel.appendChild(o); |
| 6818 | }); |
no test coverage detected