Compare commits

..

5 Commits

Author SHA1 Message Date
2a51348b9d feat(toolbox): tracker domain-rollup + history + target↔tracker correlation (closes #549)
fetch_graph() gains three additive, read-time keys (no schema change,
d3 /social/graph contract untouched):
- by_domain: trackers rolled up under their registrable parent (eTLD+1,
  all *.doubleclick.net → doubleclick.net) with tracker_count/hits/sites/vendors
- targets: inverse map — per 1st-party site, the trackers + parent domains
  watching it
- history: per-UTC-day timeline (hits/trackers/sites) from social_edges
stats gains total_domains; local _registrable_domain helper (no publicsuffix
dep). Integration-tested (rollup, inversion, history). secubox-toolbox 2.6.17.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 13:16:06 +02:00
CyberMind
4cb8fb87fa
Merge pull request #546 from CyberMind-FR/feature/545-injected-banner-neon-tube-redesign-for-r
Neon-tube injected banner for R3 (+ R4-ready theme) (#545)
2026-06-13 13:15:19 +02:00
8d48d86bcd feat(toolbox): neon-tube injected banner for R3 (+R4-ready theme) (closes #545)
_LEVEL_THEME map drives the banner look per opt-in level: R3 (and the
planned R4) get a neon-tube treatment — dark glass bar, glowing tube
border (layered box-shadow) and neon text-shadow on the title — while R2
keeps the original amber flat bar. _banner_html_dynamic() now takes the
level and themes both the CSP-strict (JS-less) and JS (dismissible)
variants; all inline CSS, no injected <style>/@keyframes, ASCII/NCR-clean.
R4 theme is defined but inert until _client_level() returns 'r4'.
secubox-toolbox 2.6.16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 13:14:56 +02:00
CyberMind
7a651f360d
Merge pull request #544 from CyberMind-FR/feature/543-kbin-landing-radical-simplify-livelier-f
kbin landing: radical-simplify redesign (#543)
2026-06-13 13:14:08 +02:00
e4d59569eb feat(toolbox): kbin landing radical-simplify redesign (closes #543)
Animated hero (gazing eye + floating tracker dots) + one big 'Protège-moi
(R3)' CTA + the auto-detected install panel up front. KPIs, cert-probe,
pitch, R0-R3 levels, charts, architecture, open-source and contact are
folded behind an 'En savoir plus' <details>. Quick-nav trimmed: dropped
the CA iPhone / CA Android / QR profil cards (now inside the per-platform
install panel); kept R3 Install / Mon rapport / Ma carto / Wiki / Cabine.
Count-up animation on the live KPIs. All Jinja vars + live-stats and
cert-probe scripts preserved. secubox-toolbox 2.6.15.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 12:52:34 +02:00
3 changed files with 237 additions and 128 deletions

View File

@ -1,20 +1,57 @@
{# SPDX-License-Identifier: LicenseRef-CMSD-1.0 #} {# SPDX-License-Identifier: LicenseRef-CMSD-1.0 #}
{# Public landing page — kbin.gk2.secubox.in #} {# Public landing page — kbin.gk2.secubox.in #}
{# Radical-simplify redesign (#543): animated hero + one CTA + install panel
up top ; everything else folded behind "En savoir plus". #}
<!DOCTYPE html> <!DOCTYPE html>
<html lang=fr><head> <html lang=fr><head>
<meta charset=UTF-8> <meta charset=UTF-8>
<meta name=viewport content="width=device-width,initial-scale=1"> <meta name=viewport content="width=device-width,initial-scale=1">
<meta name=description content="VILLAGE3B — Cabine numérique Gondwana : diagnostic compromission iPhone/Android anonyme, gratuit, open source CMSD"> <meta name=description content="VILLAGE3B — Cabine numérique Gondwana : diagnostic compromission iPhone/Android anonyme, gratuit, open source CMSD">
<title>📡 VILLAGE3B — Cabine Numérique Gondwana</title> <title>👁️ VILLAGE3B — Qui te piste ?</title>
<link rel=manifest href=/manifest.json> <link rel=manifest href=/manifest.json>
<style> <style>
:root{--bg:#0a0a0f;--bg2:#0e0e15;--phos:#00dd44;--phos-hot:#00ff55;--dim:#006622;--text:#e8e6d9;--purple:#9e76ff;--gold:#c9a84c;--amber:#ffb347;--red:#ff4466} :root{--bg:#0a0a0f;--bg2:#0e0e15;--phos:#00dd44;--phos-hot:#00ff55;--dim:#006622;--text:#e8e6d9;--purple:#9e76ff;--gold:#c9a84c;--amber:#ffb347;--red:#ff4466;--cyan:#00d4ff}
*{box-sizing:border-box;margin:0;padding:0} *{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Courier New',Menlo,monospace;background:var(--bg);color:var(--text);line-height:1.55;padding-bottom:3rem} body{font-family:'Courier New',Menlo,monospace;background:var(--bg);color:var(--text);line-height:1.55;padding-bottom:3rem}
.hero{background:linear-gradient(135deg,#1a0a2e 0%,#0a0a0f 100%);padding:2.5rem 1.5rem;text-align:center;border-bottom:2px solid var(--phos)} a{color:var(--phos);text-decoration:none}
.hero h1{font-size:2.4rem;color:var(--phos-hot);text-shadow:0 0 8px var(--phos);letter-spacing:0.08em} a:hover{text-decoration:underline}
.hero p.tag{color:var(--gold);font-size:1rem;margin-top:0.4rem;letter-spacing:0.08em}
.hero p.sub{color:var(--dim);font-size:0.85rem;margin-top:0.6rem;max-width:600px;margin-left:auto;margin-right:auto} /* ── HERO ── */
.hero{position:relative;overflow:hidden;background:radial-gradient(120% 120% at 50% -10%,#221041 0%,#0a0a0f 60%);padding:3rem 1.5rem 2.4rem;text-align:center;border-bottom:2px solid var(--phos)}
.eye{font-size:3.4rem;line-height:1;display:inline-block;animation:gaze 5s ease-in-out infinite;filter:drop-shadow(0 0 14px rgba(0,255,85,0.55))}
@keyframes gaze{0%,100%{transform:translateX(0) scale(1)}25%{transform:translateX(-6px) scale(1.04)}60%{transform:translateX(7px) scale(1.04)}}
.hero h1{font-size:2.6rem;color:var(--phos-hot);text-shadow:0 0 10px var(--phos);letter-spacing:0.08em;margin-top:0.3rem}
.hero .punch{color:var(--text);font-size:1.25rem;margin-top:0.6rem;font-weight:bold}
.hero .punch b{color:var(--gold)}
.hero .sub{color:var(--dim);font-size:0.82rem;margin-top:0.5rem;max-width:560px;margin-left:auto;margin-right:auto}
/* floating tracker dots = "who's watching" */
.dots{position:absolute;inset:0;pointer-events:none;z-index:0}
.dots i{position:absolute;width:7px;height:7px;border-radius:50%;opacity:0.0;animation:float 7s ease-in-out infinite}
.dots i:nth-child(1){left:12%;top:30%;background:var(--cyan);animation-delay:.0s}
.dots i:nth-child(2){left:82%;top:24%;background:var(--amber);animation-delay:1.1s}
.dots i:nth-child(3){left:24%;top:68%;background:var(--red);animation-delay:2.3s}
.dots i:nth-child(4){left:70%;top:64%;background:var(--purple);animation-delay:.7s}
.dots i:nth-child(5){left:50%;top:14%;background:var(--cyan);animation-delay:3.0s}
.dots i:nth-child(6){left:90%;top:54%;background:var(--red);animation-delay:1.8s}
@keyframes float{0%{opacity:0;transform:translateY(8px) scale(.6)}30%{opacity:.85}70%{opacity:.7}100%{opacity:0;transform:translateY(-14px) scale(1.1)}}
.hero>*{position:relative;z-index:1}
/* ── big CTA row ── */
.ctas{margin-top:1.4rem;display:flex;gap:0.6rem;justify-content:center;flex-wrap:wrap}
.cta{display:inline-block;padding:0.85rem 1.6rem;font-weight:bold;border-radius:8px;font-size:1.02rem;text-shadow:none;transition:transform .12s,box-shadow .12s}
.cta:hover{text-decoration:none;transform:translateY(-2px)}
.cta.go{background:var(--phos);color:#0a0a0f;box-shadow:0 4px 18px rgba(0,221,68,0.4)}
.cta.go:hover{box-shadow:0 6px 24px rgba(0,221,68,0.6)}
.cta.alt{background:transparent;color:var(--purple);border:1px solid var(--purple)}
.cta.alt:hover{background:rgba(158,118,255,0.12)}
/* ── quicknav (trimmed) ── */
.quicknav{display:flex;flex-wrap:wrap;justify-content:center;gap:0.6rem;margin-top:1.4rem;max-width:620px;margin-left:auto;margin-right:auto}
.qi{display:flex;flex-direction:column;align-items:center;gap:4px;padding:0.5rem 0.4rem;min-width:74px;background:rgba(110,64,201,0.08);border:1px solid var(--purple);border-radius:8px;text-decoration:none;color:var(--text);transition:all 0.12s;font-family:inherit}
.qi:hover{background:rgba(110,64,201,0.22);transform:translateY(-2px);box-shadow:0 4px 14px rgba(158,118,255,0.35);text-decoration:none}
.qi-emoji{font-size:1.5rem;line-height:1}
.qi-label{font-size:0.62rem;letter-spacing:0.04em;color:var(--phos-hot);font-weight:bold;white-space:nowrap}
.container{max-width:1080px;margin:auto;padding:2rem 1.5rem} .container{max-width:1080px;margin:auto;padding:2rem 1.5rem}
.section{margin-bottom:2.5rem} .section{margin-bottom:2.5rem}
h2{color:var(--phos-hot);text-shadow:0 0 4px var(--phos);font-size:1.3rem;margin-bottom:0.8rem;border-bottom:1px solid var(--dim);padding-bottom:0.4rem;letter-spacing:0.04em} h2{color:var(--phos-hot);text-shadow:0 0 4px var(--phos);font-size:1.3rem;margin-bottom:0.8rem;border-bottom:1px solid var(--dim);padding-bottom:0.4rem;letter-spacing:0.04em}
@ -43,30 +80,54 @@ svg.chart{width:100%;max-width:400px;height:auto}
.svg-bar{fill:var(--phos);transition:fill 0.3s} .svg-bar{fill:var(--phos);transition:fill 0.3s}
.svg-bar.medium{fill:var(--amber)} .svg-bar.medium{fill:var(--amber)}
.svg-bar.high{fill:var(--red)} .svg-bar.high{fill:var(--red)}
.steps{counter-reset:step}
.steps li{counter-increment:step;list-style:none;padding:0.6rem 0 0.6rem 2.4rem;position:relative;font-size:0.9rem}
.steps li::before{content:counter(step);position:absolute;left:0;top:0.5rem;width:1.8rem;height:1.8rem;border-radius:50%;background:var(--phos);color:#0a0a0f;text-align:center;line-height:1.8rem;font-weight:bold;text-shadow:none}
code{background:#222;padding:0.1rem 0.4rem;border-radius:2px;font-size:0.85rem;color:var(--phos-hot)} code{background:#222;padding:0.1rem 0.4rem;border-radius:2px;font-size:0.85rem;color:var(--phos-hot)}
a{color:var(--phos);text-decoration:none} .cta-sm{display:inline-block;background:var(--phos);color:#0a0a0f;padding:0.7rem 1.4rem;text-decoration:none;font-weight:bold;border-radius:4px;margin:0.5rem 0.3rem 0.5rem 0;text-shadow:none}
a:hover{text-decoration:underline} .cta-sm.outline{background:transparent;color:var(--phos);border:1px solid var(--phos)}
.cta{display:inline-block;background:var(--phos);color:#0a0a0f;padding:0.7rem 1.4rem;text-decoration:none;font-weight:bold;border-radius:4px;margin:0.5rem 0.3rem 0.5rem 0;text-shadow:none}
.cta.outline{background:transparent;color:var(--phos);border:1px solid var(--phos)}
.cta.purple{background:var(--purple);color:#0a0a0f}
.footer{text-align:center;font-size:0.78rem;color:var(--dim);padding:1.5rem;border-top:1px solid var(--dim);margin-top:2rem} .footer{text-align:center;font-size:0.78rem;color:var(--dim);padding:1.5rem;border-top:1px solid var(--dim);margin-top:2rem}
.arch{font-family:monospace;font-size:0.75rem;color:var(--phos-hot);text-shadow:0 0 4px var(--phos);background:var(--bg2);padding:1rem;border:1px solid var(--dim);border-radius:4px;overflow-x:auto;white-space:pre;line-height:1.4} .arch{font-family:monospace;font-size:0.75rem;color:var(--phos-hot);text-shadow:0 0 4px var(--phos);background:var(--bg2);padding:1rem;border:1px solid var(--dim);border-radius:4px;overflow-x:auto;white-space:pre;line-height:1.4}
.quicknav{display:flex;flex-wrap:wrap;justify-content:center;gap:0.7rem;margin-top:1.2rem;max-width:780px;margin-left:auto;margin-right:auto}
.qi{display:flex;flex-direction:column;align-items:center;gap:4px;padding:0.6rem 0.4rem;min-width:78px;background:rgba(110,64,201,0.08);border:1px solid var(--purple);border-radius:8px;text-decoration:none;color:var(--text);transition:all 0.12s;font-family:inherit} /* ── install panel (kept up top) ── */
.qi:hover{background:rgba(110,64,201,0.22);transform:translateY(-2px);box-shadow:0 4px 14px rgba(158,118,255,0.35);text-decoration:none} .install-panel{background:rgba(0,255,65,0.04);border:1px solid rgba(0,255,65,0.25);border-radius:6px;padding:0.6rem 0.9rem;margin:0.45rem 0;text-align:left}
.qi-emoji{font-size:1.6rem;line-height:1} .install-panel summary{cursor:pointer;font-size:0.95rem;color:var(--phos-hot);list-style:none;outline:none}
.qi-label{font-size:0.65rem;letter-spacing:0.04em;color:var(--phos-hot);font-weight:bold;white-space:nowrap} .install-panel summary::-webkit-details-marker{display:none}
.install-panel[open] summary{margin-bottom:0.6rem}
.install-panel .emoji{font-size:1.1rem;margin-right:0.3rem}
.install-panel ol{padding-left:1.1rem;line-height:1.5;font-size:0.85rem}
.install-panel .btn{display:inline-block;padding:0.45rem 0.75rem;margin:0.25rem 0.2rem 0.25rem 0;background:var(--purple);color:#fff;text-decoration:none;border-radius:5px;font-weight:bold;font-size:0.82rem}
.install-panel .btn.alt{background:transparent;border:1px solid var(--purple);color:var(--purple)}
.install-panel code{background:rgba(0,0,0,0.4);padding:0.1rem 0.35rem;border-radius:3px;font-size:0.8rem;color:var(--phos-hot)}
.install-panel .note{color:var(--dim);font-size:0.78rem;margin-top:0.6rem;border-left:2px solid var(--amber);padding-left:0.6rem}
.install-panel img{max-width:100%;border-radius:5px;margin:0.4rem 0}
.install-panel pre{background:rgba(0,0,0,0.4);padding:0.5rem 0.7rem;border-radius:4px;overflow-x:auto;font-size:0.78rem;margin:0.4rem 0}
/* ── "En savoir plus" fold ── */
.more{max-width:1080px;margin:0 auto;padding:0 1.5rem}
.more>summary{cursor:pointer;list-style:none;text-align:center;color:var(--purple);font-size:0.95rem;letter-spacing:0.05em;padding:0.9rem;border:1px dashed var(--purple);border-radius:8px;margin-bottom:1rem;transition:background .12s}
.more>summary::-webkit-details-marker{display:none}
.more>summary:hover{background:rgba(158,118,255,0.1)}
.more[open]>summary{margin-bottom:1.6rem}
.more>summary .chev{display:inline-block;transition:transform .2s}
.more[open]>summary .chev{transform:rotate(90deg)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}
.v.tick{animation:flash 0.6s}
@keyframes flash{0%{color:var(--gold);transform:scale(1.15)}100%{color:var(--phos-hot);transform:scale(1)}}
</style></head><body> </style></head><body>
<div class=hero> <div class=hero>
<h1>📡 VILLAGE3B</h1> <div class=dots><i></i><i></i><i></i><i></i><i></i><i></i></div>
<p class=tag>// CABINE NUMÉRIQUE GONDWANA · TOOLBOX</p> <span class=eye>👁️</span>
<h1>VILLAGE3B</h1>
<p class=punch>Qui te piste ? <b>La cabine te le montre.</b></p>
<p class=sub>Diagnostic gratuit de compromission iPhone / Android · Anonyme · Open Source · CMSD-1.0</p> <p class=sub>Diagnostic gratuit de compromission iPhone / Android · Anonyme · Open Source · CMSD-1.0</p>
{# Phase 6.I : quick-access icon nav — one-tap to all key endpoints #} <div class=ctas>
<a href="/wg/r3-install" class="cta go">✨ Protège-moi (R3)</a>
<a href="/social/me" class="cta alt">🕸️ Qui me piste ?</a>
</div>
{# trimmed quick-nav — CA iPhone / CA Android / QR profil moved into the
per-platform install panel below (#543) #}
<div class=quicknav> <div class=quicknav>
<a href="/wg/r3-install" class=qi title="Installer R3 WireGuard"> <a href="/wg/r3-install" class=qi title="Installer R3 WireGuard">
<span class=qi-emoji>🌐</span><span class=qi-label>R3 Install</span> <span class=qi-emoji>🌐</span><span class=qi-label>R3 Install</span>
@ -77,15 +138,6 @@ a:hover{text-decoration:underline}
<a href="/social/me" class=qi title="Cartographie sociale — qui me piste, où ?"> <a href="/social/me" class=qi title="Cartographie sociale — qui me piste, où ?">
<span class=qi-emoji>🕸️</span><span class=qi-label>Ma carto</span> <span class=qi-emoji>🕸️</span><span class=qi-label>Ma carto</span>
</a> </a>
<a href="/wg/ca.mobileconfig" class=qi title="CA R3 iPhone (.mobileconfig)">
<span class=qi-emoji>📲</span><span class=qi-label>CA iPhone</span>
</a>
<a href="/wg/ca.pem" class=qi title="CA R3 Android/PC (.pem)">
<span class=qi-emoji>🤖</span><span class=qi-label>CA Android</span>
</a>
<a href="/wg/qr.png" class=qi title="QR profil WireGuard">
<span class=qi-emoji>📱</span><span class=qi-label>QR profil</span>
</a>
<a href="https://github.com/CyberMind-FR/secubox-deb/wiki/R3-WireGuard-install" class=qi title="Wiki R3 multi-OS"> <a href="https://github.com/CyberMind-FR/secubox-deb/wiki/R3-WireGuard-install" class=qi title="Wiki R3 multi-OS">
<span class=qi-emoji>📖</span><span class=qi-label>Wiki</span> <span class=qi-emoji>📖</span><span class=qi-label>Wiki</span>
</a> </a>
@ -95,7 +147,25 @@ a:hover{text-decoration:underline}
</div> </div>
</div> </div>
{# ── Install : auto-detected platform panel, front and centre ── #}
<div class=container> <div class=container>
<div class=section style="margin-bottom:1.5rem">
<h2>📥 Installe en 1 tap</h2>
<p style="font-size:0.85rem;color:var(--dim);margin-bottom:0.8rem">
On a détecté <code>{{ install_platform }}</code> — le panneau adapté est ouvert.
Le CA, le QR et le profil sont dedans. Autre appareil ? Déplie le bon panneau.
</p>
{{ install_panels | safe }}
<p style="margin-top:0.8rem;font-size:0.78rem;color:var(--dim)">
R3 marche hors-cabine (4G/5G, autre WiFi), couvre tout le HTTPS, et se révoque
à tout moment. Page standalone : <a href=/wg/onboard>/wg/onboard</a>.
</p>
</div>
</div>
{# ── Everything else, folded ── #}
<details class=more>
<summary><span class=chev>▸</span> En savoir plus — la cabine en détail, en chiffres, en open source</summary>
{# ── KPI live (auto-refresh 5s via /cumulative-stats.json) ── #} {# ── KPI live (auto-refresh 5s via /cumulative-stats.json) ── #}
<div class=section> <div class=section>
@ -241,43 +311,6 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
</div> </div>
</div> </div>
{# ── Install : auto-detected platform panels (Phase 8.2 #500) ── #}
<div class=section>
<h2>📥 Installer R3 sur ton appareil</h2>
<p style="font-size:0.85rem;color:var(--dim);margin-bottom:0.8rem">
On a détecté <code>{{ install_platform }}</code> via ton navigateur — le panneau adapté
est ouvert en premier. Autre appareil ? Déplie le bon panneau ci-dessous.
</p>
<style>
.install-panel{background:rgba(0,255,65,0.04);border:1px solid rgba(0,255,65,0.25);
border-radius:6px;padding:0.6rem 0.9rem;margin:0.45rem 0}
.install-panel summary{cursor:pointer;font-size:0.95rem;color:var(--phos-peak,#00dd44);
list-style:none;outline:none}
.install-panel summary::-webkit-details-marker{display:none}
.install-panel[open] summary{margin-bottom:0.6rem}
.install-panel .emoji{font-size:1.1rem;margin-right:0.3rem}
.install-panel ol{padding-left:1.1rem;line-height:1.5;font-size:0.85rem}
.install-panel .btn{display:inline-block;padding:0.45rem 0.75rem;margin:0.25rem 0.2rem 0.25rem 0;
background:var(--purple,#6e40c9);color:#fff;text-decoration:none;border-radius:5px;
font-weight:bold;font-size:0.82rem}
.install-panel .btn.alt{background:transparent;border:1px solid var(--purple,#6e40c9);
color:var(--purple,#6e40c9)}
.install-panel code{background:rgba(0,0,0,0.4);padding:0.1rem 0.35rem;border-radius:3px;
font-size:0.8rem;color:var(--phos-peak,#00dd44)}
.install-panel .note{color:var(--dim,#888);font-size:0.78rem;margin-top:0.6rem;
border-left:2px solid var(--phos-hot,#ffb347);padding-left:0.6rem}
.install-panel img{max-width:100%;border-radius:5px;margin:0.4rem 0}
.install-panel pre{background:rgba(0,0,0,0.4);padding:0.5rem 0.7rem;border-radius:4px;
overflow-x:auto;font-size:0.78rem;margin:0.4rem 0}
</style>
{{ install_panels | safe }}
<p style="margin-top:0.8rem;font-size:0.78rem;color:var(--dim)">
Avantage R3 : marche hors-cabine (4G/5G, autre WiFi). Inclut tout le trafic (HTTPS).
Profil + CA bundlés. Le tunnel est révoquable à tout moment depuis Réglages.
Page équivalente standalone : <a href=/wg/onboard>/wg/onboard</a>.
</p>
</div>
{# ── Open Source ── #} {# ── Open Source ── #}
<div class=section> <div class=section>
<h2>🔓 Open Source — CMSD-1.0</h2> <h2>🔓 Open Source — CMSD-1.0</h2>
@ -286,8 +319,8 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
(audit citoyen possible, droits d'usage régis par licence CMSD). Pas de boîte noire. (audit citoyen possible, droits d'usage régis par licence CMSD). Pas de boîte noire.
</p> </p>
<div style="margin-top:0.6rem"> <div style="margin-top:0.6rem">
<a href="https://github.com/CyberMind-FR/secubox-deb" class=cta>📂 Code source GitHub</a> <a href="https://github.com/CyberMind-FR/secubox-deb" class=cta-sm>📂 Code source GitHub</a>
<a href="https://github.com/CyberMind-FR/secubox-deb/blob/master/LICENCE-CMSD-1.0.md" class="cta outline">📜 Licence CMSD-1.0</a> <a href="https://github.com/CyberMind-FR/secubox-deb/blob/master/LICENCE-CMSD-1.0.md" class="cta-sm outline">📜 Licence CMSD-1.0</a>
</div> </div>
</div> </div>
@ -314,7 +347,7 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
</div> </div>
</div> </div>
</div> </details>
<div class=footer> <div class=footer>
📡 Gondwana ToolBox · CyberMind / Gérald Kerma · Notre-Dame-du-Cruet (73130) · Savoie · France<br> 📡 Gondwana ToolBox · CyberMind / Gérald Kerma · Notre-Dame-du-Cruet (73130) · Savoie · France<br>
@ -322,14 +355,8 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
// DIY · Open Source · Open Audit // DIY · Open Source · Open Audit
</div> </div>
<style>
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}
.v.tick{animation:flash 0.6s}
@keyframes flash{0%{color:var(--gold);transform:scale(1.15)}100%{color:var(--phos-hot);transform:scale(1)}}
</style>
<script> <script>
// ── Live KPI auto-refresh from /cumulative-stats.json ── // ── Live KPI auto-refresh from /cumulative-stats.json (+ count-up on first paint) ──
(function(){ (function(){
function dig(o,path){ function dig(o,path){
var parts = path.split('.'); var parts = path.split('.');
@ -349,6 +376,23 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
var ss = String(d.getSeconds()).padStart(2,'0'); var ss = String(d.getSeconds()).padStart(2,'0');
return 'maj ' + hh+':'+mm+':'+ss; return 'maj ' + hh+':'+mm+':'+ss;
} }
// count-up: animate each KPI from 0 → its server-rendered value, once.
function countUp(el, target){
var start = 0, dur = 900, t0 = null;
function step(ts){
if (t0 === null) t0 = ts;
var p = Math.min(1, (ts - t0) / dur);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(start + (target - start) * eased);
if (p < 1) requestAnimationFrame(step);
else el.textContent = target;
}
requestAnimationFrame(step);
}
document.querySelectorAll('.kpi .v[data-live]').forEach(function(el){
var n = parseInt(el.textContent.trim(), 10);
if (!isNaN(n) && n > 0) countUp(el, n);
});
function refresh(){ function refresh(){
fetch('/cumulative-stats.json', {cache:'no-store'}).then(function(r){return r.json();}).then(function(d){ fetch('/cumulative-stats.json', {cache:'no-store'}).then(function(r){return r.json();}).then(function(d){
document.querySelectorAll('[data-live]').forEach(function(el){ document.querySelectorAll('[data-live]').forEach(function(el){
@ -379,16 +423,10 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
var fp = document.getElementById('cert-fp-r3'); var fp = document.getElementById('cert-fp-r3');
if (!btn) return; if (!btn) return;
// Display the WG CA fingerprint (use ?ca=wg flag if endpoint supports it,
// else fallback to default ca fingerprint).
fetch('/ca/fingerprint').then(function(r){return r.json();}).then(function(d){ fetch('/ca/fingerprint').then(function(r){return r.json();}).then(function(d){
fp.textContent = d.sha1 || d.sha256 || '?'; fp.textContent = d.sha1 || d.sha256 || '?';
}).catch(function(){fp.textContent='?';}); }).catch(function(){fp.textContent='?';});
// Phase 6.H : 3-step probe :
// 1) Detect if user is in WG R3 tunnel (probe our internal-only endpoint)
// 2) Probe an external HTTPS (verifies mitm decrypt + CA trust)
// 3) Combine results into a clear verdict
function runProbe(){ function runProbe(){
emj.textContent = '⏳'; emj.textContent = '⏳';
txt.innerHTML = 'Test en cours… (1/2 détection tunnel)'; txt.innerHTML = 'Test en cours… (1/2 détection tunnel)';
@ -416,31 +454,12 @@ LXC toolbox-mitm-wg 10.100.0.62 R3 WireGuard
} }
} }
// Phase 7 (#498) — same-origin HTTPS R3 probe.
// The previous probe loaded http://10.99.0.1:8088/qr/splash.png as
// an Image. iOS Safari blocks mixed content (HTTP from an HTTPS
// page) so the request never fired and inWG always stayed false.
// /wg/r3-check returns { tunnel: bool } based on the X-R3-Peer /
// XFF headers mitm-wg sets via the inject_xff addon.
fetch('/wg/r3-check?t=' + Date.now(), {cache: 'no-store'}) fetch('/wg/r3-check?t=' + Date.now(), {cache: 'no-store'})
.then(function(r){ return r.ok ? r.json() : {tunnel:false}; }) .then(function(r){ return r.ok ? r.json() : {tunnel:false}; })
.then(function(d){ .then(function(d){
inWG = !!(d && d.tunnel); inWG = !!(d && d.tunnel);
if (!inWG) { finalize(); return; } if (!inWG) { finalize(); return; }
txt.innerHTML = 'Tunnel R3 détecté ✓ — test 2/2 cert mitm…'; txt.innerHTML = 'Tunnel R3 détecté ✓ — test 2/2 cert mitm…';
// Step 2 : probe an external HTTPS host that mitm-wg DOES
// intercept (i.e. not in ignore_hosts). gstatic / google /
// apple / fbcdn are whitelisted so they pass through with
// their real cert ; useless to test CA trust. duckduckgo
// isn't whitelisted, so the TLS handshake is performed by
// mitm-wg with the R3 CA — it succeeds only if the iPhone
// trusts that CA.
//
// fetch(no-cors) resolves on any successful TLS handshake
// regardless of HTTP status, and rejects on cert / network
// error — exactly the signal we want. Image.onload was
// ambiguous : a 204 No Content reply (no image data) also
// triggered onerror, making CA-trusted look like CA-untrusted.
var extDone = false; var extDone = false;
fetch('https://duckduckgo.com/favicon.ico?t=' + Date.now(), fetch('https://duckduckgo.com/favicon.ico?t=' + Date.now(),
{mode: 'no-cors', cache: 'no-store'}) {mode: 'no-cors', cache: 'no-store'})

View File

@ -15,6 +15,35 @@ secubox-toolbox (2.6.17-1~bookworm1) bookworm; urgency=medium
-- Gerald KERMA <devel@cybermind.fr> Sat, 13 Jun 2026 13:00:00 +0200 -- Gerald KERMA <devel@cybermind.fr> Sat, 13 Jun 2026 13:00:00 +0200
secubox-toolbox (2.6.16-1~bookworm1) bookworm; urgency=medium
* Injected banner neon-tube redesign (#545) — inject_banner.py.
- New _LEVEL_THEME map: R3 (and the planned R4) get a neon-tube look
(dark glass bar, glowing tube border via layered box-shadow + neon
text-shadow on the title) ; R2 keeps the original amber flat bar.
- _banner_html_dynamic() takes the level and themes both the
CSP-strict (JS-less) and JS (dismissible) variants ; all inline CSS,
no injected <style>/@keyframes, ASCII/NCR-clean for legacy charsets.
- R4 theme defined but inert until _client_level() returns 'r4'.
-- Gerald KERMA <devel@cybermind.fr> Sat, 13 Jun 2026 12:30:00 +0200
secubox-toolbox (2.6.15-1~bookworm1) bookworm; urgency=medium
* kbin landing radical-simplify redesign (#543) — conf/landing.html.j2.
- Animated hero (gazing 👁️ + floating tracker dots) + one big
"✨ Protège-moi (R3)" CTA + "🕸️ Qui me piste ?" secondary.
- Auto-detected install panel pulled up front ("📥 Installe en 1 tap").
- KPIs / cert-probe / pitch / R0-R3 levels / charts / architecture /
open-source / contact moved behind an "En savoir plus" <details> fold.
- Quick-nav trimmed: removed CA iPhone / CA Android / QR profil cards
(they live inside the per-platform install panel now) — kept R3
Install / Mon rapport / Ma carto / Wiki / Cabine.
- Count-up animation on the live KPIs on first paint. All Jinja
variables + the live-stats and cert-probe scripts preserved.
-- Gerald KERMA <devel@cybermind.fr> Sat, 13 Jun 2026 12:00:00 +0200
secubox-toolbox (2.6.14-1~bookworm1) bookworm; urgency=medium secubox-toolbox (2.6.14-1~bookworm1) bookworm; urgency=medium
* Serve the browser ToolBoX extension .xpi from the toolbox (#532). * Serve the browser ToolBoX extension .xpi from the toolbox (#532).

View File

@ -404,8 +404,40 @@ def _detect_csp_strict(flow: http.HTTPFlow) -> bool:
return False return False
# Per-level visual theme (#545). R3 — and the planned R4 — get the
# neon-tube treatment (dark glass bar, glowing tube border + neon
# text-shadow). R2 keeps the original amber flat bar. All values are inline
# CSS only (no injected <style>/@keyframes) so it survives strict CSP and
# arbitrary third-party pages.
_LEVEL_THEME = {
"r2": {
"neon": False,
"bg": "linear-gradient(90deg,#ffb347 60%,#0a0a0f 100%)",
"fg": "#0a0a0f", "edge": "#C04E24", "accent": "#ffb347",
"glow": "", "link": "#0a5840", "chip": "rgba(0,0,0,0.1)",
},
"r3": {
"neon": True,
"bg": "rgba(8,8,14,0.95)",
"fg": "#e8e6d9", "edge": "#00d4ff", "accent": "#00d4ff",
"glow": "rgba(0,212,255,0.45)", "link": "#00d4ff",
"chip": "rgba(0,212,255,0.12)",
},
# planned (#545): R4 drops in with its own neon colour — inert until
# _client_level() can return 'r4'.
"r4": {
"neon": True,
"bg": "rgba(12,8,16,0.96)",
"fg": "#e8e6d9", "edge": "#ff3df0", "accent": "#ff3df0",
"glow": "rgba(255,61,240,0.45)", "link": "#ff3df0",
"chip": "rgba(255,61,240,0.12)",
},
}
def _banner_html_dynamic(sha1: str, ctx: dict, csp_strict: bool, def _banner_html_dynamic(sha1: str, ctx: dict, csp_strict: bool,
report_url: str, level_label: str) -> bytes: report_url: str, level_label: str,
level: str = "r2") -> bytes:
"""Render the injection payload. """Render the injection payload.
Two flavors depending on CSP strictness : Two flavors depending on CSP strictness :
@ -455,22 +487,47 @@ def _banner_html_dynamic(sha1: str, ctx: dict, csp_strict: bool,
# Static emojis used in the left-side text # Static emojis used in the left-side text
SAT_EMOJI = "&#x1F4E1;" # 📡 satellite dish SAT_EMOJI = "&#x1F4E1;" # 📡 satellite dish
# ── theme resolution (#545) : R3/R4 neon tube, R2 amber flat ──
th = _LEVEL_THEME.get(level, _LEVEL_THEME["r2"])
_base = (
"position:fixed!important;top:0!important;left:0!important;right:0!important;"
"z-index:2147483647!important;font-family:Menlo,Consolas,monospace!important;"
"padding:6px 12px!important;font-size:11px!important;line-height:1.4!important;"
"text-align:left!important;display:flex!important;justify-content:space-between!important;"
"align-items:center!important;gap:8px!important;"
)
if th["neon"]:
# glowing glass tube : outer + inset accent glow, neon edge
bar_css = (
_base
+ f"background:{th['bg']}!important;color:{th['fg']}!important;"
+ f"border-bottom:2px solid {th['edge']}!important;"
+ f"box-shadow:0 0 10px {th['accent']},0 3px 22px {th['glow']},"
f"inset 0 -1px 6px {th['glow']}!important;"
+ "backdrop-filter:blur(3px)!important;"
)
title_css = f"color:{th['accent']};text-shadow:0 0 6px {th['accent']},0 0 12px {th['accent']}"
else:
bar_css = (
_base
+ f"background:{th['bg']}!important;color:{th['fg']}!important;"
+ f"border-bottom:2px solid {th['edge']}!important;"
+ "box-shadow:0 2px 8px rgba(0,0,0,0.3)!important;"
)
title_css = ""
code_css = f"background:{th['chip']};padding:1px 4px;border-radius:2px"
link_css = f"color:{th['link']};text-decoration:underline;font-weight:bold"
title_attr = f" style=\"{title_css}\"" if title_css else ""
if csp_strict: if csp_strict:
# JS-less HTML banner — visible only, no close button. !important # JS-less HTML banner — visible only, no close button. !important
# everywhere so page CSS can't override the fixed positioning. # everywhere so page CSS can't override the fixed positioning.
# NCRs work even when page charset is iso-8859-1. # NCRs work even when page charset is iso-8859-1.
html = ( html = (
f"<div id=\"gondwana-mitm-banner\" role=\"status\" " f"<div id=\"gondwana-mitm-banner\" role=\"status\" style=\"{bar_css}\">"
f"style=\"position:fixed!important;top:0!important;left:0!important;right:0!important;" f"<span><b{title_attr}>{SAT_EMOJI} ToolBoX {level_label}</b> &#xB7; CA SHA1: "
f"z-index:2147483647!important;" f"<code style=\"{code_css}\">{sha1[:23]}</code>"
f"background:linear-gradient(90deg,#ffb347 60%,#0a0a0f 100%)!important;" f" &#xB7; <a href=\"{report_url}\" style=\"{link_css}\">Mon rapport</a></span>"
f"color:#0a0a0f!important;font-family:Menlo,Consolas,monospace!important;"
f"padding:6px 12px!important;font-size:11px!important;line-height:1.4!important;"
f"border-bottom:2px solid #C04E24!important;text-align:left!important;"
f"display:flex!important;justify-content:space-between!important;align-items:center!important;gap:8px!important\">"
f"<span><b>{SAT_EMOJI} ToolBoX {level_label}</b> &#xB7; CA SHA1: "
f"<code style=\"background:rgba(0,0,0,0.1);padding:1px 4px;border-radius:2px\">{sha1[:23]}</code>"
f" &#xB7; <a href=\"{report_url}\" style=\"color:#0a5840;text-decoration:underline;font-weight:bold\">Mon rapport</a></span>"
f"<span style=\"color:#e8e6d9;background:rgba(0,0,0,0.4);padding:3px 8px;border-radius:3px\">" f"<span style=\"color:#e8e6d9;background:rgba(0,0,0,0.4);padding:3px 8px;border-radius:3px\">"
f"{right_text}" f"{right_text}"
f" &#xB7; <b style=\"color:{grade_color};background:#0a0a0f;padding:1px 5px;border-radius:2px\">{grade}</b>" f" &#xB7; <b style=\"color:{grade_color};background:#0a0a0f;padding:1px 5px;border-radius:2px\">{grade}</b>"
@ -489,6 +546,12 @@ def _banner_html_dynamic(sha1: str, ctx: dict, csp_strict: bool,
level_js = _json.dumps(level_label) level_js = _json.dumps(level_label)
sat_js = _json.dumps(SAT_EMOJI) sat_js = _json.dumps(SAT_EMOJI)
mid_js = _json.dumps(" &#xB7; ") mid_js = _json.dumps(" &#xB7; ")
# theme (#545) — JS-encoded so the same neon/amber styling applies here
bar_css_js = _json.dumps(bar_css)
title_attr_js = _json.dumps(title_attr)
code_css_js = _json.dumps(code_css)
link_css_js = _json.dumps(link_css)
close_col_js = _json.dumps(th["fg"])
js = f""" js = f"""
(function(){{ (function(){{
@ -499,14 +562,7 @@ def _banner_html_dynamic(sha1: str, ctx: dict, csp_strict: bool,
var b=document.createElement('div'); var b=document.createElement('div');
b.id='gondwana-mitm-banner'; b.id='gondwana-mitm-banner';
b.setAttribute('role','status'); b.setAttribute('role','status');
b.style.cssText='position:fixed!important;top:0!important;left:0!important;right:0!important;'+ b.style.cssText={bar_css_js};
'z-index:2147483647!important;'+
'background:linear-gradient(90deg,#ffb347 60%,#0a0a0f 100%)!important;'+
'color:#0a0a0f!important;font-family:Menlo,Consolas,monospace!important;'+
'padding:6px 12px!important;font-size:11px!important;line-height:1.4!important;'+
'border-bottom:2px solid #C04E24!important;box-shadow:0 2px 8px rgba(0,0,0,0.3)!important;'+
'text-align:left!important;display:flex!important;'+
'justify-content:space-between!important;align-items:center!important;gap:8px!important';
var rightText={right_js}; var rightText={right_js};
var grade={grade_js}; var grade={grade_js};
var gradeCol={grade_col_js}; var gradeCol={grade_col_js};
@ -515,14 +571,18 @@ def _banner_html_dynamic(sha1: str, ctx: dict, csp_strict: bool,
var level={level_js}; var level={level_js};
var SAT={sat_js}; var SAT={sat_js};
var MID={mid_js}; var MID={mid_js};
b.innerHTML='<span><b>'+SAT+' ToolBoX '+level+'</b>'+MID+'CA SHA1: '+ var TITLE_ATTR={title_attr_js};
'<code style=\"background:rgba(0,0,0,0.1);padding:1px 4px;border-radius:2px\">'+sha1+'</code>'+ var CODE_CSS={code_css_js};
MID+'<a href=\"'+reportUrl+'\" style=\"color:#0a5840;text-decoration:underline;font-weight:bold\">Mon rapport</a></span>'+ var LINK_CSS={link_css_js};
var CLOSE_COL={close_col_js};
b.innerHTML='<span><b'+TITLE_ATTR+'>'+SAT+' ToolBoX '+level+'</b>'+MID+'CA SHA1: '+
'<code style=\"'+CODE_CSS+'\">'+sha1+'</code>'+
MID+'<a href=\"'+reportUrl+'\" style=\"'+LINK_CSS+'\">Mon rapport</a></span>'+
'<span style=\"display:flex;align-items:center;gap:8px\">'+ '<span style=\"display:flex;align-items:center;gap:8px\">'+
'<span style=\"color:#e8e6d9;background:rgba(0,0,0,0.4);padding:3px 8px;border-radius:3px\">'+ '<span style=\"color:#e8e6d9;background:rgba(0,0,0,0.4);padding:3px 8px;border-radius:3px\">'+
rightText+MID+'<b style=\"color:'+gradeCol+';background:#0a0a0f;padding:1px 5px;border-radius:2px\">'+grade+'</b>'+ rightText+MID+'<b style=\"color:'+gradeCol+';background:#0a0a0f;padding:1px 5px;border-radius:2px\">'+grade+'</b>'+
'</span>'+ '</span>'+
'<a href=\"javascript:void(0)\" onclick=\"document.getElementById(\\'gondwana-mitm-banner\\').style.display=\\'none\\';document.body.style.paddingTop=0\" style=\"color:#0a0a0f;text-decoration:none;font-weight:bold;cursor:pointer\">[&#xD7;]</a>'+ '<a href=\"javascript:void(0)\" onclick=\"document.getElementById(\\'gondwana-mitm-banner\\').style.display=\\'none\\';document.body.style.paddingTop=0\" style=\"color:'+CLOSE_COL+';text-decoration:none;font-weight:bold;cursor:pointer\">[&#xD7;]</a>'+
'</span>'; '</span>';
if(document.body.firstChild){{document.body.insertBefore(b,document.body.firstChild)}} if(document.body.firstChild){{document.body.insertBefore(b,document.body.firstChild)}}
else{{document.body.appendChild(b)}} else{{document.body.appendChild(b)}}
@ -610,8 +670,9 @@ class InjectBanner:
csp_strict = _detect_csp_strict(flow) csp_strict = _detect_csp_strict(flow)
report_url = _report_url_for(flow) report_url = _report_url_for(flow)
level_label = _level_label(flow) level_label = _level_label(flow)
level = _client_level(flow)
snippet = _banner_html_dynamic(_CA_SHA1, ctx, csp_strict, snippet = _banner_html_dynamic(_CA_SHA1, ctx, csp_strict,
report_url, level_label) report_url, level_label, level)
except Exception as e: except Exception as e:
log.warning("banner compute failed for %s: %s", flow.request.host, e) log.warning("banner compute failed for %s: %s", flow.request.host, e)
# Fail-open : skip injection rather than break the page # Fail-open : skip injection rather than break the page