* Detect and handle headless mode (server installations without a display) * Headless mode hides display-related UI elements
()
| 7204 | <td class="px-2 py-1 font-mono ${r.virtual ? 'text-gray-500' : 'text-gray-200'}">${escapeHtml(r.iface)}</td> |
| 7205 | <td class="px-2 py-1">${badge}</td> |
| 7206 | <td class="px-2 py-1 font-mono ${red(r.accept_redirects)}">${r.accept_redirects}</td> |
| 7207 | <td class="px-2 py-1 font-mono ${red(r.accept_ra_rtr_pref)}">${r.accept_ra_rtr_pref}</td> |
| 7208 | <td class="px-2 py-1 font-mono text-gray-400">${r.accept_ra}</td> |
| 7209 | </tr>`; |
| 7210 | }; |
| 7211 | if (rows.length) { |
| 7212 | html += '<table class="min-w-full text-xs text-gray-300 whitespace-nowrap"><thead>' + |
| 7213 | '<tr class="text-left text-gray-500"><th class="px-2 py-1">Interface</th><th class="px-2 py-1">Grade</th><th class="px-2 py-1">accept_redirects</th><th class="px-2 py-1">rtr_pref</th><th class="px-2 py-1">accept_ra</th></tr>' + |
| 7214 | '</thead><tbody>' + phys.map(fmt).join(''); |
| 7215 | if (virt.length) { |
| 7216 | html += `<tr class="border-t border-slate-800"><td colspan="5" class="px-2 py-1"><button onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display==='none'?'':'none'" class="text-cyan-400 underline text-xs">${virt.length} virtual/container interface(s) ▸</button><span style="display:none"></span></td></tr>`; |
| 7217 | html += '<tr style="display:none" class="virt-rows"><td colspan="5" class="p-0"><table class="min-w-full text-xs"><tbody>' + virt.map(fmt).join('') + '</tbody></table></td></tr>'; |
| 7218 | } |
| 7219 | html += '</tbody></table>'; |
| 7220 | } |
| 7221 | const gws = d.gateways || []; |
| 7222 | if (gws.length) { |
| 7223 | html += '<p class="text-xs text-gray-500 mt-2">Accepted IPv6 gateway(s): ' + |
| 7224 | gws.map(g => `<span class="font-mono text-gray-300">${escapeHtml(g.gw)}</span> dev ${escapeHtml(g.dev)}${g.from_ra ? ' <span class="text-amber-300">(from RA)</span>' : ''}`).join(', ') + '</p>'; |
| 7225 | } |
| 7226 | if (d.reasons && d.reasons.length) { |
| 7227 | html += '<ul class="text-xs text-gray-400 mt-2 list-disc pl-5">' + |
| 7228 | d.reasons.map(r => '<li>' + escapeHtml(r) + '</li>').join('') + '</ul>'; |
| 7229 | } |
| 7230 | if (d.needs_hardening && !d.applied) { |
| 7231 | html += '<p class="text-xs text-amber-300 mt-2">Click <strong>Harden host</strong> to close these (sets accept_redirects=0 and accept_ra_rtr_pref=0; leaves accept_ra alone).</p>'; |
| 7232 | } |
| 7233 | if (d.advisories && d.advisories.length) { |
| 7234 | html += '<ul class="text-xs text-cyan-400/80 mt-2 list-disc pl-5">' + |
no test coverage detected