Skip to content

Security: unauthenticated pipe surface lets any local process spoof agent status / notifications / diffs (agent.activity, hook.event, V1 all bypass the token) #72

Description

@Rajveerx11

Summary

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):

  1. All V1 text commandsPipeServer.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.
  2. 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.
  3. 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.
  • Inject fake Edit/Write hook events and diff refreshes → misleading diff view.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions