v2.6.0: BT Radar — consent-gated bluetooth scan/whitelist/push (paired-device consent model), /api/btradar, drawer BT panel; auto-crack.py for nightmare (chromium v10 offline decrypt verified)
Some checks failed
Build Agent Binaries / build-macos (push) Failing after 1s
Build Agent Binaries / build-linux (push) Successful in 23s
Build Agent Binaries / build-windows (push) Failing after 1s

This commit is contained in:
Hermes
2026-10-01 23:54:32 +00:00
committed by root
parent 9e6d7e8d48
commit 8130de56fe
5 changed files with 220 additions and 5 deletions

View File

@@ -725,3 +725,92 @@ async function updateAllOutdated() {
});
mo.observe(document.body, { childList: true, subtree: true });
})();
// ═════════════════════════════════════════════════════════════════════
// BT RADAR — consent-gated bluetooth device inventory
// ═════════════════════════════════════════════════════════════════════
async function drawerBtScan(nodeId) {
const el = document.getElementById('btBox') || document.getElementById('pingResult');
try {
await api(`/api/nodes/${nodeId}/command`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ actionType: 'bt_scan', command: 'bt_scan', payload: {} })
});
// poll btradar for this node
let tries = 0;
const iv = setInterval(async () => {
tries++;
const r = await api('/api/btradar').catch(() => null);
const entry = r && r.radar && r.radar[nodeId];
if (entry) {
clearInterval(iv);
renderBtList(nodeId, entry);
} else if (tries > 12) {
clearInterval(iv);
}
}, 4000);
} catch (e) { console.error(e); }
}
function renderBtList(nodeId, entry) {
let box = document.getElementById('btBox');
const body = document.getElementById('ndBody');
if (!body) return;
if (!box) {
box = document.createElement('div');
box.id = 'btBox';
box.style.cssText = 'border:1px solid #1e293b;border-radius:8px;padding:0.75rem;';
body.appendChild(box);
}
const devs = (entry.devices || []);
const ago = Math.round((Date.now() - entry.at) / 60000);
const esc = escapeHtml;
box.innerHTML = `
<div style="display:flex;justify-content:space-between;margin-bottom:0.4rem;">
<b>BT Radar</b><span style="color:#64748b;font-size:0.7rem;">scanned ${ago}m ago</span>
</div>
${devs.length ? devs.map(d => `
<div style="display:flex;justify-content:space-between;align-items:center;gap:0.5rem;padding:0.2rem 0;border-bottom:1px solid #1e293b;">
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
${d.mine ? '<span style="color:#4ade80;">●</span> ' : '<span style="color:#94a3b8;">○</span> '}
<b>${esc(d.name || 'unnamed')}</b>
<span style="color:#64748b;font-size:0.7rem;">${esc(d.mac)}${d.rssi ? ' · ' + esc(d.rssi) + 'dBm' : ''}</span>
</span>
${d.mine
? '<span style="color:#4ade80;font-size:0.7rem;">mine</span>'
: `<button class="btn btn-secondary" style="padding:0.15rem 0.5rem;font-size:0.7rem;" onclick="btWhitelist('${nodeId}','${esc(d.mac)}',true)">Approve</button>`}
</div>`).join('')
: '<div style="color:#64748b;">no devices in range</div>'}
<div style="display:flex;gap:0.4rem;margin-top:0.5rem;">
<button class="btn btn-secondary" onclick="drawerBtScan('${nodeId}')">Re-scan</button>
</div>
<div style="color:#64748b;font-size:0.65rem;margin-top:0.3rem;">● = on your approved list (push-enabled) · ○ = discovered, push blocked until approved</div>`;
}
async function btWhitelist(nodeId, mac, add) {
await api(`/api/nodes/${nodeId}/command`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ actionType: 'bt_whitelist', command: 'bt_whitelist', payload: { add: add ? mac : undefined, remove: add ? undefined : mac } })
});
// re-scan to refresh the view
drawerBtScan(nodeId);
}
// inject BT button into drawer action row
(function injectBtBtn() {
const mo = new MutationObserver(() => {
const row = document.querySelector('#nexusDrawer div[style*="flex-wrap"]');
if (row && !document.getElementById('btScanBtn')) {
const b = document.createElement('button');
b.id = 'btScanBtn';
b.className = 'btn btn-secondary';
b.innerHTML = '<i class="fa-brands fa-bluetooth-b"></i> BT Radar';
b.title = 'Scan bluetooth devices in range of this node. Push (sync) only works on devices you Approve — pairing on the device itself is the consent.';
b.onclick = () => drawerBtScan(window._drawerNodeId);
row.appendChild(b);
}
});
mo.observe(document.body, { childList: true, subtree: true });
})();