Security: unauthenticated pipe surface lets any local process spoof agent status / notifications / diffs (agent.activity, hook.event, V1 all bypass the token) · Issue #72 · amirlehmam/wmux · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Security: unauthenticated pipe surface lets any local process spoof agent status / notifications / diffs (agent.activity, hook.event, V1 all bypass the token) #72
The control pipe (\.\pipe\wmux) authenticates privileged V2 methods with a per-instance token — good. But three unauthenticated paths let any local process mutate wmux's UI/agent state without ever presenting the token, and the pipe is created with no restrictive ACL, so the caller can be any process on the machine (any integrity level):
All V1 text commands — PipeServer.handleV1 (src/main/pipe-server.ts) does no token check. Every V1 command is forwarded to all renderer windows as METADATA_UPDATE (src/main/index.ts:377), including notify, report_pwd, report_pr.
hook.event (in PUBLIC_V2_METHODS) — src/main/index.ts:741 forwards arbitrary params to all windows as HOOK_EVENT, and for tool: "Edit"|"Write" fires DIFF_UPDATE. An anonymous caller can inject fake hook events and force diff-pane refreshes.
agent.activity (in PUBLIC_V2_METHODS) — src/main/index.ts:762 calls applyExternalActivity(surfaceId, …) for an arbitrary, caller-supplied surfaceId with no ownership check, writing lastTool / activeSkill / isDone onto any surface's agent.
Why this is a real problem
pipe-server.ts documents the allowlist principle as "read-only or telemetry-style … don't grant code execution / file access." But agent.activity is a cross-surface state write — process A can rewrite process B's agent status, e.g. mark a still-running agent isDone: true, or make an idle surface look busy. For a tool whose entire value proposition is giving a human a trustworthy view of what AI agents are doing, unauthenticated spoofing of agent state / notifications / diffs is an integrity issue:
Fabricate "agent needs input" style notifications → phish the user into typing secrets into an attacker-chosen pane.
Flip real agents' status (busy⇄done) so the operator loses track of what's actually running.
Severity is local integrity/spoofing (not RCE), but it undermines the exact guarantee wmux exists to provide.
Why the "telemetry must work tokenless" rationale doesn't hold
wmux already injects WMUX_PIPE_TOKEN into every spawned shell (shared/instance.ts), so the legitimate hook/agent-telemetry clients — Claude Code hooks, agents — run inside wmux shells and have the token. Keeping these methods in the tokenless allowlist only benefits processes that aren't wmux children, which is precisely the set that shouldn't be writing agent state.
Proposed fix (contributable, code-level)
Move state-mutating methods out of PUBLIC_V2_METHODS — at minimum agent.activity and hook.event should require the token. Keep only genuinely read-only system.identify / system.capabilities public.
Authenticate (or drop) V1 mutating commands — require the token for notify / report_pwd / report_pr, or gate them behind the same allowlist logic.
Add an ownership check to agent.activity so a caller can only update the surface it owns (e.g. match against WMUX_SURFACE_ID).
Restrict the named pipe with a current-user-only DACL on creation, so non-user / low-integrity processes can't connect at all (defense in depth).
I can put up a PR that (a) trims the allowlist to the two read-only methods and (b) requires the token on the V1 mutating commands, since the real callers already carry it.
V1 text commands (notify, report_pwd, report_pr, shell state, …) now require the per-instance token via an auth <token> prefix. Only ping (read-only liveness) stays public. Unauthenticated senders get unauthorized.
hook.event and agent.activity are out of the public V2 allowlist — they now require WMUX_PIPE_TOKEN like every other privileged method. The allowlist is down to the strictly read-only system.identify / system.capabilities.
The CLI and the PowerShell shell integration (including the background PR poller job) were updated to send the token automatically — legitimate clients run inside wmux-spawned shells and already carry WMUX_PIPE_TOKEN, so nothing to configure.
Your point that the "telemetry must work tokenless" rationale didn't hold was exactly right, and it made the fix simple. The two follow-ups from your list that did not ship in this release: the per-surface ownership check on agent.activity, and the current-user-only DACL on the pipe — both are good defense-in-depth and PRs are welcome.
Upgrade: download wmux-0.17.0-win-x64.zip, right-click → Unblock, extract over your install (or let the auto-updater pick it up).
Activity
security: require pipe token for V1 commands and hook.event/agent.act…
Merge pull request #73 from Rajveerx11/fix/72-pipe-auth
amirlehmam commentedon Jul 7, 2026
Fixed in v0.17.0 via #73 — thanks @Rajveerx11 for the detailed report and the PR.
What shipped:
notify,report_pwd,report_pr, shell state, …) now require the per-instance token via anauth <token>prefix. Onlyping(read-only liveness) stays public. Unauthenticated senders getunauthorized.hook.eventandagent.activityare out of the public V2 allowlist — they now requireWMUX_PIPE_TOKENlike every other privileged method. The allowlist is down to the strictly read-onlysystem.identify/system.capabilities.WMUX_PIPE_TOKEN, so nothing to configure.Your point that the "telemetry must work tokenless" rationale didn't hold was exactly right, and it made the fix simple. The two follow-ups from your list that did not ship in this release: the per-surface ownership check on
agent.activity, and the current-user-only DACL on the pipe — both are good defense-in-depth and PRs are welcome.Upgrade: download wmux-0.17.0-win-x64.zip, right-click → Unblock, extract over your install (or let the auto-updater pick it up).
fix(shell-integration): resync all three packaged copies with src
Merge PR #169: resync all three packaged shell-integration copies (from