()
| 7761 | // ---- LDAP Watch (Active Directory / LDAP, passive) ------------------------- |
| 7762 | const _LDAP_VERDICT_STYLE = { |
| 7763 | clean: ['bg-green-950/40 border-green-900 text-green-400', '✓ No LDAP/AD exposure or attack observed'], |
| 7764 | suspicious: ['bg-amber-950/50 border-amber-800 text-amber-300', '⚠ Weak LDAP posture / recon — anonymous binds, enumeration, or CLDAP exposure'], |
| 7765 | compromised: ['bg-red-950/60 border-red-800 text-red-300', '🛑 LDAP CREDENTIAL EXPOSURE / ATTACK — cleartext creds, StartTLS strip, injection, or brute force'], |
| 7766 | unknown: ['bg-slate-800 border-slate-700 text-slate-400', '— Could not determine'], |
| 7767 | }; |
| 7768 | const _LDAP_SEV_STYLE = { high: 'text-red-300', warn: 'text-amber-300', low: 'text-gray-400', info: 'text-gray-500' }; |
| 7769 | function _ldapFillIfaces() { |
| 7770 | const sel = document.getElementById('ldap-iface'); |
| 7771 | if (!sel || sel.dataset.filled === '1') return Promise.resolve(); |
| 7772 | return fetchAPI('/api/net/interfaces').then(x => { |
| 7773 | (x.interfaces || []).forEach(i => { |
| 7774 | const o = document.createElement('option'); |
| 7775 | o.value = i.name; |
| 7776 | const tag = i.type === 'wifi' ? ' (WiFi)' : i.type === 'ethernet' ? ' (LAN)' : (i.type ? ' (' + i.type + ')' : ''); |
| 7777 | o.textContent = i.name + tag; |
| 7778 | sel.appendChild(o); |
| 7779 | }); |
| 7780 | sel.dataset.filled = '1'; |
| 7781 | }).catch(() => {}); |
| 7782 | } |
| 7783 | async function runLdapWatch() { |
| 7784 | const out = document.getElementById('ldap-results'); |
| 7785 | if (!out) return; |
| 7786 | const btn = (typeof event !== 'undefined' && event && event.target) ? event.target : null; |
| 7787 | const ifaceSel = document.getElementById('ldap-iface'); |
| 7788 | const iface = ifaceSel && ifaceSel.value ? ifaceSel.value : ''; |
| 7789 | const secsEl = document.getElementById('ldap-secs'); |
| 7790 | const secs = secsEl && secsEl.value ? secsEl.value : '15'; |
| 7791 | _ndBusy(btn, true, 'Listening…'); |
| 7792 | out.classList.remove('hidden'); |
| 7793 | out.innerHTML = '<p class="text-sm text-gray-400">Passively capturing LDAP / CLDAP…</p>'; |
| 7794 | try { |
| 7795 | _ldapFillIfaces(); |
| 7796 | const qs = '?seconds=' + encodeURIComponent(secs) + (iface ? '&interface=' + encodeURIComponent(iface) : ''); |
| 7797 | const d = await fetchAPI('/api/net/ldap-watch' + qs); |
| 7798 | if (!d || d.success === false) { |
| 7799 | const msg = (d && d.error) || 'failed'; |
| 7800 | let extra = ''; |
| 7801 | if (d && d.missing_tool === 'scapy') extra = ' — install Scapy via Detector Self-Test'; |
| 7802 | out.innerHTML = '<p class="text-sm text-red-400">Error: ' + escapeHtml(msg) + escapeHtml(extra) + '</p>'; |
| 7803 | return; |
| 7804 | } |
| 7805 | const [cls, label] = _LDAP_VERDICT_STYLE[d.verdict] || _LDAP_VERDICT_STYLE.unknown; |
| 7806 | const st = d.stats || {}; |
| 7807 | let html = `<div class="mb-2 px-3 py-2 rounded border ${cls} text-sm">${label}</div>`; |
| 7808 | html += `<p class="text-xs text-gray-500 mb-2">Interface: ${escapeHtml(d.interface || '—')} · ${d.seconds}s · ${st.ldap_messages || 0} messages · ${st.binds || 0} binds · ${st.searches || 0} searches · ${st.extended || 0} extended · ${st.cldap || 0} CLDAP · ${st.flows || 0} flows</p>`; |
| 7809 | const findings = d.findings || []; |
| 7810 | if (findings.length) { |
| 7811 | html += '<table class="min-w-full text-xs text-gray-300 whitespace-nowrap"><thead>' + |
| 7812 | '<tr class="text-left text-gray-500"><th class="px-2 py-1">Sev</th><th class="px-2 py-1">Finding</th><th class="px-2 py-1">Source → Target</th><th class="px-2 py-1">Detail</th></tr>' + |
| 7813 | '</thead><tbody>' + |
| 7814 | findings.map(f => { |
| 7815 | const sev = _LDAP_SEV_STYLE[f.severity] || 'text-gray-400'; |
| 7816 | return `<tr class="border-t border-slate-800 align-top"> |
| 7817 | <td class="px-2 py-1 ${sev} uppercase">${escapeHtml(f.severity || '')}</td> |
| 7818 | <td class="px-2 py-1 ${sev}">${escapeHtml(f.code || '')}</td> |
nothing calls this directly
no test coverage detected