From 40fbd64f11bba3834493b3c9cb838be74a3c0b92 Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:35:00 -0400 Subject: [PATCH] feat(surface): BearTrap + BearWall dedicated surface page + counter chiclet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both actors were emitting the beartrap-detection observer signal but the user had no dedicated place to see caught probes / blocked vendors / canary exfiltrations. Hamburger menu items I added in #142 anchored at bearnet.html#beartrap — a page that didn't exist. Adds: - settings/start/beartrap.html — unified surface page, 3 tabs: * Fingerprint probes (BearTrap) — kind: 'fingerprint' * Blocked vendors (BearWall) — kind: 'vendor-blocked' * Canary tokens — kind: 'canary' Polls http://127.0.0.1:8093/honeypot (already exposed by capture-sidecar at server.rs:68) every 2s. Deep-link support: #beartrap|#bearwall|#canary jump to the corresponding tab. credentials:'omit', cache:'no-store'. Empty-state explainers so first-run users understand what each tab means. - settings/start/bearstart-autoconfig.js — extend the existing observer handler that already tracked fingerprint origins. Now tracks all three kinds, sets data-block-count on the nav-bar button, updates tooltip with a per-kind summary (e.g. '2 fingerprint attempts, 5 vendor requests blocked — open BearTrap'). Chiclet visible from any tab. - scripts/stage-bearnet.sh — stage the new beartrap.html into the packaged app so resource://bearstart/beartrap.html resolves. - settings/start/bearstart-autoconfig.js — update the hamburger appMenu BearTrap + BearWall entries to point at beartrap.html (was pointing at the non-existent bearnet.html#beartrap anchor). The observer wiring is unchanged from what BearTrapMonitor.sys.mjs already emits (line 97 for canary/canary matches, line 289 for vendor-blocked → same 'beartrap-detection' topic with kind fields). No new actor plumbing needed. --- scripts/stage-bearnet.sh | 2 +- settings/start/bearstart-autoconfig.js | 39 +++-- settings/start/beartrap.html | 221 +++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 14 deletions(-) create mode 100644 settings/start/beartrap.html diff --git a/scripts/stage-bearnet.sh b/scripts/stage-bearnet.sh index 3f75524..bbf32aa 100755 --- a/scripts/stage-bearnet.sh +++ b/scripts/stage-bearnet.sh @@ -28,7 +28,7 @@ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # 1. The pages, loose where resource://bearstart/ resolves. bs="$GRE/browser/bearstart" mkdir -p "$bs" -for f in bearbrowser-start.html bearnet.html cockpit-waiter.html world.json dm-sans-latin.woff2 dm-sans-latin-ext.woff2; do +for f in bearbrowser-start.html bearnet.html beartrap.html cockpit-waiter.html world.json dm-sans-latin.woff2 dm-sans-latin-ext.woff2; do [ -f "$REPO/settings/start/$f" ] && cp "$REPO/settings/start/$f" "$bs/" done # Wordmark SVG referenced by bearbrowser-start.html — sits next to the HTML so diff --git a/settings/start/bearstart-autoconfig.js b/settings/start/bearstart-autoconfig.js index dc6ce11..8aa26dc 100644 --- a/settings/start/bearstart-autoconfig.js +++ b/settings/start/bearstart-autoconfig.js @@ -325,27 +325,40 @@ try { onWindowTitleChange() {}, }); - // Badge the BearNet button when BearTrap catches fingerprinting — so the - // honeypot is visible from anywhere, not just inside the panel. + // Badge the BearNet button when BearTrap catches fingerprinting OR when + // BearWall blocks a vendor host — so the honeypot + firewall are visible + // from anywhere, not just inside the panel. Total count on tooltip; the + // per-kind counts show up on the BearTrap surface page. try { - const fpOrigins = new Set(); + const fpOrigins = new Set(); // fingerprint probe origins seen + const vendorHosts = new Set(); // BearWall-blocked hosts seen + const canaryOrigins = new Set(); // canary-token exfiltration origins + const summary = () => { + const bits = []; + if (fpOrigins.size) bits.push(fpOrigins.size + " fingerprint attempt" + (fpOrigins.size === 1 ? "" : "s")); + if (vendorHosts.size) bits.push(vendorHosts.size + " vendor request" + (vendorHosts.size === 1 ? "" : "s") + " blocked"); + if (canaryOrigins.size) bits.push(canaryOrigins.size + " canary exfiltration" + (canaryOrigins.size === 1 ? "" : "s")); + return bits.length ? bits.join(", ") + " — open BearTrap" : "BearNet — see and block what this browser is talking to"; + }; + const total = () => fpOrigins.size + vendorHosts.size + canaryOrigins.size; Services.obs.addObserver( { observe(subj) { try { const d = subj && subj.wrappedJSObject; - if (d && d.kind === "fingerprint" && d.origin) fpOrigins.add(d.origin); - const n = fpOrigins.size; + if (!d) return; + if (d.kind === "fingerprint" && d.origin) fpOrigins.add(d.origin); + if (d.kind === "vendor-blocked" && (d.dest || d.host)) vendorHosts.add(d.dest || d.host); + if (d.kind === "canary" && d.origin) canaryOrigins.add(d.origin); + const n = total(); + const active = fpOrigins.size + vendorHosts.size + canaryOrigins.size > 0; const e2 = Services.wm.getEnumerator("navigator:browser"); while (e2.hasMoreElements()) { const b = e2.getNext().document.getElementById("bearnet-button"); if (b) { - b.setAttribute( - "tooltiptext", - n + " fingerprinting attempt" + (n === 1 ? "" : "s") + - " caught — open BearNet" - ); - b.setAttribute("beartrap", "1"); + b.setAttribute("tooltiptext", summary()); + b.setAttribute("beartrap", active ? "1" : "0"); + b.setAttribute("data-block-count", String(n)); } } } catch (e) {} @@ -375,8 +388,8 @@ try { if (!Services.appinfo.inSafeMode) { const ENTRIES = [ { id: "bearbrowser-appmenu-bearnet", label: "BearNet — Network Monitor", url: "resource://bearstart/bearnet.html", accessKey: "N" }, - { id: "bearbrowser-appmenu-beartrap", label: "BearTrap — Fingerprint Log", url: "resource://bearstart/bearnet.html#beartrap", accessKey: "T" }, - { id: "bearbrowser-appmenu-bearwall", label: "BearWall — Blocked Vendors", url: "resource://bearstart/bearnet.html#bearwall", accessKey: "W" }, + { id: "bearbrowser-appmenu-beartrap", label: "BearTrap — Fingerprint Log", url: "resource://bearstart/beartrap.html#beartrap", accessKey: "T" }, + { id: "bearbrowser-appmenu-bearwall", label: "BearWall — Blocked Vendors", url: "resource://bearstart/beartrap.html#bearwall", accessKey: "W" }, { id: "bearbrowser-appmenu-cockpit", label: "Cockpit", url: "resource://bearbrowser-cockpit/index.html", accessKey: "C" }, ]; const ensureAppMenuSection = (win) => { diff --git a/settings/start/beartrap.html b/settings/start/beartrap.html new file mode 100644 index 0000000..01bf6d6 --- /dev/null +++ b/settings/start/beartrap.html @@ -0,0 +1,221 @@ + + + + +BearTrap — Fingerprint & Vendor Log + + + + + +
+ +
+ +

BearTrap what the honeypot has caught this session

+ +
connecting to loopback sidecar…
+ +
+ + + +
+ +
+

Every fingerprinting probe the browser sees is caught here — Canvas, WebGL, AudioContext, font enumeration, timing, hardware-concurrency, and more. The origin that ran the probe is recorded so you can see who is trying to identify you.

+
No fingerprint probes caught yet in this session.
+
+ +
+

BearWall enforces a hard denylist of upstream telemetry / update / crash / metrics endpoints. When it blocks a request, the host + count is logged here. If the honeypot sink is enabled, blocked requests are redirected to a loopback endpoint that returns 204 — recorded here.

+
No vendor requests blocked yet in this session.
+
+ +
+

Canary tokens are per-origin sentinel strings BearTrap injects into hidden fields. If a request body contains one, a script scraped the field and is exfiltrating — the origin, destination, and matched token are recorded here.

+
No canary-token exfiltrations detected in this session.
+
+ + + + +