Skip to content

fix(gateway): enforce chat.abort owner-authorization — reject non-owner aborts (#2721)#2729

Merged
alexey-pelykh merged 1 commit into
mainfrom
fix/gateway-chat-abort-owner-authz-2721
Jun 18, 2026
Merged

fix(gateway): enforce chat.abort owner-authorization — reject non-owner aborts (#2721)#2729
alexey-pelykh merged 1 commit into
mainfrom
fix/gateway-chat-abort-owner-authz-2721

Conversation

@alexey-pelykh

Copy link
Copy Markdown

Summary

chat.abort never read client identity, so any authenticated client could abort another client's in-flight chat run — cross-client denial of in-flight work (HIGH). This restores the owner-authorization control, self-contained to src/gateway/server-methods/chat.ts (no shared-core edits).

Closes #2721.

What changed

  • resolveChatAbortRequester — identifies the requesting client: connId, deviceId (connect.device.id), and operator.admin scope.
  • canRequesterAbortChatRun — authorizes a requester against the run's recorded owner:
    • operator.admin → bypass;
    • unowned run (no ownerConnId/ownerDeviceId) → abortable by any authenticated client (no regression for legacy / no-identity runs);
    • owner-by-deviceId (a paired device keeps abort rights across reconnects) or owner-by-connId (originating connection).
  • Explicit-run abort from a non-owner → rejected { code: "INVALID_REQUEST", message: "unauthorized" }; the run is left untouched.
  • Session-scoped abort → cancels only requester-owned runs (resolveAuthorizedRunIdsForSession + abortAuthorizedSessionRunsWithPartials, preserving partial-transcript persistence).
  • chat.send populates ownerConnId / ownerDeviceId at run-start so the check has data in production.
  • Un-skips the two covering tests in chat.abort-authorization.test.ts.

Behavior change (intended tightening)

Previously-accepted non-owner aborts now return unauthorized. The owner (same connection, or same paired device after reconnect) and operator.admin retain full abort capability; unowned runs are unaffected.

Test plan

  • chat.abort-authorization.test.ts4/4 (the 2 previously it.skip cases un-skipped and passing).
  • ✅ Full pnpm test:gateway1554 passed / 0 failed (140 files), incl. chat.abort persistence (client: null + unowned runs) and both server.chat.gateway-server-chat* e2e suites (same-ws send → abort).
  • acp/translator.cancel-scoping.test.ts — 7/7.
  • pnpm check (oxfmt + tsgo + oxlint + css-class-drift) clean; throwing-stub-callers gate 0 violations.

Merge

Per the dispatch directive, this PR is intentionally not merged — merging is handled by the orchestrator once CI is green.

🤖 Generated with Claude Code

…er aborts (#2721)

chat.abort never read client identity, so any authenticated client could abort
another client's in-flight run (cross-client denial of in-flight work, HIGH).

Restore the owner-authorization control, self-contained to chat.ts:
- resolveChatAbortRequester identifies the requesting client (connId / deviceId /
  operator.admin scope).
- canRequesterAbortChatRun authorizes the requester against the run's owner: admin
  bypass, owner-by-deviceId (paired device across reconnects), owner-by-connId;
  unowned runs stay abortable by any authenticated client (no legacy regression).
- Explicit-run aborts from a non-owner are rejected as "unauthorized".
- Session-scoped aborts cancel only requester-owned runs
  (resolveAuthorizedRunIdsForSession + abortAuthorizedSessionRunsWithPartials).
- chat.send populates ownerConnId / ownerDeviceId at run-start so the check has data.

Un-skips the two covering tests in chat.abort-authorization.test.ts. Behavior
tightening: previously-accepted non-owner aborts now return unauthorized.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@alexey-pelykh
alexey-pelykh merged commit adb7264 into main Jun 18, 2026
17 checks passed
@alexey-pelykh
alexey-pelykh deleted the fix/gateway-chat-abort-owner-authz-2721 branch June 18, 2026 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gateway: chat.abort has no owner-authorization — any client can abort another client's run

1 participant