# Changelog All notable changes to this project will be documented in this file. ## [0.13.12] - 2026-11-04 ### Security - Bump `@hono/node-server` 2.1.1 to 2.1.6 (via `@modelcontextprotocol/sdk`): fixes the advisories affecting versions below 2.1.3 - Bump `serialize-javascript` 7.1.0 to 7.1.4 (via `vite-plugin-pwa` and `workbox-build`, build time only): fixes the advisory affecting versions below 7.1.3 - Lockfile only, both stay within their declared ranges. Replaces Dependabot PR #107 --- ## [0.13.11] - 2026-11-04 ### Security - Bump `fast-uri` 4.2.1 to 4.2.3 (via `@modelcontextprotocol/sdk` and `ajv`), override raised to `>=4.2.3`; replaces Dependabot PR #105 - Server network stats: removed the shell `exec` fallback that ran `cat` on the same three `/proc/net/dev` paths the `fs.readFile` loop already reads. No behaviour change, one less shell call ### Changed - `.gitignore` and `.dockerignore`: the broad `*secret*` glob is replaced by specific patterns (`*.secret`, `*.secret.*`, `*_secret`, `*_secret.*`, `.secrets`, `secrets/`) so source files with "secret" in their name are never silently excluded --- ## [0.13.10] - 2026-10-23 ### Fixed - Network scan: the console showed a stream of `429 Too Many Requests` on `/api/network-scan/progress`, `port-scan-progress`, `stats` and `history` during a scan started from the UI. The page polls up to about 120 requests per minute during a scan while `scanLimiter` allowed 60. Read requests (GET and the read-only `POST /traffic/batch`) now use the general API limit (300 per minute); scan triggers and other writes keep the 60 per minute limit - Network scan: the auto-scan progress poller no longer runs at the same time as the manual scan/refresh poller, which made `/progress` be requested twice every 2 seconds --- ## [0.13.9] - 2026-10-23 ### Security - Bump `dompurify` 3.4.16 to 3.4.17 (via `jspdf`): fixes GHSA-cg2g-xh64-v4q7 (critical, SAFE_FOR_TEMPLATES bypass), GHSA-mrv2-3pqr-j5jr (high, IN_PLACE detached subtree handlers) and GHSA-c6gr-qvv4-5v3f (low) - Bump `body-parser` 2.3.0 to 2.4.0 (via `express`): fixes CVE-2026-111181, a process crash on a compressed request body with trailing bytes - Bump `ip-address` 10.7.3 to 10.7.4 (via `express-rate-limit`): fixes GHSA-rxp7-6hxx-x8q5, quadratic-time zone identifier matching on long IPv6 strings --- ## [0.13.8] - 2026-10-23 ### Fixed - Home Assistant Ingress: `index.html` is now served with its asset, icon and manifest paths prefixed by the `X-Ingress-Path` value, and without the PWA service worker registration (its scope `/` would land on the Home Assistant host). Before, the browser requested `/assets/...` from the Home Assistant root and got a blank page. Only applied in Ingress mode with a valid `/api/hassio_ingress/` header; other deployments are unchanged - Home Assistant Ingress: lazy-loaded pages failed with `Unable to preload CSS for /assets/...` because the Vite preload helper used absolute `/assets/` URLs. Asset URLs inside JS and CSS are now built relative to the importing file (`experimental.renderBuiltUrl`) - Home Assistant Ingress: `/api/system/environment` and `/api/settings/theme` were fetched from the Home Assistant root (404); they now go through the Ingress base path like the other API calls --- ## [0.13.7] - 2026-10-22 ### Security - Rebuild the Docker image on Node.js 22.23.4 (`node:22-alpine`) to fix CVE-2026-111497, an out-of-bounds write in the Brotli decoder reachable through `fetch()` and the Express 5 body parser; 0.13.x images shipped Node.js 22.23.3 --- ## [0.13.6] - 2026-10-21 ### Added - `GET /api/plugins/unifi/card-devices`: each port row now carries `rx_errors`, `tx_errors`, `rx_dropped` and `tx_dropped` (cumulative controller counters since the device booted); a port that does not report them leaves them out instead of sending 0 --- ## [0.13.5] - 2026-10-21 ### Security - Bump `@modelcontextprotocol/sdk` from 1.31.0 to 1.32.1 and `vite` from 6.4.3 to 6.4.5 (replaces Dependabot PR #97, whose security check cannot read the repository secret) --- ## [0.13.4] - 2026-10-21 ### Changed - UniFi per-client traffic history (background poll every 30 s) now reuses a `stat/sta` answer that another reader (live tab, stats, MyServices, MCP) fetched in the last 25 s instead of sending its own request. Counters are cumulative, so the recorded totals stay exact; fewer `GET stat/sta` calls reach the controller --- ## [0.13.3] - 2026-10-20 ### Security - Bump `hono` from 4.13.7 to 4.13.13 (transitive dependency of the MCP SDK) to fix GHSA-5r4p-p66f-jhc7 (`serveStatic` decoded the request path twice, which could bypass middleware on static paths). The application does not use `serveStatic` --- ## [0.13.2] - 2026-10-20 ### Added - UniFi SFP modules: temperature and optical power (RX / TX) per transceiver, graded orange from 70 °C and red from 85 °C, optical RX orange below -14 dBm and red below -18 dBm. Shown in the front-panel card port detail and in a "SFP modules" list under each device on the Overview page - Electrical (copper) and optical modules are told apart: a copper transceiver reports placeholder optical power, so only its temperature is graded - Freebox page: SFP card (column 3) with the fibre module (model, vendor, serial, link, signal, module power, RX / TX in dBm, orange below -24 dBm and red below -27 dBm) and the SFP LAN port (link, speed, connected device). New `GET /api/settings/sfp` (cached 2 min). The box reports no temperature for either - Freebox page: "Latest devices" card (column 4) listing the newest and the last disconnected devices from the LAN browser timestamps - MCP: `sfp_modules` in the UniFi device summaries, SFP readings in `card-devices` (serial numbers left out), module identity in the Freebox FTTH summary ### Changed - Home page: an SFP icon appears in the "system state" frame of the UniFi card (hot module, or optic without signal / weak RX / fault) and of the Freebox card (fibre module without signal or weak RX; the box reports no module temperature), with a hover detail. Nothing is shown while the modules are fine - Freebox page: the SFP and latest devices cards sit in columns 3 and 4, so column 1 is no longer the only loaded one - Home Network scan card: the "Offline IPs" and "Top worst latency" titles are links to the scan page with the matching filter and sort (`/network-scan?status=offline`, `?status=online&sortBy=ping_latency&sortOrder=desc`); the offline list shows 5 entries at most; the stat rectangles are flatter; the data volume block sits before the offline list in a fixed 8-row frame, and the data volume, offline and latency rows have no frame; the last scan line fits on one line, with a single separator above the data volume --- ## [0.13.1] - 2026-10-20 ### Added - Port errors on the home cards and UniFi pages: a total badge with an hover detail (which port, RX / TX, usual causes). UniFi: System info on the Overview page, a badge in the home card system state, an "Err." column in the switches table of the Analysis tab and an exact-counter tooltip in the Switch tab (RX / TX errors, dropped packets) - Freebox: port error badge on the home card, by cause (bad CRC, receive errors, fragments, oversized / undersized frames, collisions). New `GET /api/settings/switch/errors` reads `/switch/port/{id}/stats` (one call per port) and keeps the result 5 minutes - UniFi home card: DHCP client count over the pool size, like Freebox - Switch tab: the switch badges in the title filter the table to one switch (click again to show all) ### Changed - Clients and Switch tables share the same zebra and row height - Home cards: Freebox firmware / LAN header cells stay on a single line (the model name drops the word "Freebox"), the UniFi DHCP range fits on one line, and the system state frames of both cards have the same minimum height. The Freebox Wi-Fi access points list is now a separate block under it --- ## [0.13.0] - 2026-10-20 ### Changed - UniFi Overview: the Access points, Switches and Sites sections are replaced by one Devices card showing the live front-panel cards (gateway, then switches, then APs by default, drag to reorder as before) with the status, system and network info back under each card. Client / AP / switch totals are now tags on the card header - UniFi Overview: System info is compact and holds the update status chips (up to date, update available, critical update, total devices); it is aligned with Network alerts at equal height - UniFi tabs reordered: Overview, Switch, Clients, then Traffic, Threats, Analyse - Reset layout button restyled and only shown once the card layout was changed - Search page: for a wired client, the fake 8-square port strip is replaced by the real front-panel card of its switch (real ports and LEDs) with the client's port selected ### Removed - UniFi CARD tab, merged into Overview (`/unifi/card` redirects to `/unifi/overview`) - Hardware front panel view toggle (cards always show the ports view) - Threats tab is hidden when there is no compatible gateway (no gateway device, or a controller type without IPS events) --- ## [0.12.4] - 2026-10-19 ### Changed - `GET /api/connection/summary`: the rarely changing extra blocks (LAN, DHCP and WiFi configuration, disks, FTTH optics, VM disk sizes, VM host info) are cached 5 minutes and dropped by any write to the box; live counts (hosts, WiFi stations, VM states, switch links) stay at 30 s. Disk temperature and usage can be up to 5 minutes old in this summary - `GET /api/auth/check` (permissions refresh of every open tab) no longer asks the Freebox for its session status when an authenticated request succeeded in the last minute; the keep-alive still checks it --- ## [0.12.3] - 2026-10-19 ### Security - `undici` 6.28.1 to 6.29.1: fixes SNYK-JS-UNDICI-21181005 (TLS options passed as `tls` ignored by `Agent`; MyNetwork passes them through `connect`, so it was not exposed) and four denial-of-service issues (SNYK-JS-UNDICI-21181013, 21181015, 21181019, 21185768) --- ## [0.12.2] - 2026-10-19 ### Changed - Freebox LAN devices are no longer polled every 10 s on every settings page: the parental control and port forwarding dialogs load them when they open - Freebox WiFi status refreshes every 15 s on the Freebox page and every 60 s elsewhere (header and plugin summary) - App-wide plugin stats refresh every 30 s on the dashboard and every 60 s on other pages --- ## [0.12.1] - 2026-10-19 ### Added - Upstream request counter: every request sent to the Freebox and the UniFi controller is counted per endpoint, with the reads answered without a request (cache hit or shared in-flight request). Shown in Administration > Debug ("Freebox and UniFi requests": last minute, average per minute over the last hour, total, share saved) and available to admins at `GET /api/debug/upstream-calls`, reset with `DELETE /api/debug/upstream-calls`. Counters live in memory and restart with the server --- ## [0.12.0] - 2026-10-19 Request optimisation release: far fewer calls to the UniFi controller and the Freebox, with the same data on screen and in the API. ### Added - Per-token rate limits for API and MCP tokens: 300 requests per minute, 30 per minute for expensive calls (ip-details, `scan_get_machine_details`, rescans, live top traffic). Answers `429 RATE_LIMITED` with `Retry-After` - Opt-in compact output for `GET /api/lan/devices`, `/api/wifi/stations` and `/api/dhcp/leases`: `?compact=1`, with `active=1`, `limit=N` and `search=`. Default output unchanged - MCP: optional `search` and `limit` on `freebox_get_dhcp_leases` and `freebox_get_switch_ports`, optional `compact` on `scan_get_device_by_ip` ### Changed - UniFi: short shared cache with in-flight sharing for controller reads (devices, clients, dashboard 1.5 s; sysinfo and configuration 60 s), cleared after every write - UniFi: live WebSocket polls every 2 s (was 1 s); with no client connected the background sampler runs every 30 s (was 1 s, around the clock). No tick starts while the previous one is still running - UniFi: WAN sampling asks the controller for the gateway only, one client list per tick instead of two, stats cached 10 s, bandwidth reports cached 60 s, threat lookups remember the endpoint that answered - UniFi: no forced re-login every 15 minutes, a single login at startup, and the connection test no longer logs the live session out - Freebox: short shared cache for GET requests (per endpoint, 0.9 s to 30 s), cleared after every write; API version fetched once; login and session checks shared between concurrent callers; stats cached 10 s; RRD history cached according to its range; network summary cached 30 s - Freebox: an expired session is renewed once and the request replayed, instead of waiting for the keep-alive - Freebox: the native WebSocket (host and VM events) now starts with the plugin and uses the configured box address - Network scan: plugin data is read once per scan instead of twice per host - ip-details: one UniFi stats read instead of two, sources fetched in parallel; IPs outside the scan ranges are no longer pinged, unless already recorded by the scanner (for example added by hand) - MCP `scan_get_machine_details` and `scan_rescan_ip` also accept IPs added by hand outside the scan ranges - Frontend: a single app-wide stats poller paused on hidden tabs; Freebox devices, downloads, VMs and history only polled on the pages that show them; Speedtest widget reads the live connection stream instead of polling every 2 s; CARD tab is the only UniFi stats poller while open - Frontend: WebSockets close after 2 minutes on a hidden tab and reopen when it is visible again; the UniFi live chart is refilled from the server so it shows no gap; one shared UniFi socket for all components - Network scan page: lighter polling during scans, latency and traffic data refreshed only when needed - SQLite: one transaction per traffic poll, two redundant indexes removed ### Fixed - Freebox: concurrent requests to the same endpoint with different bodies got the same answer (temperature history showed network data, every VM showed the first VM's disk size, a second concurrent write was never sent) - Freebox: a timed-out write could be sent twice on Freebox Revolution - Freebox WebSocket gave up after one failed reconnect in production and stayed on 5 s HTTP polling until reload - `GET /api/network-scan/history`: `offset` returned an empty page and `total` was the page size - `GET /api/plugins/:id` logged the shared UniFi session out ### Security - Removed the debug route `GET /api/plugins/unifi/threats/raw`, which relayed any controller path to any logged-in user - MCP `raw=true` on UniFi devices, clients and system info no longer returns device secrets (`x_authkey`, SSH and SSO fields) --- ## [0.11.2] - 2026-10-18 ### Added - `GET /api/plugins/unifi/top-traffic`, open to read-only API tokens: clients using the most traffic, `mode=live` (rate), `today` or `alltime` (volume), `limit` 1 to 50 - `GET /api/network-scan/stats`, open to read-only API tokens: total, online, offline and unknown hosts, last scan date - MCP `scan_get_devices`: `sort_by` (`last_seen`, `latency`, `ip`, `hostname`) and `order`, so the worst latencies come first with `status=online`, `sort_by=latency`, `order=desc` ### Changed - `unifi_get_top_traffic` and the new REST route share one implementation --- ## [0.11.1] - 2026-10-18 ### Added - `GET /api/plugins/unifi/card-devices`, open to read-only API tokens: switches, gateways and access points with the per-port detail the front-panel cards read (port rows with link, speed, PoE, live RX/TX, uplink flag, LLDP neighbours that are known UniFi devices; uplink port, WAN links without addresses, firmware update). Explicit field list, numbers are real numbers, a gateway only exposes its LAN address, cached 3 s server side - CARD tab: per-card uplink selector next to the title, with the real uplink port of switches and gateways detected from the controller (uplink object, LLDP, child devices) and selected when the card opens ### Changed - CARD tab: port RX/TX shown as bit/s, kbit/s, Mbit/s or Gbit/s with the unit picked automatically ### Fixed - CARD tab: special ports (WAN, SFP+, 10G RJ45) showed no link and no speed because their rows were dropped before the card rebuilt them - CARD tab: link status shown untranslated ("no_link"), and ports with no controller data now say so --- ## [0.11.0] - 2026-10-18 ### Added - UniFi page, new **CARD** tab: realistic live cards for UniFi switches, gateways and access points (front panel, ports, link speed, PoE, uptime, clients), refreshed every 5 s, read-only. Built on the MIT-licensed [unifi-device-card](https://github.com/bluenazgul/unifi-device-card) (vendored in `src/vendor/unifi-device-card`, local changes listed in `VENDOR.md`) with a MyNetwork adapter feeding it UniFi controller data instead of Home Assistant entities - Each card shows the model code received and whether a dedicated front panel exists for it or a generic one is drawn - Cards follow the application theme, optional hardware front panel view (off by default) - Port LEDs blink slowly when the link is up and faster when the port carries traffic - Cards can be reordered by drag and drop, and resized with the mouse (right edge for width, lower border for height with proportional reduction, bottom-right corner for both). The layout is kept in the browser (localStorage), with a reset button ### Changed - `src/vendor/**` is excluded from SonarCloud analysis --- ## [0.10.75] - 2026-10-18 ### Added - MCP tool `scan_get_machine_details(ip)`: everything known about a machine in one call (scan with open ports and protocol, last port scan, Freebox host and DHCP lease, port forwarding, UniFi client with switch name, IP and port, or AP). Same compact projection as `GET /api/search/ip-details/:ip`. Read-only, the IP must be inside the scan ranges (one ping is sent) ### Changed - Per-IP aggregation moved from the Search route to `ipDetailsService`, shared by the route and the MCP tool (no behaviour change) --- ## [0.10.74] - 2026-10-18 ### Added - `GET /api/search/ip-details/:ip`, open to read-only API tokens: the machine panel of the Search page in one call (scan with open ports and last port scan, Freebox host and DHCP lease, port forwarding, UniFi client, switch, AP). Compact projection for tokens, raw Freebox and UniFi objects are never returned. Existing tokens work without being recreated - `GET /api/plugins/unifi/clients` and the `unifi_get_clients` MCP tool: `switch_name` and `switch_ip` for wired clients, resolved from the switch MAC --- ## [0.10.73] - 2026-10-17 ### Added - `GET /api/connection/summary`: `hosts` and `hosts_active` (devices known and active on the box) and `switch` (ports up, per-port link and speed, no MAC lists) - `GET /api/updates/status`, open to read-only API tokens: installed and latest MynetworK version from the shared cached check (no forced refresh possible). `update_available` is `null` when the check is disabled - `GET /api/plugins/unifi/system`: `gateway.wan` (per WAN link up, speed, latency, no public IP) and `gateway.speedtest` (last speed test of the gateway) --- ## [0.10.72] - 2026-10-17 ### Added - `GET /api/connection/summary`, four new optional blocks for MyServices: - `firmware_update`: Freebox Server and Player firmware (installed, latest, date, changelog, blog URL) from the dev.freebox.fr check. `null` until the check has run - `dhcp`: enabled, address range, gateway, netmask, DNS (static leases are not included) - `wifi.networks`: SSID, enabled, hidden, state and band of the main networks. The guest network, keys and passphrases are never returned - `health`: temperatures, fans, disk status, highest partition usage, line and fibre state, grouped in one block (existing fields unchanged) ### Removed - `system.box_authenticated`: it was always dropped by the API token credential filter (name contains "auth") --- ## [0.10.71] - 2026-10-17 ### Changed - Docs: `docs/API-TOKENS.md` describes the new `GET /api/connection/summary` blocks (`storage`, `ftth`, `wifi`, `vms`, `vm_system`, `missing_rights`, `system.lan_ip`) - `GET /api/connection/summary`: `vm_system` gains `memory_pct` and `cpu_pct` (share of memory and vCPUs allocated to VMs) --- ## [0.10.70] - 2026-10-17 ### Fixed - API tokens settings: the list of reachable routes now shows `/api/plugins/unifi/system` (missing since 0.10.68) ### Security - Dependencies: `qs` 6.16.1 (SNYK-JS-QS-20943482) and `zod` 4.6.7 (SNYK-JS-ZOD-20510278), both fixed upstream --- ## [0.10.69] - 2026-10-17 ### Added - `GET /api/connection/summary`, optional and backward compatible blocks for MyServices (unknown values are `null`, never `false`): - `system.lan_ip`, plus `system.board_name`, `system.user_main_storage`, `system.box_authenticated` - `storage`: disks (model, state, `smart_ok`, temperature, size) and partitions (label, fstype, sizes, computed `used_pct`) - `ftth` (only when media is `ftth`): SFP presence, signal, link, optical power in dBm. Line IPs are deliberately not exposed - `wifi`: `enabled`, access points (name, state, band, channel, width), `stations_count` - `vms` (Delta / Ultra only): status, vCPUs, memory, disk type and disk sizes, and `vm_system` (total/used memory and CPUs) - `missing_rights`: Freebox permissions the paired app lacks. A denied block is `null`, the rest of the response stays valid - Explicit field allowlists: serial, MAC, keys and raw Freebox payloads are never returned. Extra blocks share a 30 s cache --- ## [0.10.68] - 2026-10-13 ### Added - `GET /api/plugins/unifi/system`, open to read-only API tokens: UniFi Network version and pending update (`version`, `update_available`), UniFi OS console version, and gateway health (firmware and update, LAN IP, uptime, CPU, memory, temperature). Compact projection only: SSO secrets and controller IPs from `stat/sysinfo` are never returned - `GET /api/plugins/unifi/devices`: new `upgrade_to_firmware` field, and `lan_ip` for gateways (their `ip` is often the WAN address) - `GET /api/connection/summary`: new `system.latest_firmware` and `system.update_available` fields, from the dev.freebox.fr blog check - MCP: `freebox_get_system_info` adds the same two firmware fields; `unifi_get_system_info` now returns the compact summary (`raw=true` for the full object) ### Changed - UniFi devices: `upgradable` is always present, `true`, `false`, or `null` when the controller does not report it (it was only sent when `true`). Unknown values are `null`, never `false` - UniFi devices: `cpu_pct` and `mem_pct` are numbers instead of strings, `temperature` is the highest reading of `temperatures[]` with `general_temperature` as fallback (UniFi OS gateways often only fill the former) --- ## [0.10.67] - 2026-10-13 ### Added - `GET /api/health` now returns the app `version` read from `package.json`. The route stays public (no token required), so API token permissions are unchanged --- ## [0.10.66] - 2026-10-12 ### Added - `GET /api/connection/summary`, open to read-only API tokens: Freebox line state, rates and box health (temperatures, fans, uptime, firmware, disk) in one call. Identifiers are left out: no public IP, serial number, MAC or remote-access domain ### Fixed - `GET /api/wifi/stations` called `/wifi/stations/` on the Freebox, which does not exist (404). It now gathers the stations of every access point and adds an `ap_id` field. This also fixes the MCP tool that lists WiFi stations --- ## [0.10.65] - 2026-10-11 ### Fixed - `scripts/update-version.sh` no longer pre-fills the CHANGELOG and `commit-message.txt` with commits from previous releases. The base lookup missed scoped subjects such as `fix(security): Version 0.10.63` and fell back to a months-old merge; it now starts from the last `v*` tag reachable from HEAD --- ## [0.10.64] - 2026-10-11 ### Added - `MCP_TRUSTED_PROXIES` (optional, IPv4 or CIDR list, comma-separated): the LAN-only check used by MCP, REST API tokens and the Prometheus export reads `X-Forwarded-For` only when the TCP peer is one of these proxies, so the allowlist protects installs exposed behind a reverse proxy. The header is walked from the right, skipping trusted proxies; a missing or invalid header is refused. Unset by default: behavior unchanged --- ## [0.10.63] - 2026-10-10 ### Security - `GET /api/system` and `GET /api/system/version` now require a logged-in user: they exposed the Freebox serial number, MAC addresses, remote-access domain and HTTPS port to anyone reaching the port. The UI only calls them after login - `GET /api/fs/share` and `GET /api/fs/share/:token` are now admin only: each Freebox share link grants direct access to files (creating links was already admin only). Non-admins see an empty share list - `GET /api/metrics/prometheus` now returns 404 when the Prometheus export is disabled (it answered regardless), and only accepts the local network and Tailscale, like MCP and API tokens - The default CORS configuration no longer allows `*.myoueb.fr` (the author's domain, hardcoded for every install). Defaults are now localhost and private networks only; the UI is served from the same origin and never needs CORS. Extra origins can be added in Administration > Security > CORS - The Freebox native WebSocket (self-signed certificate, verification disabled) now applies the same LAN-only safeguard as the HTTP client: it refuses to connect without certificate verification to a public IP literal in `FREEBOX_HOST` --- ## [0.10.62] - 2026-10-09 ### Added - Read-only REST API tokens (Administration > API tokens, or `scripts/mcp-token.ts --api`) so external LAN services such as MyServices can read the network inventory without a user account. Tokens use a `mwk_api_` prefix, are hashed at rest, can expire and be revoked, and record their last use. See `docs/API-TOKENS.md` for routes and response formats - `GET /api/dhcp/leases`: Freebox dynamic DHCP leases (no DHCP configuration) - `GET /api/plugins/unifi/clients` and `GET /api/plugins/unifi/devices`: compact UniFi inventory (same summaries as the MCP tools, never raw controller objects), with `type`, `search` and `limit` filters - `npm test`: first automated tests (`node:test`, no new dependency) covering the API token auth matrix ### Security - API tokens only work on a GET/HEAD allowlist (inventory routes), only from the LAN and Tailscale, and are always refused on admin routes and on `/api/mcp`. MCP tokens are refused on the REST API. Credential-like fields are stripped from every API token response - `@modelcontextprotocol/sdk` 1.30.0 to 1.31.0 (Dependabot alert #74, high: the SDK's OAuth client could send credentials to an authorization server chosen by the MCP server). MyNetwork only uses the server side, but the fix is applied anyway. Supersedes PR #63 (Dependabot) and PR #65 (Snyk), which made the same bump - Fixed token expiry check: `expires_at` (ISO 8601) was compared as text against SQLite `datetime('now')`, so an MCP token expiring today stayed valid until the end of the UTC day ### CI - Docker builds pull the official `node:22-alpine` image through Google's Docker Hub mirror (`mirror.gcr.io`), and Snyk scans run the Snyk CLI directly instead of Docker-based actions: Docker Hub's anonymous pull rate limit was failing builds and scans on shared GitHub runners - Removed the Snyk Code (SAST) job: Snyk Code is not enabled on the Snyk organization, so it never scanned anything. CodeQL and SonarCloud still cover static analysis --- ## [0.10.61] - 2026-10-09 ### Security - Removed the public `POST /api/users/register` route: anyone reaching the port could create an account with no restriction (it was never used by the UI). Accounts are created by admins via `POST /api/users` - Credentials are now masked for non-admin users: Freebox WiFi and guest keys (`/api/wifi/bss`, `/api/wifi/full`, `/api/wifi/guest/keys`, `/api/wifi/guest/config`), DynDNS password (`/api/connection/ddns/:provider`), FTP password (`/api/settings/ftp`), and the UniFi controller password and API key in plugin settings (`/api/plugins`, `/api/plugins/:id`). Admin responses are unchanged - `GET /api/plugins/:id/token` (UniFi API key) is now admin only - WebSocket connections now enforce the same rules as the REST API: revoked (logged out) tokens and disabled accounts are rejected. `/ws/logs` (server logs) is admin only, matching `GET /api/logs` - The user role and username are now read from the database on every request instead of the token, so demoting or renaming a user takes effect immediately rather than when their token expires (up to 7 days) --- ## [0.10.60] - 2026-10-09 ### Security - `/api/settings/*` had no authentication (except `/theme`): anyone reaching the port could change the Freebox DHCP, port forwarding/DMZ, VPN (users, server config, start/stop), FTP, connection, IPv6 and LAN settings. All routes now require a logged-in user, and every write requires the admin role - VPN reads that may expose credentials (`/vpn/users`, `/vpn/servers/:id/config`, `/vpn/client`) are now admin only - `POST /api/system/reboot` and `POST /api/system/reboot/schedule` had no authentication (unauthenticated Freebox reboot). Both now require the admin role; `GET /api/system/reboot/schedule` requires a logged-in user --- ## [0.10.59] - 2026-10-06 ### Security - `source-map-js` 1.2.1 to 1.2.2 (GHSA-68fv-2mgg-jv7q / CVE-2026-93749, DoS via indexed source-map offsets), via a new `>=1.2.2` override. Pulled in by `postcss`, build-time only - `postcss-selector-parser` 6.1.4 to 7.1.6 (GHSA-rj75-hqrm-r3gf, quadratic selector parsing), via a new `>=7.1.6` override. Pulled in by `tailwindcss` 3; generated CSS checked byte-identical to the 6.x build - Replaces Dependabot PR #59, which fixed the same advisories by jumping `tailwindcss` 3 to 4 (major, breaks the PostCSS integration and the build) - Known and accepted: `braces` 3.0.3 (GHSA-vfj7-8cjw-p6xm) has no patched release; it only reaches the build through `tailwindcss` 3 (chokidar/micromatch) with glob patterns from our own `tailwind.config.js`, never user input. Goes away with a future Tailwind 4 migration --- ## [0.10.58] - 2026-10-06 ### Changed - MCP: the IP allowlist now also accepts Tailscale IPv4 addresses (100.64.0.0/10), so MCP clients connecting directly over Tailscale (without a reverse proxy in front) are no longer rejected with 403. Tailscale IPv6 (fd7a:115c:a1e0::/48) was already accepted. The shared `isPrivateNetworkIp()` is unchanged, so the TLS bypass in `insecureAgent` does not extend to this range --- ## [0.10.57] - 2026-10-06 ### Security - MCP: disruptive write tools now carry `destructiveHint: true` (`freebox_reboot`, `freebox_set_wifi_enabled`, `freebox_toggle_wifi_bss`, `unifi_block_client`, `unifi_set_wlan_enabled`, `unifi_restart_device`, `scan_blacklist_add`), so MCP clients ask the user before running them. Limits prompt injection planted in device names, DHCP hostnames or call logs from chaining into a disruptive action - MCP: `scan_rescan_ip` and `scan_add_manual_ip` only accept IPs inside the configured scan ranges or the host's local /24 networks, so the scanner can no longer be pointed at arbitrary external hosts - MCP: changing a token's access level or tool overrides now applies immediately to its open sessions (clients get `tools/list_changed`), instead of only on the next session --- ## [0.10.56] - 2026-10-02 ### Security - `shell-quote` 1.10.0 to 1.12.0 (CVE-2026-102422 / GHSA-pqg4-j6r4-53mv, command injection in `quote()`), override raised to `>=1.11.0`. Pulled in by `concurrently`, no user input reaches it ### Changed - `concurrently` and `cross-env` moved to `devDependencies`: the container runs `tsx` directly, so neither is needed at runtime and both are now pruned from the Docker image --- ## [0.10.55] - 2026-10-02 ### Security - `fast-uri` 4.1.4 to 4.2.1 (GHSA-jvvf-x445-j334) and `ip-address` 10.5.0 to 10.7.3 (GHSA-2vr4-cq9g-pvrc), Dependabot PR #55 - `brace-expansion` 2.1.4 to 2.1.7 and 5.0.9 to 5.0.12 (GHSA-q2hr-2g5m-vwhr and related DoS advisories), build-time only via `vite-plugin-pwa` - `dompurify` 3.4.13 to 3.4.16 (GHSA-p98j-92pf-mc4p), pulled in by `jspdf` --- ## [0.10.54] - 2026-09-26 ### Changed - MCP tool responses are now compact JSON (no indentation), for every tool - `unifi_get_clients`, `unifi_get_devices`, `freebox_get_lan_hosts`, `freebox_get_wifi_stations` and `scan_get_devices` return a compact per-item summary instead of the raw controller objects (e.g. ~100 KB down to ~10 KB for 35 UniFi clients), `raw: true` returns the full objects - New list filters: `type` (wifi/wired, ap/switch/gateway), `search` (name, IP or MAC), `limit`, `status` for the scanner, `activeOnly` (default true) for Freebox LAN hosts - `freebox_get_call_log` (most recent first, default 50) and `freebox_get_contacts` (default 100) gain `search`/`limit`, the call log also a `type` filter - `unifi_get_bandwidth_report` range capped at 7 days - List responses keep the `{success, result}` envelope and add `total` (matches before `limit`) --- ## [0.10.53] - 2026-09-26 ### Changed - Last 6 frontend `catch (error: any)` migrated to `unknown` (UsersPage, SettingsPage, LogsPage, SearchPage), frontend and backend now have no `catch any` left - Duplicated network/socket/timeout error handling of UsersPage and SettingsPage factored into `getRequestErrorMessage()` (`src/utils/errorMessage.ts`) --- ## [0.10.52] - 2026-09-26 ### CI - Bump ossf/scorecard-action from v2.4.1 to v2.4.4 (SHA-pinned), Scorecard engine v5.5.0 --- ## [0.10.51] - 2026-09-25 ### CI - Bump all GitHub Actions still running on the deprecated Node.js 20 runtime to their Node.js 24 releases (SHA-pinned): checkout v7, upload-artifact v7, download-artifact v8, dependency-review v5, docker build-push v7, login v4, metadata v6, setup-buildx v4, codeql upload-sarif v4, sonarqube-scan v8 --- ## [0.10.50] - 2026-09-25 ### Changed - Dashboard scanner widget, "Data volume" list: the row numbers are replaced by an arrow showing each device's dominant traffic direction (down arrow when download dominates, up arrow when upload dominates), in live, today and all-time modes --- ## [0.10.49] - 2026-09-24 ### Refactor - Network scanner deduplication. Routes: the 38 identical catch bodies (log + 500 `{ success: false, error: { message, code } }`) go through a single `sendScanError()` helper. Service: the Freebox/UniFi lookup pairs are merged into `getMacFromPlugin()`, `getHostnameFromPlugin()` and `getVendorFromPlugin()`, and `scanNetwork()` / `refreshExistingIps()` share `prepareScanRun()`. About 400 lines removed, same messages, codes and lookup order. - Typed catch blocks finished: the 21 `catch (error: any)` left in the scanner's duplicated blocks now use `unknown`, so the backend has none left. Frontend: 46 of 52 converted through a new `src/utils/errorMessage.ts` (`getErrorMessage()` returns '' for non-Errors so `getErrorMessage(e) || fallback` keeps the old fallback behavior, plus `getApiErrorPayload()` for `{ error: { message, code } }` shapes). The 6 left sit inside duplicated blocks (UsersPage, LogsPage, SearchPage, SettingsPage). --- ## [0.10.48] - 2026-09-24 ### Refactor - Typed catch blocks in the network scanner (`server/services/networkScanService.ts`, `server/routes/network-scan.ts`): 68 of the 89 remaining `catch (error: any)` / `.catch((err: any) => ...)` now use `unknown`, narrowed through the shared `getErrorMessage()` helper (same pattern as v0.10.35). Stack traces read via `instanceof Error`, ping exec errors (`signal`/`stderr`) through an explicit shape. No functional change: same messages and error codes. The other 21 sit inside legacy copy-pasted blocks (config/purge route handlers, Freebox/UniFi lookup pairs) and are left for a dedicated deduplication pass, since touching them makes SonarCloud count the whole duplicated block as new code. --- ## [0.10.47] - 2026-09-24 ### Fixed - SonarCloud S1082 (8 Minor) and the S6819 accessibility smells (5 existing + the ones a first `role="presentation"` attempt introduced): no more click handlers on non-interactive elements, same behavior for the user. - Modal backdrops (custom domain modal, network-scan insights overlay, top traffic overlay): closing on backdrop click now goes through an invisible real `