(status, visuals = null)
| 6409 | html += `<p class="text-xs text-gray-600 mt-2">Starvation capture skipped: ${escapeHtml(st.error)}</p>`; |
| 6410 | } |
| 6411 | if (d.arp_verdict && d.arp_verdict !== 'clean' && d.arp_verdict !== 'unknown') { |
| 6412 | html += `<p class="text-xs text-red-300 mt-1">Gateway ARP verdict: ${escapeHtml(d.arp_verdict)}</p>`; |
| 6413 | } |
| 6414 | if (d.reasons && d.reasons.length) { |
| 6415 | html += '<ul class="text-xs text-gray-400 mt-2 list-disc pl-5">' + |
| 6416 | d.reasons.map(r => '<li>' + escapeHtml(r) + '</li>').join('') + '</ul>'; |
| 6417 | } |
| 6418 | out.innerHTML = html; |
| 6419 | } catch (e) { |
| 6420 | out.innerHTML = '<p class="text-sm text-red-400">Failed: ' + escapeHtml(e.message) + '</p>'; |
| 6421 | } finally { |
| 6422 | _ndBusy(btn, false); |
| 6423 | } |
| 6424 | } |
| 6425 | async function dhcpTrustCurrent() { |
| 6426 | try { |
| 6427 | await postAPI('/api/net/dhcp-baseline', { action: 'reset' }); |
| 6428 | addConsoleMessage('DHCP baseline reset — re-learning current server(s)', 'info'); |
| 6429 | await runDhcpGuardian(); |
| 6430 | } catch (e) { |
| 6431 | addConsoleMessage('Failed to reset DHCP baseline: ' + e.message, 'error'); |
| 6432 | } |
| 6433 | } |
| 6434 | |
| 6435 | // ---- TLS Watch (passive TLS/QUIC handshake observer) ----------------------- |
| 6436 | const _TLS_VERDICT_STYLE = { |
| 6437 | clean: ['bg-green-950/40 border-green-900 text-green-400', '✓ No TLS/QUIC handshake anomalies detected'], |
| 6438 | suspicious: ['bg-amber-950/50 border-amber-800 text-amber-300', '⚠ Suspicious TLS posture — review the findings'], |
| 6439 | compromised: ['bg-red-950/60 border-red-800 text-red-300', '🛑 Possible interception — SNI↔cert mismatch or known-bad JA4'], |
| 6440 | unknown: ['bg-slate-800 border-slate-700 text-slate-400', '— Could not determine'], |
| 6441 | }; |
| 6442 | const _TLS_SEV_COLOR = { high: 'text-red-300', warn: 'text-amber-300', notice: 'text-gray-300', info: 'text-gray-500' }; |
| 6443 | function _tlsFillIfaces() { |
| 6444 | const sel = document.getElementById('tls-iface'); |
| 6445 | if (!sel || sel.dataset.filled === '1') return Promise.resolve(); |
| 6446 | return fetchAPI('/api/net/interfaces').then(x => { |
| 6447 | (x.interfaces || []).forEach(i => { |
| 6448 | const o = document.createElement('option'); |
| 6449 | o.value = i.name; |
| 6450 | const tag = i.type === 'wifi' ? ' (WiFi)' : i.type === 'ethernet' ? ' (LAN)' : (i.type ? ' (' + i.type + ')' : ''); |
| 6451 | o.textContent = i.name + tag; |
| 6452 | sel.appendChild(o); |
| 6453 | }); |
| 6454 | sel.dataset.filled = '1'; |
no test coverage detected