Skip to content

fix(security): re-port webchat compaction + conversation-hook access guards (#2764)#2767

Merged
alexey-pelykh merged 1 commit into
mainfrom
fix/2764-webchat-compact-conversation-hook-guards
Jul 2, 2026
Merged

fix(security): re-port webchat compaction + conversation-hook access guards (#2764)#2767
alexey-pelykh merged 1 commit into
mainfrom
fix/2764-webchat-compact-conversation-hook-guards

Conversation

@alexey-pelykh

Copy link
Copy Markdown

Summary

Re-ports the two Priority-1 Medium security fixes identified in #2764 — advertised/expected controls that were absent on live fork paths after the v2026.4.24 content-only sync. Both apply to kept fork layers (gateway, plugins).

Scope: medium items only. The Priority-2 Low items in #2764 are intentionally out of scope, so this PR uses Refs, not Closes — #2764 stays open for the low-priority drift.

Item 1 — sessions.compact webchat session-mutation guard

File: src/gateway/server-methods/sessions.ts · Upstream: 873dce178de (PR openclaw#70716)

sessions.patch/sessions.delete call rejectWebchatSessionMutation; sessions.compact did not, letting an admin-scoped webchat client (e.g. an XSS'd control-UI session) compact/mutate session transcripts even though the same client class is blocked from patch/delete.

  • Extended the rejectWebchatSessionMutation action union with "compact".
  • Gave sessions.compact the client + isWebchatConnect handler params and call the guard before mutating (mirrors patch/delete).
  • Scope note: the fork has no sessions.restore handler (upstream added "restore" too), so only "compact" is ported — no dead union member.

Item 2 — enforce plugins.entries.*.hooks.allowConversationAccess

Files: src/plugins/types.ts, src/plugins/registry.ts · Upstream: 51f9f94cc36 (PR openclaw#70786, registry/hooks portion)

The allowConversationAccess opt-in was fully plumbed and advertised (config schema, normalization, and loader.ts passes it into hookPolicy) but inertregisterTypedHook never gated on it, so an operator-allowed non-bundled plugin got ungated access to conversation transcripts and the control did nothing.

  • Added CONVERSATION_HOOK_NAMES (llm_input, llm_output, agent_end) + isConversationHookName to types.ts (the fork's equivalent of upstream's hook-types.ts).
  • Added allowConversationAccess to PluginTypedHookPolicy and enforced it in registerTypedHook: non-bundled plugins are blocked unless they set it true; bundled plugins stay allowed unless they set it false — a diagnostic is pushed on block.
  • Scope note: the upstream status.ts hunk is excluded — the fork's status.ts has diverged and surfaces neither allowPromptInjection nor the policy block, so porting only allowConversationAccess there would be inconsistent and is outside the stated fix. The cli-runner transcript-loading portion of the upstream commit is not part of Re-port upstream security/correctness fixes missed in the v2026.4.24 content-only sync #2764's identified gap.

Verification

Both fixes re-verified against #2764's own "verify the gap" steps (gaps confirmed closed):

  • sessions.compact now calls rejectWebchatSessionMutation({ action: "compact", … }).
  • isConversationHookName now exists and is used; registerTypedHook gates on policy?.allowConversationAccess.

Tests (added/extended):

  • src/gateway/server.sessions.gateway-server-sessions-a.test.ts — webchat-mutation test now also asserts sessions.compact is rejected (18/18 pass).
  • src/plugins/dead-hooks.test.ts — new conversation hook access gate suite: non-bundled block, opt-in allow, bundled default-allow, bundled explicit opt-out, non-conversation-hook not gated (7/7 pass).

Gates: pnpm check green (format, tsgo, oxlint 0/0, all fork linters); standalone fork-integrity gates clean (zombie-imports, stub-debt baseline 140==140, throwing-stub-callers 0, no openclaw/lobster leaks).

⚠️ Security change — do not auto-merge. Please review before merging (auto-merge intentionally not enabled).

Refs #2764

🤖 Generated with Claude Code

…guards (#2764)

Re-port two medium-severity upstream security fixes missed in the
v2026.4.24 content-only sync — both apply to kept fork paths (gateway,
plugins) and were advertised/expected controls that were absent live.

- gateway: block webchat clients from sessions.compact. Extend
  rejectWebchatSessionMutation to the "compact" action and guard the
  handler (client + isWebchatConnect), mirroring sessions.patch/delete.
  Upstream 873dce1 (PR openclaw#70716). The fork has no sessions.restore
  handler, so only "compact" is ported (not upstream's "restore").

- plugins: enforce plugins.entries.*.hooks.allowConversationAccess. Add
  isConversationHookName and gate conversation hooks (llm_input,
  llm_output, agent_end) in registerTypedHook so non-bundled plugins
  must opt in explicitly; bundled plugins stay allowed unless they set
  it false. The config field was fully plumbed but inert. Upstream
  51f9f94 (PR openclaw#70786) — registry/hooks portion only.

Tests: extend the webchat-mutation gateway test to assert compact is
rejected; add a conversation-access gate suite to plugins
dead-hooks.test.ts (non-bundled block, opt-in allow, bundled default,
bundled opt-out, non-conversation-hook not gated).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@alexey-pelykh
alexey-pelykh merged commit debdb1e into main Jul 2, 2026
17 checks passed
@alexey-pelykh
alexey-pelykh deleted the fix/2764-webchat-compact-conversation-hook-guards branch July 2, 2026 12:37
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.

1 participant