diff --git a/packages/secubox-toolbox/secubox_toolbox/store.py b/packages/secubox-toolbox/secubox_toolbox/store.py
index 580e963b..9ec17e3b 100644
--- a/packages/secubox-toolbox/secubox_toolbox/store.py
+++ b/packages/secubox-toolbox/secubox_toolbox/store.py
@@ -173,7 +173,7 @@ def ad_stats(hours: int = 24, top: int = 25) -> dict:
cutoff = time.time() - hours * 3600
out = {"window_hours": hours, "total_blocked": 0, "total_bytes": 0,
"by_action": {"block": 0, "silent": 0}, "top_hosts": [], "top_sites": [],
- "top_visitors": []}
+ "top_visitors": [], "total_candidates": 0, "top_candidates": []}
try:
with _conn() as c:
for action, hits in c.execute(
@@ -194,6 +194,18 @@ def ad_stats(hours: int = 24, top: int = 25) -> dict:
"SELECT mac_hash, SUM(hits) FROM ad_block_client_host "
"WHERE last_seen>=? AND mac_hash<>'' GROUP BY mac_hash "
"ORDER BY SUM(hits) DESC LIMIT ?", (cutoff, top))]
+ # #802 — DETECTED ad/tracker hosts (ad_candidates: observed, awaiting
+ # autolearn promotion to active 204-blocking). Surface them so the
+ # dashboard reflects real detection even when nothing is blocked yet.
+ try:
+ out["top_candidates"] = [{"host": h, "hits": int(n)} for h, n in c.execute(
+ "SELECT host, SUM(hits) FROM ad_candidates WHERE last_seen>=? "
+ "GROUP BY host ORDER BY SUM(hits) DESC LIMIT ?", (cutoff, top))]
+ r = c.execute("SELECT COUNT(DISTINCT host) FROM ad_candidates WHERE last_seen>=?",
+ (cutoff,)).fetchone()
+ out["total_candidates"] = int((r and r[0]) or 0)
+ except sqlite3.Error:
+ pass
# #755 — trackers detected/poisoned by the MITM in the window: distinct
# cross-site cookie-identifier hashes seen on social_edges. This is the
# "Trackers" half of the card (the 204 ad-block is the "pubs" half).
diff --git a/packages/secubox-toolbox/www/toolbox/index.html b/packages/secubox-toolbox/www/toolbox/index.html
index c80d5b74..bad2b199 100644
--- a/packages/secubox-toolbox/www/toolbox/index.html
+++ b/packages/secubox-toolbox/www/toolbox/index.html
@@ -185,6 +185,10 @@
🎯 Top hôtes publicitaires bloqués
+
+
🔎 Trackers/pubs détectés (candidats — observés, pas encore promus au blocage)
+
+
🌐 Top sites visités
@@ -640,6 +644,11 @@ async function loadAds() {
document.getElementById('ads-sites').innerHTML = siteRows
? '
| Site | trackers/pubs bloqués |
'+siteRows+'
'
: '';
+ // #802 — detected candidates (observed, awaiting promotion to active blocking)
+ const candRows = (d.top_candidates||[]).slice(0, 15).map(r=>`
${esc(r.host)} | ${r.hits} |
`).join('');
+ document.getElementById('ads-candidates').innerHTML = candRows
+ ? `
${d.total_candidates||0} hôtes détectés dans la fenêtre
| Hôte détecté | observations |
${candRows}
`
+ : '
aucun tracker/pub détecté dans la fenêtre
';
const visRows = (d.top_visitors||[]).slice(0, 5).map(r=>{
const mh = esc(r.mac_hash);
return `
${mh} | ${r.hits} |
`;