github Kpa-clawbot/CoreScope v3.8.3
CoreScope v3.8.3

5 hours ago

CoreScope v3.8.3

Upgrade urgency: High — contains a stored XSS fix. Public dashboards should upgrade immediately.

187 commits since v3.8.2 (47 substantive + 140 auto-generated coverage bumps). Every bullet ends with a commit SHA — git show <sha> to verify.

Highlights

  • The live map is buttery now. Packet animations and node pulses moved off SVG-per-frame onto a hardware-accelerated canvas overlay — 60 FPS even when the mesh is firing on all cylinders. (#1490, 914f869; #1521, 75a38f0)
  • Dashboards and APIs feel snappier under load. /api/observers p95 dropped from ~10.8s to sub-second on busy meshes, /api/stats is no longer re-scanning the observations table every 15s for the navbar, and per-observer analytics stopped re-parsing every timestamp three times. (#1483, 13bdee5; #1516, 3850600)
  • Stored XSS class closed — same class as CVE-2026-45323. Mesh-advertised node names and observer names were rendered to the DOM unescaped on app/nodes/observers/packets/live/analytics/route-view/area-map; a payload like <img src=x onerror=…> executed. All sinks now escape; follow-up audits closed three more XSS sinks, an unbounded-limit DoS, and several log-injection paths. New CI gate hard-fails PRs that reintroduce either class. (#1537, f15b677; #1539, 53339e0; #1540, 800d61c; #1544, 7b43045; #1543, e4a21fc)
  • Cross-domain embeds. ?embed=1 on /#/map and /#/channels strips the chrome, and a new CORS_ALLOWED_ORIGINS env unlocks Home Assistant / Grafana panel embeds. (#1500, 367265e)
  • The map's region filter now actually filters the map. Selecting a region hides non-region nodes by default; a "Show all nodes" toggle preserves the old behavior. (#1501, 28713fa)
  • Observers with broken clocks now name themselves. Observers emitting zone-less timestamps get a ⚠️ chip on the observer list and a banner on detail with fix instructions — no more "why is this observer showing 1970?" support tickets. (#1480, 43b93c6)
  • Operators with big DBs no longer wait minutes for ingestor startup. Heavy index builds run in the background via a new async-migration runner; the ingestor accepts packets immediately on boot. (#1541, e438451)
  • Fresh deploys can decrypt #Public out of the box. Default Public channel key now ships in channel-rainbow.json. (#897, 451b5e8)
Stored-XSS sink list (for security verification)

HTML-escaped at: app.js global search dropdown (node + channel name); nodes.js table row + Leaflet popups (×2); observers.js table name cell; packets.js observer-name cells (×4) + multi-select checkbox label; live.js node-filter <option> + map tooltip + hop popup; analytics.js topology tooltip + RF-health aria-label; packets.js CHAN hex decode fields; route-view.js hop + union tooltips (×2); area-map.html node popups (×2). Global escapeHtml now covers the full 5-char OWASP set (& < > " '). map.js safeEsc was a no-op identity since #48 — now wired to the real escaper. Backend sanitizeName() deliberately keeps < > " & for lossless storage / meshcore:// deep-links; fix is at the sink per OWASP. Round-2 sweep added: traces.js URL-fragment in popups, observer-detail.js MQTT-meta tooltip, analytics.js RF-health aria-label. Pinned by test-xss-escape-sinks.js and test-anl1-tooltip-render.js with tag-injection and attribute-breakout payloads. (#1537, #1539)

Features

  • Hide non-region nodes by default on the live map when a region is selected; "Show all nodes" toggle restores legacy view; state in localStorage['mc-region-show-all-nodes']. Fixes #1108. (#1501, 28713fa)
  • ?embed=1 on /#/map or /#/channels suppresses top-nav, bottom-nav, drawer; new corsAllowedOrigins config + CORS_ALLOWED_ORIGINS env; Access-Control-Allow-Methods tightened to GET, HEAD, OPTIONS. Fixes #1369. (#1500, 367265e)
  • Customizer: marker stroke color/width/opacity tunable via Colors → Marker Stroke; backed by --mc-marker-stroke-* CSS vars + markerStroke config block. Fixes #1488. (#1494, ca2c3d6)
  • Observers: "⚠️ Naive clock" chip on the observer list + banner on detail; backed by observers_clock_naive_v1 migration. Fixes #1478. (#1480, 43b93c6)

Fixes (selected)

  • Live page mobile: filter dropdowns no longer clipped by the scrolling toggle bar; settings cog pushed right. Fixes #1529. (#1531, 99cea7b)
  • Live nav-pin: 📌 button moved into .nav-right so it stops squeezing search/theme/hamburger. Fixes #1526. (0273f15)
  • Live: requestAnimationFrame dt clamped to 32ms — no more 8× fast-forward when a backgrounded tab wakes. (#1524, 3e4c456); VCR speed no longer leaks into live mode. Fixes #1346. (#1427, b71b26a)
  • Live: animations restored to a custom Leaflet pane (z=650) — were painting behind markers since #1334. Fixes #1485. (#1491, 268751f)
  • Live canvas follow-up: DPR listener {once:true}, scratch buffers hoisted, whitespace churn reverted. Fixes #1514. (#1520, bf8bb87)
  • Channels: selectChannel() / refreshMessages() no longer stomp WS-pushed messages during the REST replacement window — real production bug. Fixes #1498. (#1513, c9b98cb)
  • Marker stroke: server defaults restored to v3.7.2 visual after #1494's translucent default looked weak on upgrade. Fixes #1506. (#1507, a7b156d)
  • Customizer "🗑️ Reset All" now actually clears everything (CB preset, encrypted-channel toggle, dark-tile pick, marker-stroke vars, per-role writes). Fixes #1496. (#1497, 9bed0e8)
  • Packets: collapse chevron on grouped rows no longer reopens a just-closed detail panel. Fixes #1486. (#1492, 7fcb226); HB column uses getPathLenOffset(route_type) (TRANSPORT path_len is at offset 5). Fixes #1469. (d4280be)
  • Nodes dark mode: --card-bg aligned with --surface-2 — was washed-out. Fixes #1470. (#1517, 24a840d)
  • BYOP modal header no longer occludes body content on mobile. Fixes #1487. (#1493, c841dbc)
  • Trace tool: validated hash written back via history.replaceState, so #/tools/trace/<hash> is shareable. Fixes #1522. (#1523, 73ceb47)
  • Ingestor: readProcSelfIO() stops stamping time.Now() before os.Open — eliminates phantom rate spikes after transient failures. Fixes #1169. (#1428, 196f1c6)
  • Ingestor: per-message naive-timestamp warning silenced; observer.last_seen and per-packet rxTime already clamped. (#1479, 0a58aa1)
  • Version/commit badge moved from navbar to a Version card on /#/perf. (#1503, 788a509)
  • Mobile nav follow-ups (#1471 series): duplicate .filter-toggle-btn hidden so navbar mirror is the only visible affordance (#1475 f0da38f, #1482 b6e0050); More-sheet mirrors re-injected on each open via click delegate (#1476, 497e419).
  • Live: "ghost" hops renamed to "inferred". Partial fix for #1505. (#1527, deafe32)
  • Live: nav-pin state persisted in localStorage['live-nav-pinned']. Fixes #1510. (#1515, 878d162)

Upgrade Notes

New config (config.example.json, all opt-in, safe defaults):

  • corsAllowedOrigins: [] — cross-origin allowlist for embed (#1369). Env: CORS_ALLOWED_ORIGINS (comma-separated).
  • markerStroke: { color: "#fff", width: 2, opacity: 1 } — v3.7.2-matching defaults (#1488/#1506).
  • neighborGraph.cacheRecomputeIntervalSeconds: 300 (#1483).
  • observersCache.ttlSeconds: 30 (#1483).

Auto-applied migrations:

  • observers_clock_naive_v1clock_skew_seconds, clock_skew_count_24h, clock_last_naive_at on observers (#1480).
  • obs_observer_ts_idx_v1 — composite (observer_idx, timestamp) index on observations, runs async via the new RunAsyncMigration helper; ingestor accepts packets while it builds (#1483/#1541). State tracked in _async_migrations; idempotent across restarts.

Behavior changes:

  • Live map: selecting a region hides non-region nodes by default. Toggle "Show all nodes" once to opt out (persists in localStorage). (#1501)
  • Access-Control-Allow-Methods no longer advertises POST — cross-origin writes fail preflight by design; same-origin admin writes unaffected. (#1500)
  • List endpoints silently clamp limit to 500 (lists) / 200 (analytics, bulk-health). Requests over the cap return the cap. (#1540)
  • Version badge lives on /#/perf now, not the navbar. (#1503)

No breaking config removals or deprecations.

Acknowledgements

  • @mxsasha (Sasha Romijn) — responsible disclosure of the stored XSS in #1536.
  • @efiten — audit and fix across all unescaped sinks in #1537.

Tagging

git tag -a v3.8.3 e4a21fc9 -m "v3.8.3"

Don't miss a new CoreScope release

NewReleases is sending notifications on new releases.