Skip to content

v2.17.0

Choose a tag to compare

@github-actions github-actions released this 07 Jun 01:02
· 134 commits to main since this release

The big batch since v2.16.2. Headline: a security-hardening sweep across the daemon, MCP, A2A, release pipeline, and browser surfaces — most of it surfaced by an external codex security scan, with each finding triaged and adversarially verified before merge (a chunk turned out to be false-positives or duplicates and were closed rather than merged). Plus a set of fixes that make the embedded browser tools work on packaged builds, per-workspace environment/startup profiles, and the workspace-management UX that profiles implied (duplicate, per-terminal working directories). No config changes required — defaults are unchanged.

Added

  • Per-workspace process profiles. Each workspace can define environment variables and an optional startup command, applied to new panes only — existing and recovered daemon PTYs keep their create-time environment. Right-click a workspace → "Configure profile…". Generic by design (no provider hardcoding): point CLAUDE_CONFIG_DIR, CODEX_HOME, SSH wrappers, etc. at different accounts per workspace. This is environment separation, not an OS-level security sandbox. See docs/workspace-profiles.md for setup and multi-account recipes. (#101, #103)
  • Workspace management actions. Right-click a workspace to duplicate it — the layout (fresh pane/surface ids, cleared ptyIds so new panes spawn their own PTYs) and the profile (re-normalized through the secret-name policy) are cloned as <name> (copy N). A new Working directories menu lists each terminal's live cwd with copy; every terminal now tracks its own cwd (shown in the tab tooltip), terminal tabs can be renamed (double-click), and an accidental workspace close is guarded by a confirmation. New-pane semantics throughout, consistent with the profile contract. (#141)

Security

  • Token-file ACL grants by the labeled SID. getCurrentUserSid parsed the first SID-shaped substring of whoami output, so a SID-shaped account or machine name (e.g. S-1-1-0 = Everyone) could be granted the auth-token ACL instead of the real owner, leaving the token world-readable. It now parses the explicit SID: field. (#118)
  • Token-file DACL is rebuilt owner-only, even on the upgrade path. The shipped icacls /grant:r … /inheritance:r only replaced the named principal's ACE and stripped inherited ACEs, so a pre-existing explicit broad ACE (e.g. Everyone:(R) from a redirected/roamed/MDM profile) survived and left the token world-readable. The DACL is now rebuilt with a .NET DACL-only primitive (no owner/group/SACL writes, so it needs no privilege and succeeds on the upgrade-from-icacls state), with icacls as a fail-closed fallback when PowerShell is blocked. (#140)
  • MCP approvals bind to the reviewed capability snapshot. A plugin could redeclare broader capabilities while an approval prompt was pending and get trusted for a set the user never saw (a TOCTOU between consent and call). The approval now pins the exact capabilities shown in the dialog. (#122)
  • Terminal drops are restricted to wmux drag sources. Text dragged from a browser or another app no longer routes straight into a terminal PTY (where embedded newlines could auto-run at a shell prompt); only internal wmux drags — sidebar, surface tabs, file tree — write to the pane. (#123)
  • Default MCP terminal resolution fails closed. A spoofable WMUX_WORKSPACE_ID env hint or a failed workspace claim could fall back to the user's active pane, i.e. cross-workspace keystroke injection/read. Terminal tools now require a verified, PID-mapped identity and refuse the env hint, throwing rather than touching the focused pane. (#125)
  • A2A sender identity is authoritative. Company A2A send/broadcast no longer accept a caller-supplied from; the sender is derived from the authenticated workspace, so one agent can't impersonate another (or the CEO) when delivering a message into a peer's terminal. (#129)
  • Inter-agent PTY delivery is bracketed-paste wrapped. A2A messages written into a peer's terminal are bracketed and ESC-sanitized so an embedded newline can't submit a command in the receiving shell. (#132)
  • Remote SOUL prompt loading is disabled. Company agent personas were fetched from a third-party URL at spawn and written verbatim into the agent's instruction file — a remote prompt-injection / supply-chain path into command-capable agents. Spawning now uses the built-in role prompts only. (#131)
  • RPC browser profile switching is scoped. An RPC caller could mount an arbitrary Electron persistent partition or the human's pre-seeded login session store (reading its cookies over CDP). Profile names are now validated and RPC selection is restricted to a safe allowlist. (#133)
  • IPv6 navigation SSRF hardening. The navigation URL validator now un-brackets and bit-masks IPv6 literals (unique-local, link-local, IPv4-mapped), closing a bracket bypass that reached internal addresses through the browser_tabs new-tab path. (#137)
  • Bundled first-party MCP server runs under enforce mode. Under packaged enforce mode the bundled server was denied because it never went through declare/approve, so wmux's own tools were locked out. A name-recognized, scoped allowlist lets the first-party tools run without opening the gate to third-party servers. (#109)
  • Release pipeline hardening. The release tag is passed through env: instead of being interpolated into a shell run: block (Actions script-injection); the SignPath token is scoped to the signing step instead of the whole job; third-party release actions are pinned to immutable commit SHAs; WinGet publishing moved to a least-privilege job; and the installer fails closed when the checksum manifest is missing or invalid. (#119, #120, #121, #126, #135)
  • Recursive IPC error-log redaction. The structured IPC error logger now redacts sensitive keys at any depth, redacts startup-command values, and summarizes env maps to a key count — so workspace-profile env/commands flowing through pty:create can never leak into args_summary. Profile env is also kept out of the copy-session-info / drag-export markdown, and reserved WMUX_* keys are rejected so a profile can't spoof workspace identity. (#103)
  • Child shells never inherit a stale wmux identity. A wmux launched from inside a wmux pane (e.g. npm start while dogfooding) inherited the parent pane's WMUX_WORKSPACE_ID / WMUX_SURFACE_ID / WMUX_SOCKET_PATH in its own environment, which could survive into freshly created child shells. The whole reserved WMUX_* namespace is now cleared from the spawn baseline before identity is forced, so a child's identity is only ever what wmux explicitly sets — the spoofing guarantee is now unconditional, not profile-only. (#141)

Fixed

  • Embedded browser tools work on packaged builds. On packaged builds getPage() can't surface the <webview> guest as a Playwright Page, so a swath of browser tools failed with No browser page available. They now fall back to the main-process CDP/RPC channel: DOM tools read the real webview instead of the wmux app shell (#104), extraction/snapshot (#105), console/network/response-body capture (#106), browser_extract_data field mapping (#110), cookies/storage/emulate/resize (#111), geolocation grants + reset semantics (#112), and browser_wait (#114). browser_open/session_start route through requireWorkspaceId so the browser opens in the calling workspace, not the active one (#96).
  • Memory-leak audit survivors. Three real leaks found in a leak audit are now bounded: the MCP capture buffer (a Page-keyed WeakMap), the A2A GC hard cap, and PTY listener cleanup. (#102)
  • Per-terminal working directory is reported correctly (local and daemon mode). The tab tooltip and the workspace "Working directories" menu showed each shell's startup home directory (e.g. C:\Users\me) for every PowerShell regardless of where it had cd'd. Two compounding parser bugs are fixed: OSC 7 left Windows paths as /C:/Users/me (leading slash, forward slashes), and prompt detection matched the stale echoed prompt and froze the reported cwd at startup. Parsing is extracted into a unit-tested cwdDetect module (shared by both spawn paths) that normalizes the OSC 7 URI to a native path — including UNC shares — and reads the live (last) prompt. Daemon mode additionally never forwarded its detected cwd to the renderer; a new session:cwd event now closes that gap so daemon-backed panes live-update like local ones. (#141)
  • Tighter workspace right-click menu. The context menu was pinned to a fixed minimum width, leaving a wide blank gutter beside short items (and an oversized gap before the "Working directories" submenu arrow); it now sizes to its content. (#141)

Contributors

This release leaned on the community — two external contributors landed real features and fixes, not just reports.

@junbeom09 (조준범) carried forward the packaged-build hardening he started in 2.16.2. Dogfooding the packaged app, he found the browser DOM tools were silently reading the wmux app shell instead of the embedded <webview> — a bug that never reproduces in a dev build — and contributed the runtime shell-detection fix (#104). He then verified the CDP capture and geolocation fallbacks (#108/#112) on a real install, confirming the exact paths CI can't prove. Fixes and reports from real-world setups a single maintainer never sees are how wmux gets more robust.

@snowyukitty had the busiest release of anyone. He built per-workspace process profiles end to end (#101), then followed up after review with path-pointer credential-var allowlisting and non-destructive profile loading so an existing profile is never clobbered on load (#103). He shipped the workspace-management UX that profiles implied — duplicate workspace, the working-directories menu, per-terminal cwd tracking, tab rename, and close confirmation — and fixed the OSC 7 / prompt-detection cwd bugs and the child-shell identity-inheritance leak along the way (#141). He also split the Vitest runtime lane (#97) so timing-sensitive tests run serially instead of flaking under parallel load.

The security-hardening sweep (#118#137) was surfaced by an external codex security scan; each of the 20-plus findings was triaged and adversarially verified before landing, with false-positives and duplicates closed rather than merged. The token-file ACL rebuild (#118 plus the DACL-only primitive in #140) was additionally dogfooded against a real %USERPROFILE%\.wmux descriptor — a directory that grants SYSTEM and Administrators inherited FullControl — to confirm the hardened token comes out owner-only with no self-lockout.

Maintained by @openwong2kim, with engineering and code-review pairing by Claude (Anthropic). Thanks to everyone filing issues and dogfooding. 🙏

What's Changed

  • fix(mcp): route browser_open/session_start through requireWorkspaceId by @openwong2kim in #96
  • test: serialize runtime tests by @snowyukitty in #97
  • fix: memory-leak audit survivors — bound MCP capture, A2A GC hard cap, PTY listener cleanup by @openwong2kim in #102
  • feat: per-workspace process profiles (env + startup command for new panes) by @snowyukitty in #101
  • fix(profile): allowlist path-pointer credential vars; non-destructive load (follow-up to #101) by @snowyukitty in #103
  • fix(mcp): browser DOM tools read app shell instead of webview in packaged builds by @junbeom09 in #104
  • fix(mcp): RPC fallback for browser extraction/snapshot tools in packaged builds (#105) by @openwong2kim in #107
  • feat(mcp): RPC fallback for browser_console/network/response_body in packaged builds (#106) by @openwong2kim in #108
  • fix(mcp): allow the bundled first-party MCP server under enforce mode by @openwong2kim in #109
  • fix(mcp): browser_extract_data field mapping (#110) + packaged RPC fallback for state tools (#111) by @openwong2kim in #112
  • fix(mcp): CDP fallback for browser_wait in packaged builds (#114) by @openwong2kim in #115
  • security: parse whoami SID field for ACL grants by @openwong2kim in #118
  • fix(install): fail closed when checksum manifest missing or invalid by @openwong2kim in #121
  • fix(mcp): bind approvals to capability snapshot by @openwong2kim in #122
  • fix: restrict terminal text drops to wmux drags by @openwong2kim in #123
  • fix(company): authorize A2A sender identity by @openwong2kim in #129
  • fix(company): disable remote SOUL prompt loading by @openwong2kim in #131
  • fix: bracket inter-agent PTY message delivery by @openwong2kim in #132
  • security: harden IPv6 navigation URL validation by @openwong2kim in #137
  • fix(ci): avoid release tag shell injection by @openwong2kim in #119
  • fix(ci): scope SignPath token to signing action by @openwong2kim in #120
  • Harden WinGet release publishing by @openwong2kim in #126
  • ci: pin release workflow actions to immutable SHAs by @openwong2kim in #135
  • Restrict RPC browser profile switching by @openwong2kim in #133
  • fix(mcp): fail-closed default PTY resolution and verify workspace identity by @openwong2kim in #125
  • Workspace UX: duplicate, per-terminal cwd & working-dirs menu — plus cwd/identity fixes by @snowyukitty in #141
  • fix(security): rebuild Windows token DACL with a DACL-only primitive by @openwong2kim in #140

New Contributors

Full Changelog: v2.16.2...v2.17.0