()
| 6153 | function onNetDiagDisplayToggled(cb) { |
| 6154 | const note = document.getElementById('netdiag-display-note'); |
| 6155 | if (note) note.classList.toggle('hidden', !cb.checked); |
| 6156 | postAPI('/api/config', { network_diagnostic_mode: cb.checked }) |
| 6157 | .then(() => addConsoleMessage( |
| 6158 | cb.checked ? 'E-Paper: network diagnostic mode ON' : 'E-Paper: network diagnostic mode OFF', |
| 6159 | 'info')) |
| 6160 | .catch(err => { |
| 6161 | addConsoleMessage('Failed to toggle network diagnostic mode: ' + err.message, 'error'); |
| 6162 | cb.checked = !cb.checked; |
| 6163 | if (note) note.classList.toggle('hidden', !cb.checked); |
| 6164 | }); |
| 6165 | } |
| 6166 | |
| 6167 | // ---- Network Integrity Monitor (DNS poisoning + ARP spoofing) -------------- |
| 6168 | const _NETINT_STYLE = { |
| 6169 | clean: ['bg-green-950/40 border-green-900 text-green-400', '✓'], |
| 6170 | suspicious: ['bg-amber-950/50 border-amber-800 text-amber-300', '⚠'], |
| 6171 | compromised: ['bg-red-950/60 border-red-800 text-red-300', '🛑'], |
| 6172 | hijacked: ['bg-red-950/60 border-red-800 text-red-300', '🛑'], |
| 6173 | spoofed: ['bg-red-950/60 border-red-800 text-red-300', '🛑'], |
| 6174 | rogue: ['bg-red-950/60 border-red-800 text-red-300', '🛑'], |
| 6175 | starvation: ['bg-red-950/60 border-red-800 text-red-300', '🛑'], |
| 6176 | unknown: ['bg-slate-800 border-slate-700 text-slate-400', '—'], |
| 6177 | }; |
| 6178 | // A verdict is clean/informational, an active attack (critical, red), or — anything |
| 6179 | // else non-clean — a suspicious finding (amber). Mirrors the server's _ni_rank so the |
| 6180 | // chips colour every scanner's verdicts without enumerating them all. |
| 6181 | const _NETINT_CLEAN = new Set(['clean', 'unknown', 'ok', 'none', 'hardened', 'learned', 'n/a', 'no-traffic', 'disabled']); |
| 6182 | const _NETINT_CRITICAL = new Set(['hijacked', 'spoofed', 'rogue', 'starvation', 'compromised', 'root-hijack', 'bpdu-flood', 'vlan-hop', 'hijack', 'injection', 'rogue-router', 'poisoning', 'spoof-conflict', 'smbv1-active', 'coercion-attempt', 'relay-suspected', 'rogue-speaker', 'rogue-redirect', 'rogue-ra', 'rogue-irdp']); |
| 6183 | function _netintRank(verdict) { |
| 6184 | const v = verdict || 'unknown'; |
| 6185 | if (_NETINT_CLEAN.has(v)) return 0; |
| 6186 | if (_NETINT_CRITICAL.has(v)) return 2; |
| 6187 | return 1; |
| 6188 | } |
| 6189 | function _netintStyleFor(verdict) { |
| 6190 | if (_NETINT_STYLE[verdict]) return _NETINT_STYLE[verdict]; |
| 6191 | return [_NETINT_STYLE.clean, _NETINT_STYLE.suspicious, _NETINT_STYLE.compromised][_netintRank(verdict)]; |
| 6192 | } |
| 6193 | function _netintChip(label, verdict) { |
| 6194 | const [cls, icon] = _netintStyleFor(verdict); |
| 6195 | return `<span class="px-2.5 py-1 rounded border ${cls}">${icon} ${label}: ${escapeHtml(verdict || 'unknown')}</span>`; |
| 6196 | } |
| 6197 | function renderNetIntegrity(d) { |
| 6198 | const out = document.getElementById('netint-status'); |
| 6199 | if (!out) return; |
| 6200 | if (!d || d.success === false) { out.innerHTML = ''; return; } |
| 6201 | // Prefer the full per-check map (extended monitor); fall back to dns/arp/dhcp. |
| 6202 | let entries; |
| 6203 | if (d.checks && Object.keys(d.checks).length) { |
| 6204 | const order = ['dns', 'arp', 'dhcp', 'raguard', 'stp', 'dtp', 'cdp', 'vtp', 'igmp', 'ipv6', 'ndp', 'fhrp', 'ospf', 'eigrp', 'isis', 'bgp', 'smb', 'relay', 'ntp', 'icmp', 'snmp', 'cert', 'tls', 'ldap']; |
| 6205 | entries = Object.keys(d.checks).sort((a, b) => (order.indexOf(a) + 1 || 99) - (order.indexOf(b) + 1 || 99)) |
| 6206 | .map(k => [d.checks[k].label || k.toUpperCase(), d.checks[k].verdict, d.checks[k].reasons || []]); |
| 6207 | } else { |
| 6208 | entries = [['DNS', (d.dns && d.dns.verdict), (d.dns && d.dns.reasons) || []], |
| 6209 | ['ARP', (d.arp && d.arp.verdict), (d.arp && d.arp.reasons) || []], |
| 6210 | ['DHCP', (d.dhcp && d.dhcp.verdict), (d.dhcp && d.dhcp.reasons) || []]]; |
| 6211 | } |
| 6212 | // Worst-first so bad findings lead. |
no test coverage detected