mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-06-30 15:54:07 +00:00
Compare commits
4 Commits
94f40c9162
...
52e51b2766
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52e51b2766 | ||
| 87614c7143 | |||
|
|
93cf0ebafa | ||
| 6e83a4a065 |
|
|
@ -31,7 +31,7 @@ to your cabine over the R3 tunnel — no third-party calls.
|
||||||
Published release `.xpi` (downloadable directly):
|
Published release `.xpi` (downloadable directly):
|
||||||
|
|
||||||
```
|
```
|
||||||
https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.1/secubox-toolbox-webext.xpi
|
https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.2/secubox-toolbox-webext.xpi
|
||||||
```
|
```
|
||||||
|
|
||||||
The toolbox also serves it from the cabine:
|
The toolbox also serves it from the cabine:
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,12 @@ async function wipe(host, token) {
|
||||||
return await resp.json();
|
return await resp.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Favicon of a major site/tracker via the cabine's server-side proxy
|
||||||
|
// (7-day cached PNG, transparent 1×1 fallback) — no third-party call.
|
||||||
|
function faviconUrl(host, domain) {
|
||||||
|
return `${baseUrl(host)}/social/favicon/${encodeURIComponent(domain || "")}`;
|
||||||
|
}
|
||||||
|
|
||||||
function socialUrl(host, token) {
|
function socialUrl(host, token) {
|
||||||
return `${baseUrl(host)}/social/${token}`;
|
return `${baseUrl(host)}/social/${token}`;
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +112,7 @@ const SbxApi = {
|
||||||
r3Check,
|
r3Check,
|
||||||
graph,
|
graph,
|
||||||
wipe,
|
wipe,
|
||||||
|
faviconUrl,
|
||||||
socialUrl,
|
socialUrl,
|
||||||
reportUrl,
|
reportUrl,
|
||||||
tokenFromUrl,
|
tokenFromUrl,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DEFAULT_HOST="kbin.gk2.secubox.in"
|
DEFAULT_HOST="kbin.gk2.secubox.in"
|
||||||
RELEASE_URL="https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.1/secubox-toolbox-webext.xpi"
|
RELEASE_URL="https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.2/secubox-toolbox-webext.xpi"
|
||||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
say(){ printf '\033[1;36m▸\033[0m %s\n' "$*"; }
|
say(){ printf '\033[1;36m▸\033[0m %s\n' "$*"; }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "SecuBox ToolBoX — Cartographie sociale",
|
"name": "SecuBox ToolBoX — Cartographie sociale",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"description": "Surface the SecuBox R3 toolbox live tracker analysis (cartographie sociale) in your browser: live badge, per-session trackers, mini Round-Eye graph, RGPD wipe + PDF report.",
|
"description": "Surface the SecuBox R3 toolbox live tracker analysis (cartographie sociale) in your browser: live badge, per-session trackers, mini Round-Eye graph, RGPD wipe + PDF report.",
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ button.danger { color: var(--cinnabar); border-color: var(--cinnabar); }
|
||||||
display: flex; align-items: center; gap: 6px; padding: 3px 2px;
|
display: flex; align-items: center; gap: 6px; padding: 3px 2px;
|
||||||
border-bottom: 1px solid #1a1a22; font-size: 11px;
|
border-bottom: 1px solid #1a1a22; font-size: 11px;
|
||||||
}
|
}
|
||||||
|
.row .fav { width: 16px; height: 16px; border-radius: 3px; flex-shrink: 0; background: #1a1a22; object-fit: contain; }
|
||||||
.row .dom { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.row .dom { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.row .hits { color: var(--muted); }
|
.row .hits { color: var(--muted); }
|
||||||
.tier { font-size: 9px; padding: 1px 4px; border-radius: 3px; }
|
.tier { font-size: 9px; padding: 1px 4px; border-radius: 3px; }
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
// SyntaxError that aborts popup.js. Use api.ext instead.
|
// SyntaxError that aborts popup.js. Use api.ext instead.
|
||||||
const api = globalThis.SbxApi;
|
const api = globalThis.SbxApi;
|
||||||
const $ = (id) => document.getElementById(id);
|
const $ = (id) => document.getElementById(id);
|
||||||
|
let curHost = api.DEFAULTS.host; // for favicon URLs (#555)
|
||||||
|
|
||||||
function show(which) {
|
function show(which) {
|
||||||
$("pair").hidden = which !== "pair";
|
$("pair").hidden = which !== "pair";
|
||||||
|
|
@ -24,6 +25,14 @@ function fillTopList(nodes) {
|
||||||
.forEach((n) => {
|
.forEach((n) => {
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.className = "row";
|
row.className = "row";
|
||||||
|
// favicon of the major site/tracker (cabine proxy) — not an IP (#555)
|
||||||
|
const fav = document.createElement("img");
|
||||||
|
fav.className = "fav";
|
||||||
|
fav.loading = "lazy";
|
||||||
|
fav.alt = "";
|
||||||
|
fav.src = api.faviconUrl(curHost, n.domain || n.id);
|
||||||
|
fav.addEventListener("error", () => { fav.style.visibility = "hidden"; });
|
||||||
|
row.appendChild(fav);
|
||||||
const dom = document.createElement("span");
|
const dom = document.createElement("span");
|
||||||
dom.className = "dom";
|
dom.className = "dom";
|
||||||
dom.textContent = n.domain || n.id;
|
dom.textContent = n.domain || n.id;
|
||||||
|
|
@ -57,6 +66,7 @@ function paint(data) {
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const cfg = await api.getConfig();
|
const cfg = await api.getConfig();
|
||||||
|
curHost = cfg.host || api.DEFAULTS.host;
|
||||||
$("ver").textContent = "v" + (api.ext.runtime.getManifest().version || "");
|
$("ver").textContent = "v" + (api.ext.runtime.getManifest().version || "");
|
||||||
|
|
||||||
// tunnel indicator
|
// tunnel indicator
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,33 @@
|
||||||
|
secubox-toolbox (2.6.20-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* Protective mode — tracker alerting + active spoofer (#560), DEFAULT OFF.
|
||||||
|
- mitmproxy_addons/protective_mode.py : env SECUBOX_PROTECTIVE_MODE =
|
||||||
|
off (default) | alert (detect + audit-log + count) | spoof (alert +
|
||||||
|
neutralise on classified 3rd-party tracker hosts only — strip
|
||||||
|
operator-grade/tracking headers MSISDN/x-acr/x-up-*/x-wap-*/
|
||||||
|
forwarded-IPs, drop the Cookie header to the tracker, strip referer,
|
||||||
|
assert DNT:1 + Sec-GPC:1). 1st-party traffic never touched.
|
||||||
|
- Every spoof action appended to /var/log/secubox/audit.log (CSPN) ;
|
||||||
|
live counts in /run/secubox/protective.json.
|
||||||
|
- Wired into the mitm-wg launcher + mitm.service addon list (inert
|
||||||
|
until opted in). GET /admin/protective : mode + counters.
|
||||||
|
Doctrine : opt-in, default off, logged, reversible (CM-WALL ; mirrors
|
||||||
|
Phase 13.D + Phase 8).
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Sat, 13 Jun 2026 16:00:00 +0200
|
||||||
|
|
||||||
|
secubox-toolbox (2.6.19-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* Favicons of major sites in the cartographie (#555) — never IPs.
|
||||||
|
- social.js eye-view: site + tracker nodes render the site favicon
|
||||||
|
(same-origin /social/favicon/{domain} proxy, 7-day cached,
|
||||||
|
transparent 1×1 fallback → the tier-coloured circle shows through),
|
||||||
|
clipped to the bubble. No IP/ASN displayed anywhere.
|
||||||
|
- Companion webext popup gains favicons in its top-tracker list
|
||||||
|
(clients/webext-toolbox 0.1.2). /wg/toolbox.xpi tag-pin → webext-v0.1.2.
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Sat, 13 Jun 2026 15:30:00 +0200
|
||||||
|
|
||||||
secubox-toolbox (2.6.18-1~bookworm1) bookworm; urgency=medium
|
secubox-toolbox (2.6.18-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
* Cartographie donut-bubble view + geography rollups (#553).
|
* Cartographie donut-bubble view + geography rollups (#553).
|
||||||
|
|
|
||||||
141
packages/secubox-toolbox/mitmproxy_addons/protective_mode.py
Normal file
141
packages/secubox-toolbox/mitmproxy_addons/protective_mode.py
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||||
|
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||||
|
#
|
||||||
|
# Phase 14 sketch (#560, refs #525/#514/#500) — Toolbox PROTECTIVE MODE :
|
||||||
|
# tracker alerting + active spoofer.
|
||||||
|
#
|
||||||
|
# Doctrine (CM-WALL) : active interference is OPT-IN, DEFAULT OFF, LOGGED,
|
||||||
|
# REVERSIBLE — mirrors Phase 13.D escalate + Phase 8 utiq_defense. It only
|
||||||
|
# ever touches classified **3rd-party tracker** hosts ; 1st-party traffic is
|
||||||
|
# never modified, so pages keep working.
|
||||||
|
#
|
||||||
|
# Levels — env `SECUBOX_PROTECTIVE_MODE` (default `off`) :
|
||||||
|
# off passthrough (no-op).
|
||||||
|
# alert detect + audit-log + count tracker flows. No modification.
|
||||||
|
# spoof alert + neutralise on tracker hosts only :
|
||||||
|
# - strip operator-grade / tracking request headers
|
||||||
|
# (MSISDN, x-acr, x-up-calling-line-id, x-wap-*, forwarded IPs)
|
||||||
|
# - drop the Cookie header sent to the tracker (kills cookie reuse)
|
||||||
|
# - assert DNT:1 + Sec-GPC:1
|
||||||
|
# Every spoof action is appended to /var/log/secubox/audit.log.
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
from mitmproxy import http
|
||||||
|
|
||||||
|
log = logging.getLogger("secubox.toolbox.protective")
|
||||||
|
|
||||||
|
_AUDIT = "/var/log/secubox/audit.log"
|
||||||
|
_STATS = "/run/secubox/protective.json"
|
||||||
|
|
||||||
|
# 3rd-party tracker hosts (mirror of inject_banner's _TRACKER_PATTERNS).
|
||||||
|
_TRACKER = re.compile(
|
||||||
|
r"(?:^|\.)(?:"
|
||||||
|
r"doubleclick|googlesyndication|googleadservices|googletagmanager|"
|
||||||
|
r"google-analytics|googletagservices|adservice\.google|"
|
||||||
|
r"facebook\.com/tr|connect\.facebook\.net|facebook\.net|"
|
||||||
|
r"scorecardresearch|chartbeat|hotjar|mixpanel|amplitude|"
|
||||||
|
r"segment\.com|segment\.io|criteo|adnxs|rubiconproject|"
|
||||||
|
r"taboola|outbrain|smartadserver|optimizely|fullstory|"
|
||||||
|
r"newrelic|datadog|sentry|amazon-adsystem|adsrvr|adform|"
|
||||||
|
r"yieldlove|moatads|adsystem|adserver|liveramp|bluekai|"
|
||||||
|
r"krxd|demdex|agkn|tapad|exelator|utiq"
|
||||||
|
r")",
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Operator-grade / tracking request headers stripped in spoof mode.
|
||||||
|
_STRIP = (
|
||||||
|
"msisdn", "x-msisdn", "x-up-calling-line-id", "x-up-subno",
|
||||||
|
"x-nokia-msisdn", "x-acr", "x-vf-acr", "x-amobee-1", "x-amobee-2",
|
||||||
|
"tm-user-id", "x-wap-profile", "x-wap-msisdn", "x-network-info",
|
||||||
|
"x-forwarded-for", "forwarded", "x-real-ip", "via",
|
||||||
|
)
|
||||||
|
|
||||||
|
_counts = {"alerts": 0, "spoofs": 0, "since": int(time.time())}
|
||||||
|
_last_flush = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def _level() -> str:
|
||||||
|
v = (os.environ.get("SECUBOX_PROTECTIVE_MODE") or "off").strip().lower()
|
||||||
|
return v if v in ("off", "alert", "spoof") else "off"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_tracker(host: str) -> bool:
|
||||||
|
return bool(host) and bool(_TRACKER.search(host))
|
||||||
|
|
||||||
|
|
||||||
|
def _audit(action: str, host: str, detail: str) -> None:
|
||||||
|
try:
|
||||||
|
line = "%s protective %s host=%s %s\n" % (
|
||||||
|
time.strftime("%Y-%m-%dT%H:%M:%S%z"), action, host, detail)
|
||||||
|
with open(_AUDIT, "a", encoding="utf-8") as f:
|
||||||
|
f.write(line)
|
||||||
|
except Exception:
|
||||||
|
pass # audit is best-effort ; never break the flow
|
||||||
|
|
||||||
|
|
||||||
|
def _flush_stats(force: bool = False) -> None:
|
||||||
|
global _last_flush
|
||||||
|
now = time.time()
|
||||||
|
if not force and (now - _last_flush) < 5:
|
||||||
|
return
|
||||||
|
_last_flush = now
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
os.makedirs(os.path.dirname(_STATS), exist_ok=True)
|
||||||
|
with open(_STATS, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({**_counts, "mode": _level(), "updated": int(now)}, f)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ProtectiveMode:
|
||||||
|
"""Alert on, and (spoof level) actively neutralise, tracker flows."""
|
||||||
|
|
||||||
|
def requestheaders(self, flow: http.HTTPFlow) -> None:
|
||||||
|
level = _level()
|
||||||
|
if level == "off":
|
||||||
|
return
|
||||||
|
host = flow.request.pretty_host or ""
|
||||||
|
if not _is_tracker(host):
|
||||||
|
return
|
||||||
|
|
||||||
|
_counts["alerts"] += 1
|
||||||
|
if level == "alert":
|
||||||
|
_audit("alert", host, "path=%s" % (flow.request.path or "")[:120])
|
||||||
|
_flush_stats()
|
||||||
|
return
|
||||||
|
|
||||||
|
# ── spoof ──
|
||||||
|
stripped = []
|
||||||
|
for h in _STRIP:
|
||||||
|
if h in flow.request.headers:
|
||||||
|
del flow.request.headers[h]
|
||||||
|
stripped.append(h)
|
||||||
|
# kill cookie reuse to the tracker
|
||||||
|
had_cookie = "cookie" in flow.request.headers
|
||||||
|
if had_cookie:
|
||||||
|
del flow.request.headers["cookie"]
|
||||||
|
# strip a referer that would leak the 1st-party page to the tracker
|
||||||
|
if "referer" in flow.request.headers:
|
||||||
|
del flow.request.headers["referer"]
|
||||||
|
stripped.append("referer")
|
||||||
|
# assert the opt-out signals
|
||||||
|
flow.request.headers["DNT"] = "1"
|
||||||
|
flow.request.headers["Sec-GPC"] = "1"
|
||||||
|
flow.request.headers["X-SecuBox-Protected"] = "spoof"
|
||||||
|
|
||||||
|
_counts["spoofs"] += 1
|
||||||
|
_audit("spoof", host, "stripped=%s cookie=%s" % (
|
||||||
|
",".join(stripped) or "-", "drop" if had_cookie else "-"))
|
||||||
|
_flush_stats()
|
||||||
|
log.info("[protective spoof] %s stripped=%d cookie=%s",
|
||||||
|
host, len(stripped), had_cookie)
|
||||||
|
|
||||||
|
|
||||||
|
addons = [ProtectiveMode()]
|
||||||
|
|
@ -16,7 +16,7 @@ DEST_DIR="/var/lib/secubox/toolbox/webext"
|
||||||
DEST="${DEST_DIR}/secubox-toolbox-webext.xpi"
|
DEST="${DEST_DIR}/secubox-toolbox-webext.xpi"
|
||||||
# Tag-pinned (not /latest/): the webext release is make_latest:false so it
|
# 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*.
|
# 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.1/secubox-toolbox-webext.xpi"
|
RELEASE_URL="https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.2/secubox-toolbox-webext.xpi"
|
||||||
|
|
||||||
log() { logger -t "$MODULE" -- "$*" 2>/dev/null || echo "[$MODULE] $*" >&2; }
|
log() { logger -t "$MODULE" -- "$*" 2>/dev/null || echo "[$MODULE] $*" >&2; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,10 @@ fi
|
||||||
# must run BEFORE inject_banner so the banner cookies our addon
|
# must run BEFORE inject_banner so the banner cookies our addon
|
||||||
# emits don't pollute the graph
|
# emits don't pollute the graph
|
||||||
# - cert_pin_detect auto-learns pinned hosts (Phase 6.N)
|
# - cert_pin_detect auto-learns pinned hosts (Phase 6.N)
|
||||||
for addon in inject_xff utiq_defense local_store social_graph inject_banner dpi cookies avatar ja4 soc_relay cert_pin_detect; do
|
# protective_mode (#560) runs right after utiq_defense — early, so spoof-level
|
||||||
|
# header/cookie stripping happens before the logging addons record the flow.
|
||||||
|
# Inert unless SECUBOX_PROTECTIVE_MODE=alert|spoof (default off).
|
||||||
|
for addon in inject_xff utiq_defense protective_mode local_store social_graph inject_banner dpi cookies avatar ja4 soc_relay cert_pin_detect; do
|
||||||
ARGS+=(-s "$ADDON_DIR/${addon}.py")
|
ARGS+=(-s "$ADDON_DIR/${addon}.py")
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1374,7 +1374,7 @@ async def wg_toolbox_apk() -> Response:
|
||||||
_WEBEXT_XPI = Path("/var/lib/secubox/toolbox/webext/secubox-toolbox-webext.xpi")
|
_WEBEXT_XPI = Path("/var/lib/secubox/toolbox/webext/secubox-toolbox-webext.xpi")
|
||||||
_WEBEXT_XPI_RELEASE = (
|
_WEBEXT_XPI_RELEASE = (
|
||||||
"https://github.com/CyberMind-FR/secubox-deb/releases/download/"
|
"https://github.com/CyberMind-FR/secubox-deb/releases/download/"
|
||||||
"webext-v0.1.1/secubox-toolbox-webext.xpi"
|
"webext-v0.1.2/secubox-toolbox-webext.xpi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2382,6 +2382,28 @@ async def admin_escalate() -> dict:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/admin/protective")
|
||||||
|
async def admin_protective() -> dict:
|
||||||
|
"""#560 — protective-mode status + counters. Read-only.
|
||||||
|
mode comes from SECUBOX_PROTECTIVE_MODE (default off) ; the live
|
||||||
|
alert/spoof counts from the addon's state file.
|
||||||
|
"""
|
||||||
|
import json as _json
|
||||||
|
import os as _os
|
||||||
|
from pathlib import Path as _P
|
||||||
|
out: dict = {
|
||||||
|
"mode": (_os.environ.get("SECUBOX_PROTECTIVE_MODE") or "off").lower(),
|
||||||
|
"alerts": 0, "spoofs": 0, "since": None, "updated": None,
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
st = _P("/run/secubox/protective.json")
|
||||||
|
if st.exists():
|
||||||
|
out.update(_json.loads(st.read_text()))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
@router.get("/social/report/{token}.pdf")
|
@router.get("/social/report/{token}.pdf")
|
||||||
async def social_report_pdf(token: str) -> Response:
|
async def social_report_pdf(token: str) -> Response:
|
||||||
"""Phase 11.C (#508) — bilingual FR/EN evidence PDF for a peer.
|
"""Phase 11.C (#508) — bilingual FR/EN evidence PDF for a peer.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ ExecStart=/usr/bin/mitmdump \
|
||||||
-s /usr/lib/secubox/toolbox/mitmproxy_addons/ja4.py \
|
-s /usr/lib/secubox/toolbox/mitmproxy_addons/ja4.py \
|
||||||
-s /usr/lib/secubox/toolbox/mitmproxy_addons/soc_relay.py \
|
-s /usr/lib/secubox/toolbox/mitmproxy_addons/soc_relay.py \
|
||||||
-s /usr/lib/secubox/toolbox/mitmproxy_addons/inject_banner.py \
|
-s /usr/lib/secubox/toolbox/mitmproxy_addons/inject_banner.py \
|
||||||
|
-s /usr/lib/secubox/toolbox/mitmproxy_addons/protective_mode.py \
|
||||||
-s /usr/lib/secubox/toolbox/mitmproxy_addons/local_store.py
|
-s /usr/lib/secubox/toolbox/mitmproxy_addons/local_store.py
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,19 @@
|
||||||
.attr('stroke', d => (d.kind === 'tracker' && (d.cdn_vendor || d.antibot_vendor)) ? '#0a0a0f' : null)
|
.attr('stroke', d => (d.kind === 'tracker' && (d.cdn_vendor || d.antibot_vendor)) ? '#0a0a0f' : null)
|
||||||
.attr('stroke-width', d => (d.kind === 'tracker' && (d.cdn_vendor || d.antibot_vendor)) ? 1.5 : 0);
|
.attr('stroke-width', d => (d.kind === 'tracker' && (d.cdn_vendor || d.antibot_vendor)) ? 1.5 : 0);
|
||||||
|
|
||||||
|
// #555 — favicon of the major site/tracker (same-origin cabine proxy,
|
||||||
|
// 7-day cached, transparent 1×1 fallback so the coloured tier circle
|
||||||
|
// shows through when there's no icon). No IP shown anywhere.
|
||||||
|
nodeG.filter(d => d.kind !== 'eye').append('image')
|
||||||
|
.attr('href', d => '/social/favicon/' + encodeURIComponent(d.label || ''))
|
||||||
|
.attr('width', d => (d.kind === 'tracker' ? 7 : 10) * 1.7)
|
||||||
|
.attr('height', d => (d.kind === 'tracker' ? 7 : 10) * 1.7)
|
||||||
|
.attr('x', d => -(d.kind === 'tracker' ? 7 : 10) * 0.85)
|
||||||
|
.attr('y', d => -(d.kind === 'tracker' ? 7 : 10) * 0.85)
|
||||||
|
.attr('preserveAspectRatio', 'xMidYMid slice')
|
||||||
|
.attr('clip-path', 'circle()')
|
||||||
|
.attr('pointer-events', 'none');
|
||||||
|
|
||||||
nodeG.filter(d => d.kind !== 'eye').append('text')
|
nodeG.filter(d => d.kind !== 'eye').append('text')
|
||||||
.attr('x', 12).attr('y', 4)
|
.attr('x', 12).attr('y', 4)
|
||||||
.text(d => (d.antibot_vendor ? '🤖 ' : '') + (d.label.length > 22 ? d.label.slice(0, 21) + '…' : d.label));
|
.text(d => (d.antibot_vendor ? '🤖 ' : '') + (d.label.length > 22 ? d.label.slice(0, 21) + '…' : d.label));
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ R3 tunnel — no third-party calls.
|
||||||
Published release `.xpi` (downloadable directly):
|
Published release `.xpi` (downloadable directly):
|
||||||
|
|
||||||
```
|
```
|
||||||
https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.1/secubox-toolbox-webext.xpi
|
https://github.com/CyberMind-FR/secubox-deb/releases/download/webext-v0.1.2/secubox-toolbox-webext.xpi
|
||||||
```
|
```
|
||||||
|
|
||||||
The toolbox also serves it from the cabine:
|
The toolbox also serves it from the cabine:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user