From 9b7c6f67f1f7e45c77ba73d107537e331e4b59c3 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sat, 13 Jun 2026 12:32:17 +0200 Subject: [PATCH] fix(webext): 'redeclaration of const ext' SyntaxError + PNG icons, v0.1.1 (ref #532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real cause of the broken extension: api.js and background.js BOTH did `const ext = …`. In a Firefox event page the background.scripts array loads them into one shared scope (same via importScripts in a Chromium SW), and popup.js's `const ext` collided with api.js's in the popup page realm too — 'redeclaration of const ext' aborts the script. web-ext lint misses it because it never concatenates/executes the scripts. Now only api.js declares `const ext`; background.js and popup.js use `api.ext`. Also (defensive + Chromium-correct): replace the SVG action/extension icon with rasterised PNGs (48/128) and keep the SVG out of the package, so Firefox never renders SVG in chrome UI. Bump to v0.1.1; the /wg/toolbox.xpi endpoint + fetch helper + docs point at webext-v0.1.1. Co-Authored-By: Claude Opus 4.8 --- .claude/TODO.md | 2 +- .claude/WIP.md | 2 +- clients/webext-toolbox/README.md | 2 +- clients/webext-toolbox/background.js | 19 ++++++++++-------- clients/webext-toolbox/build.sh | 6 ++++-- clients/webext-toolbox/icons/icon-128.png | Bin 0 -> 1508 bytes clients/webext-toolbox/icons/icon-48.png | Bin 0 -> 618 bytes clients/webext-toolbox/manifest.json | 10 ++++----- clients/webext-toolbox/popup/popup.js | 14 +++++++------ .../sbin/secubox-toolbox-fetch-xpi | 2 +- .../secubox-toolbox/secubox_toolbox/api.py | 2 +- wiki/Browser-Extension.md | 2 +- 12 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 clients/webext-toolbox/icons/icon-128.png create mode 100644 clients/webext-toolbox/icons/icon-48.png diff --git a/.claude/TODO.md b/.claude/TODO.md index 145a1c27..1d36abb1 100644 --- a/.claude/TODO.md +++ b/.claude/TODO.md @@ -16,7 +16,7 @@ - [x] **#532 browser extension** (`clients/webext-toolbox/`) — MV3 Firefox `.xpi`/Chromium; live tracker badge + popup mini Round-Eye graph over `/social/*`; `GET /wg/toolbox.xpi` + fetch helper + `build-webext.yml`. -- [x] **#532 release** — tag `webext-v0.1.0` published the `.xpi` +- [x] **#532 release** — tag `webext-v0.1.1` published the `.xpi` (downloadable, verified 200). `make_latest:false` + tag-pinned URL so it doesn't steal "Latest" from the Android APK release. - [ ] **release signing** — Android keystore + AMO `.xpi` signing secrets in CI diff --git a/.claude/WIP.md b/.claude/WIP.md index bbc2c2c0..dade7e02 100644 --- a/.claude/WIP.md +++ b/.claude/WIP.md @@ -23,7 +23,7 @@ le navigateur : badge live des traceurs + popup. panneaux onboard, helper `secubox-toolbox-fetch-xpi`, postinst dir. - **CI** : `build-webext.yml` — `web-ext lint` (0 erreur, 2 warnings bénins) + build, artifact, release asset sur tag `webext-v*`. -- **Release** (PR #540 + #541, mergées) : tag `webext-v0.1.0` poussé → +- **Release** (PR #540 + #541, mergées) : tag `webext-v0.1.1` poussé → CI a publié `secubox-toolbox-webext.xpi` (téléchargeable, vérifié 200). `make_latest:false` + URL **tag-pinned** dans `/wg/toolbox.xpi` + `secubox-toolbox-fetch-xpi` pour ne pas voler le pointeur "Latest" à la diff --git a/clients/webext-toolbox/README.md b/clients/webext-toolbox/README.md index fed74e13..3f9ab9a6 100644 --- a/clients/webext-toolbox/README.md +++ b/clients/webext-toolbox/README.md @@ -31,7 +31,7 @@ to your cabine over the R3 tunnel — no third-party calls. Published release `.xpi` (downloadable directly): ``` -https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.0/secubox-toolbox-webext.xpi +https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.1/secubox-toolbox-webext.xpi ``` The toolbox also serves it from the cabine: diff --git a/clients/webext-toolbox/background.js b/clients/webext-toolbox/background.js index db09f971..198fdbe1 100644 --- a/clients/webext-toolbox/background.js +++ b/clients/webext-toolbox/background.js @@ -12,15 +12,18 @@ if (typeof importScripts === "function") { try { importScripts("api.js"); } catch (_) {} } +// NB: do NOT declare `const ext` here — api.js already declares it in the +// shared script scope (event page) / worker global (importScripts), and a +// second `const ext` is a "redeclaration of const ext" SyntaxError that +// kills the whole background script. Use api.ext instead. const api = globalThis.SbxApi; -const ext = globalThis.browser || globalThis.chrome; const ALARM = "sbx-refresh"; const PERIOD_MIN = 1; // poll the cabine once a minute function setBadge(text, color) { try { - ext.action.setBadgeText({ text: text || "" }); + api.ext.action.setBadgeText({ text: text || "" }); if (color) ext.action.setBadgeBackgroundColor({ color }); } catch (_) {} } @@ -59,18 +62,18 @@ async function refresh() { } } -ext.runtime.onInstalled.addListener(() => { - ext.alarms.create(ALARM, { periodInMinutes: PERIOD_MIN }); +api.ext.runtime.onInstalled.addListener(() => { + api.ext.alarms.create(ALARM, { periodInMinutes: PERIOD_MIN }); refresh(); }); -ext.runtime.onStartup && ext.runtime.onStartup.addListener(() => { - ext.alarms.create(ALARM, { periodInMinutes: PERIOD_MIN }); +api.ext.runtime.onStartup && api.ext.runtime.onStartup.addListener(() => { + api.ext.alarms.create(ALARM, { periodInMinutes: PERIOD_MIN }); refresh(); }); -ext.alarms.onAlarm.addListener((a) => { if (a.name === ALARM) refresh(); }); +api.ext.alarms.onAlarm.addListener((a) => { if (a.name === ALARM) refresh(); }); // popup asks for an immediate refresh after pairing / config change -ext.runtime.onMessage.addListener((msg, _sender, sendResponse) => { +api.ext.runtime.onMessage.addListener((msg, _sender, sendResponse) => { if (msg && msg.type === "refresh") { refresh().then(() => sendResponse({ ok: true })); return true; // async response diff --git a/clients/webext-toolbox/build.sh b/clients/webext-toolbox/build.sh index 4059cf27..eda0a137 100755 --- a/clients/webext-toolbox/build.sh +++ b/clients/webext-toolbox/build.sh @@ -14,9 +14,11 @@ OUT="secubox-toolbox-webext-${VER}.xpi" rm -f "$OUT" # -FS = sync (drop stale entries) ; exclude VCS, dotfiles, build script, -# any previously built artefact, and docs. +# any previously built artefact, docs, and the SVG icon source (only the +# rasterised PNGs are referenced by the manifest — keep SVG out of the +# package so Firefox never renders it in chrome UI). zip -r -FS "$OUT" . \ - -x '*.git*' '*/.*' 'build.sh' '*.xpi' 'README.md' >/dev/null + -x '*.git*' '*/.*' 'build.sh' '*.xpi' 'README.md' 'icons/icon.svg' >/dev/null echo "built $OUT ($(stat -c%s "$OUT" 2>/dev/null || stat -f%z "$OUT") bytes)" echo "Firefox: about:debugging → This Firefox → Load Temporary Add-on → pick the .xpi (or manifest.json)." diff --git a/clients/webext-toolbox/icons/icon-128.png b/clients/webext-toolbox/icons/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..4f77239037a702d99dd0bd8500a31ac5c53cf58d GIT binary patch literal 1508 zcma*ndpOez7zgm*)?^!{G;_@io0BC}VB4T-Z-YR)LvOcycAG#c$V=k(|K<2=vjeZPOaf4sMCWChq{alt)I4*`tUWglb&fYyBb3Jp6X-vDsWuK-2Q)#(N{z7`t_?nm2p zSP>lT!6Orrwk;7}!1#$gVQ<~qQU^JS{({67gzcFp{+@iVV_WKI&9 zcea=Ewihrgd>o+8D?EXZ1yR&tus0+g>LzecT^+V4KZf59vCN)A*V{9v_jU4;zDms& z>{4v+0!CY?tQ!DsfW>)P_R*UTXa^Afq*qW<+0wn$+i zq}yQk(Zo9%F^NnL)G1apxEiM+v;YaFHGB8pTUMOSfWR!IO$>o%z_JU zU>&If?bN2V)-4~*iKNV;&}l1hDd1qih@&{0t@PCc~1@$!px zxtCtfigeZMVrWsH+-DZ$r)QQh&#XJ1^ZenlpdXbYt>mY)HlCD3HfTO4R;;cB(^i+f zI)ux_QC;SdK42UnVrJ7oqz9JjHIKZhOv@3^0vJ}L=N1ze$5d3&sOPk`I3MBv@~IBn zZ64vnpN3!WTj`@?L%&huOQ*Rjjx!;OF=fn5+?mN=YLp!6HUCn`vGox2o|8=&ZPHl8 zFvCxHDvLPyxjaROK`&9JC8$_|E-_>)L5b@}ZpN$aB%GjXoZnb^J{iY8@$SP(5ETh! z8~uv{a$Pz?ejRK!*mFT6>?a=$n~8ON@9^1T8FJ*yT}gZ&`Qh*&{#^pdXe*2`Xw|jn zWYslBTIl-+!@ic(1_&o`ga*!E;-2Osq8o5s)Pl7w+On8l4@P>e9rh-`T7ir$Y%#zn z>=$!PLZukJso;o?(V&jqc9wk{NfRNrkgz`=-bl)iWHLLCGy14*23|3s&Gn}C+}3Vy=1x9)g8e@N~GeV)DaEjbJR{eP^Q z&HQP#hgy)%5L-7ja(5n31x`MAG!M>Z`GMJRF3Nr^3l6~kJZJ4U0KPb;0~`oCL))^- z8z9vihxocifeM1sDEioPkcqHadc+5@30wHYxoel-aV|USOVr3N_{;=kd7wuDaQSzB$Y74Y=>x>rzV$%KQt$8?1}~ literal 0 HcmV?d00001 diff --git a/clients/webext-toolbox/icons/icon-48.png b/clients/webext-toolbox/icons/icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..e2aad184f9bc99b440bb8d1dcac467fcdfa5f631 GIT binary patch literal 618 zcmV-w0+s!VP)H5c4DxMIkVJg^9VPVq!thpk}6*2?W6*_7!RnQ&cR$??XUK%j6l!3{+LPjA+EC zKk6bL$ZGYuH`W0ksMqWhq`CGh;MH;VMQXnSje)Xh43v%5K$@(PChHSODVCQtAO<+4 z37fnZu97ZheXI8m?mwL5)iyGkfze|E0GK920KDMd?LK#W`QB1~Ml=+KF<#$706-94 zaq{ds@nY7;d@=?A+&w(!3P|@iY>c4wd59%Lv1yV_FfyP@CMJ@Fw>zuM>)p1Sc}+%$ zD2$bp!l5-FikAaMVGQ7J#>jvyT!xI00WpS2PAdbZflS2{WMVXUQI?BoZlwVVpql}) zbW~--m6KdbwmNeAfbEtD@FL~848x>w7BRbwXTG@Sogs0eu_QlxB5U=7&Z z3tXM+dcrXpat+wjKDUdsJ1chPbvuxWCMSIWFz*F&BRe*?@h)UqPP=VaxeJNoQ5YY( zu#B@#oEK0R*z_<*L}83+GPKGeVtpD_AtqCq5XFnJ=96*eAZK$A7XD(ZNVn#oqx94F z_%L?8T53P%?3+0kPh?-+-oG)O31^R#t{xF>X^s*uk;~ document.getElementById(id); function show(which) { @@ -55,7 +57,7 @@ function paint(data) { async function load() { const cfg = await api.getConfig(); - $("ver").textContent = "v" + (ext.runtime.getManifest().version || ""); + $("ver").textContent = "v" + (api.ext.runtime.getManifest().version || ""); // tunnel indicator api.r3Check(cfg.host).then((r) => { @@ -96,7 +98,7 @@ $("pairBtn").addEventListener("click", async () => { await api.setConfig({ host }); const token = await api.pair(host); await api.setConfig({ token }); - ext.runtime.sendMessage({ type: "refresh" }); + api.ext.runtime.sendMessage({ type: "refresh" }); await load(); } catch (e) { $("pairMsg").textContent = e.message + " (es-tu sur le tunnel ?)"; @@ -105,11 +107,11 @@ $("pairBtn").addEventListener("click", async () => { $("openFull").addEventListener("click", async () => { const cfg = await api.getConfig(); - ext.tabs.create({ url: api.socialUrl(cfg.host, cfg.token) }); + api.ext.tabs.create({ url: api.socialUrl(cfg.host, cfg.token) }); }); $("pdf").addEventListener("click", async () => { const cfg = await api.getConfig(); - ext.tabs.create({ url: api.reportUrl(cfg.host, cfg.token) }); + api.ext.tabs.create({ url: api.reportUrl(cfg.host, cfg.token) }); }); $("wipe").addEventListener("click", async () => { if (!confirm("Effacer toutes tes données de cartographie sur la cabine ?")) return; @@ -125,7 +127,7 @@ $("wipe").addEventListener("click", async () => { }); $("settings").addEventListener("click", (e) => { e.preventDefault(); - ext.runtime.openOptionsPage(); + api.ext.runtime.openOptionsPage(); }); load(); diff --git a/packages/secubox-toolbox/sbin/secubox-toolbox-fetch-xpi b/packages/secubox-toolbox/sbin/secubox-toolbox-fetch-xpi index 5a75e2ee..135df0a9 100755 --- a/packages/secubox-toolbox/sbin/secubox-toolbox-fetch-xpi +++ b/packages/secubox-toolbox/sbin/secubox-toolbox-fetch-xpi @@ -16,7 +16,7 @@ DEST_DIR="/var/lib/secubox/toolbox/webext" DEST="${DEST_DIR}/secubox-toolbox-webext.xpi" # Tag-pinned (not /latest/): the webext release is make_latest:false so it # doesn't steal "latest" from the Android APK release. Bump on new webext-v*. -RELEASE_URL="https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.0/secubox-toolbox-webext.xpi" +RELEASE_URL="https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.1/secubox-toolbox-webext.xpi" log() { logger -t "$MODULE" -- "$*" 2>/dev/null || echo "[$MODULE] $*" >&2; } diff --git a/packages/secubox-toolbox/secubox_toolbox/api.py b/packages/secubox-toolbox/secubox_toolbox/api.py index b8e2aa89..440b7fde 100644 --- a/packages/secubox-toolbox/secubox_toolbox/api.py +++ b/packages/secubox-toolbox/secubox_toolbox/api.py @@ -1374,7 +1374,7 @@ async def wg_toolbox_apk() -> Response: _WEBEXT_XPI = Path("/var/lib/secubox/toolbox/webext/secubox-toolbox-webext.xpi") _WEBEXT_XPI_RELEASE = ( "https://github.com/CyberMind-FR/secubox-deb/releases/download/" - "webext-v0.1.0/secubox-toolbox-webext.xpi" + "webext-v0.1.1/secubox-toolbox-webext.xpi" ) diff --git a/wiki/Browser-Extension.md b/wiki/Browser-Extension.md index 4e8b9200..5e577d78 100644 --- a/wiki/Browser-Extension.md +++ b/wiki/Browser-Extension.md @@ -18,7 +18,7 @@ R3 tunnel — no third-party calls. Published release `.xpi` (downloadable directly): ``` -https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.0/secubox-toolbox-webext.xpi +https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.1/secubox-toolbox-webext.xpi ``` The toolbox also serves it from the cabine: