Skip to content

v1.8.0

Latest

Choose a tag to compare

@github-actions github-actions released this 21 Jul 07:03
1ee4917

Security

  • HTTP transport hardening: GET /metrics now requires a bearer token when any identities are configured (was fully unauthenticated); 401 responses carry a WWW-Authenticate: Bearer header; Streamable HTTP sessions are bound to the identity that created them (a token can no longer drive another identity's session) and idle sessions are evicted after 30 minutes (previously the session map grew unbounded); token lookups are cached by sha256(token) so bcrypt runs once per token instead of on every request; and non-integer numeric env vars (MIKROMCP_PORT, body/rate/ssh/retention) now fail fast with a clear configuration error instead of silently becoming NaN.
  • allowedToolPatterns in identities.yaml used prefix-only matching: it took the text before the first *, so a pattern like *_wifi had an empty prefix and silently allowed every tool. Matching now uses a proper anchored glob, so leading/mid-string wildcards (*_wifi, manage_*_rule) behave correctly.
  • run_command's deny-list guard was trivially bypassable: it matched patterns against the raw command string, so ROS7 slash-path syntax (/system/reboot), whitespace/case variation, command chaining (:put 1; /system reboot), and :execute/:parse indirection all slipped past. The guard now normalizes each command (path and space separators treated equally) and checks every ;/newline-separated segment; :execute/:parse are denied by default. Documented explicitly as best-effort defense-in-depth, not an authorization boundary.
  • TLS certificate fingerprint pinning (tls.fingerprint in routers.yaml) was a silent no-op: it was enforced via tls.checkServerIdentity, which Node ignores when rejectUnauthorized is false — exactly the self-signed setup the docs recommend pinning for. Pinning is now enforced in the connection layer (post-handshake fingerprint256 check that destroys the socket on mismatch), so it holds regardless of rejectUnauthorized.

Changed

  • Tool risk annotations audited against a written rubric (destructive = removes resources, changes the authentication/authorization surface, or can sever connectivity/running services). manage_user, manage_user_group, manage_script, run_script, manage_container, manage_vrrp_instance, manage_mangle_rule, manage_ipsec_policy, manage_dhcp_server, manage_wifi_interface, manage_package, manage_routing_rule, manage_routing_table, manage_dns_settings, manage_interface_list, manage_interface_list_member, manage_address_list_entry, and manage_wireguard_interface are now destructiveHint: true (they gate confirmation, maintenance windows, and circuit-breaker tripping). fetch_url is no longer marked read-only (POSTs have side effects and outputFile writes to the router) and both fetch_url and bandwidth_test are marked open-world. export_config is no longer read-only (it writes a file when file is set). bandwidth_test no longer auto-retries and its duration cap dropped from 30s to 20s (it collided with the 30s REST timeout). A tool may now set retryable: false to opt a read tool out of automatic retry.
  • upload_file now prefers SFTP (encrypted, over the existing SSH channel) and only falls back to plaintext FTP when SFTP is unavailable, avoiding sending router credentials and file contents in the clear. The result reports which transport was used; the FTP path is labeled as plaintext.
  • Confirmation tokens (single-tool and fleet bulk_execute) are now self-verifying HMACs: validity is recomputed from the current call and secret rather than looked up in an in-memory pending map, so a token issued before a server restart still verifies afterward and the HMAC secret is actually load-bearing. Single-use replay protection is kept via an in-memory cache (single-instance; multi-instance replay within the TTL is a documented limitation).

Fixed

  • bulk_execute tag targeting now matches routers carrying ALL requested tags, as the schema documents (it previously matched ANY tag, over-targeting destructive fan-outs). Fanned-out calls also run through the full per-router safety stack — maintenance-window enforcement, per-router circuit breakers, and read-tool retry — which direct calls already had but the fleet path skipped.
  • get_file_content now caps returned content at 65536 characters (with a truncation marker and truncated/totalLength in structuredContent) so a large file can't blow up the client context, and list_connections requests only the fields it renders to shrink large conntrack payloads.
  • When a write tool fails with an ambiguous outcome (router timeout or unreachable), the error's suggested action now warns that the write may already have been applied and to verify router state before retrying, instead of implying a blind retry is safe.
  • Fleet tools (skipRouterContext) received null-cast router-scoped capabilities; a tool that mistakenly touched routerClient/sshClient/ftpClient/routerConfig crashed with an opaque TypeError. These now raise a typed FLEET_CONTEXT_UNAVAILABLE error explaining to target a specific router.
  • The REST connection pool keyed clients by router id only, so a client cached with old credentials kept being reused after credentials rotated. Clients are now also keyed by a credential hash and rebuilt (old one closed) when credentials change.
  • rollback_change/snapshots are substantially safer: snapshots now store only restorable configuration (dynamic router-generated records excluded, runtime/counter fields like bytes/packets/rx-byte/uptime stripped), so counters no longer produce spurious diffs and read-only fields are never written back and rejected mid-restore. Semantic-key diffing falls back to whole-record matching when the key is not unique within a side (e.g. multiple uncommented firewall rules that previously collapsed to one and scheduled the rest for deletion). Order-sensitive paths (firewall filter/nat/mangle, routing rules) now emit a warning that rule order is not restored, and deleted users are never recreated (passwords aren't in snapshots) — a warning is returned instead.
  • HTTP request bodies and SSH command output are now decoded once over the full byte stream instead of per chunk, so multi-byte UTF-8 characters split across chunk boundaries are no longer corrupted into replacement characters.
  • SSH command timeouts (run_command, inline export_config) now reject with an ETIMEDOUT error instead of silently resolving with partial (or empty) output as if the command had succeeded — the command may still be running on the router, so the caller is told to verify.
  • Maintenance windows spanning midnight (e.g. 22:0002:00) never matched, so destructive operations were blocked around the clock on routers configured with an overnight window. Overnight windows are now supported: days names the day the window opens and the window wraps past midnight into the following day.
  • Boolean record fields (disabled, running, dynamic, active, …) are parsed into real JS booleans by the REST client, but several idempotency checks compared them against the string "true" — which is always false — so enable actions on manage_scheduled_job, manage_firewall_rule, manage_mangle_rule, manage_routing_rule/manage_routing_table, manage_package, and the manage_ip_address add idempotency check silently reported "no change" without applying anything. All boolean-field comparisons now go through a shared isTrue() helper. RouterOSRecord values are now typed string | number | boolean to reflect the parser's output.
  • Numeric parsing kept 64-bit RouterOS counters (e.g. rx-byte above 2^53) as JS numbers, silently losing precision. Unsafe integers now stay strings.