Skip to content

security(gateway): block webchat session mutators#20800

Merged
vincentkoc merged 6 commits intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/webchat-session-mutators-guard-clean
Feb 19, 2026
Merged

security(gateway): block webchat session mutators#20800
vincentkoc merged 6 commits intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/webchat-session-mutators-guard-clean

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

@vincentkoc vincentkoc commented Feb 19, 2026

Summary

  • Problem: WebChat clients could call session mutation RPCs (sessions.patch, sessions.delete).
  • Why it matters: WebChat should remain session-scoped via chat flows and not perform broad session-store mutations.
  • What changed: Added explicit WebChat guardrails in sessions.patch and sessions.delete to reject these calls with clear errors.
  • What did NOT change (scope boundary): No changes to non-WebChat client behavior, session key resolution, or session store structure.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #

User-visible / Behavior Changes

  • WebChat clients now receive an INVALID_REQUEST error when attempting sessions.patch or sessions.delete.

Security Impact (required)

  • New permissions/capabilities? (Yes/No): No
  • Secrets/tokens handling changed? (Yes/No): No
  • New/changed network calls? (Yes/No): No
  • Command/tool execution surface changed? (Yes/No): No
  • Data access scope changed? (Yes/No): Yes
  • If any Yes, explain risk + mitigation:
    • Mitigation: Restricts WebChat clients from mutating arbitrary session records via session RPC mutators.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22+ / pnpm
  • Model/provider: N/A
  • Integration/channel (if any): Gateway WebChat client
  • Relevant config (redacted): default gateway test harness config

Steps

  1. Connect as a WebChat-classified client.
  2. Call sessions.patch for another session key.
  3. Call sessions.delete for another session key.

Expected

  • Both calls are rejected for WebChat clients.

Actual

  • Both calls are rejected with clear INVALID_REQUEST messages.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: Targeted e2e suite for gateway sessions, including the new WebChat regression test.
  • Edge cases checked: Existing session mutator behavior for non-WebChat clients remained intact in the same suite.
  • What you did not verify: Full repository test matrix.

Compatibility / Migration

  • Backward compatible? (Yes/No): Yes
  • Config/env changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert commit 6c1830bf7.
  • Files/config to restore: src/gateway/server-methods/sessions.ts, src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts
  • Known bad symptoms reviewers should watch for: Unexpected INVALID_REQUEST for non-WebChat clients (should not occur).

Risks and Mitigations

  • Risk: Over-blocking legitimate non-WebChat session mutator usage.
    • Mitigation: Guard condition is explicitly scoped to isWebchatConnect(client.connect).

Agent-Signoff: Shellfish-Guard

Greptile Summary

Added WebChat client guards to sessions.patch and sessions.delete to prevent session-store mutations outside of chat flows. The guards properly check client type using isWebchatConnect() and return clear INVALID_REQUEST errors. Implementation is clean, well-tested, and maintains backward compatibility for non-WebChat clients.

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk - it adds security hardening with clear guardrails and comprehensive test coverage
  • Score reflects well-implemented security guardrails with proper test coverage. Minor point deducted for potential inconsistency with sessions.reset/compact not having similar guards, which should be verified as intentional
  • Verify sessions.reset and sessions.compact don't need similar WebChat restrictions in src/gateway/server-methods/sessions.ts

Last reviewed commit: d127e01

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S maintainer Maintainer-authored PR labels Feb 19, 2026
@vincentkoc vincentkoc changed the title Gateway: block webchat session mutators security(gateway): block webchat session mutators Feb 19, 2026
@vincentkoc vincentkoc self-assigned this Feb 19, 2026
@vincentkoc vincentkoc marked this pull request as ready for review February 19, 2026 09:53
@vincentkoc vincentkoc merged commit 981d266 into openclaw:main Feb 19, 2026
23 checks passed
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 285 to 421
"sessions.reset": async ({ params, respond }) => {
if (!assertValidParams(params, validateSessionsResetParams, "sessions.reset", respond)) {
return;
}
const p = params;
const key = requireSessionKey(p.key, respond);
if (!key) {
return;
}

const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
const { entry } = loadSessionEntry(key);
const commandReason = p.reason === "new" ? "new" : "reset";
const hookEvent = createInternalHookEvent(
"command",
commandReason,
target.canonicalKey ?? key,
{
sessionEntry: entry,
previousSessionEntry: entry,
commandSource: "gateway:sessions.reset",
cfg,
},
);
await triggerInternalHook(hookEvent);
const sessionId = entry?.sessionId;
const cleanupError = await ensureSessionRuntimeCleanup({ cfg, key, target, sessionId });
if (cleanupError) {
respond(false, undefined, cleanupError);
return;
}
let oldSessionId: string | undefined;
let oldSessionFile: string | undefined;
const next = await updateSessionStore(storePath, (store) => {
const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
const entry = store[primaryKey];
oldSessionId = entry?.sessionId;
oldSessionFile = entry?.sessionFile;
const now = Date.now();
const nextEntry: SessionEntry = {
sessionId: randomUUID(),
updatedAt: now,
systemSent: false,
abortedLastRun: false,
thinkingLevel: entry?.thinkingLevel,
verboseLevel: entry?.verboseLevel,
reasoningLevel: entry?.reasoningLevel,
responseUsage: entry?.responseUsage,
model: entry?.model,
contextTokens: entry?.contextTokens,
sendPolicy: entry?.sendPolicy,
label: entry?.label,
origin: snapshotSessionOrigin(entry),
lastChannel: entry?.lastChannel,
lastTo: entry?.lastTo,
skillsSnapshot: entry?.skillsSnapshot,
// Reset token counts to 0 on session reset (#1523)
inputTokens: 0,
outputTokens: 0,
totalTokens: 0,
totalTokensFresh: true,
};
store[primaryKey] = nextEntry;
return nextEntry;
});
// Archive old transcript so it doesn't accumulate on disk (#14869).
archiveSessionTranscriptsForSession({
sessionId: oldSessionId,
storePath,
sessionFile: oldSessionFile,
agentId: target.agentId,
reason: "reset",
});
respond(true, { ok: true, key: target.canonicalKey, entry: next }, undefined);
},
"sessions.delete": async ({ params, respond }) => {
"sessions.delete": async ({ params, respond, client, isWebchatConnect }) => {
if (!assertValidParams(params, validateSessionsDeleteParams, "sessions.delete", respond)) {
return;
}
const p = params;
const key = requireSessionKey(p.key, respond);
if (!key) {
return;
}
if (client?.connect && isWebchatConnect(client.connect)) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
"webchat clients cannot delete sessions; use chat.send for session-scoped updates",
),
);
return;
}

const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
const mainKey = resolveMainSessionKey(cfg);
if (target.canonicalKey === mainKey) {
respond(
false,
undefined,
errorShape(ErrorCodes.INVALID_REQUEST, `Cannot delete the main session (${mainKey}).`),
);
return;
}

const deleteTranscript = typeof p.deleteTranscript === "boolean" ? p.deleteTranscript : true;

const { entry } = loadSessionEntry(key);
const sessionId = entry?.sessionId;
const existed = Boolean(entry);
const cleanupError = await ensureSessionRuntimeCleanup({ cfg, key, target, sessionId });
if (cleanupError) {
respond(false, undefined, cleanupError);
return;
}
await updateSessionStore(storePath, (store) => {
const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
if (store[primaryKey]) {
delete store[primaryKey];
}
});

const archived = deleteTranscript
? archiveSessionTranscriptsForSession({
sessionId,
storePath,
sessionFile: entry?.sessionFile,
agentId: target.agentId,
reason: "deleted",
})
: [];

respond(true, { ok: true, key: target.canonicalKey, deleted: existed, archived }, undefined);
},
"sessions.compact": async ({ params, respond }) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that sessions.reset (line 285) and sessions.compact (line 421) intentionally allow WebChat clients - both methods mutate session state similar to patch/delete

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-methods/sessions.ts
Line: 285-421

Comment:
Check that `sessions.reset` (line 285) and `sessions.compact` (line 421) intentionally allow WebChat clients - both methods mutate session state similar to patch/delete

How can I resolve this? If you propose a fix, please make it concise.

mmyyfirstb pushed a commit to mmyyfirstb/openclaw that referenced this pull request Feb 21, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard
xianfeng92 pushed a commit to xianfeng92/openclaw that referenced this pull request Feb 24, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard

(cherry picked from commit 981d266)
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard

(cherry picked from commit 981d266)

# Conflicts:
#	.gitignore
#	CHANGELOG.md
#	src/gateway/server-methods/sessions.ts
#	src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard

(cherry picked from commit 981d266)

# Conflicts:
#	.gitignore
#	CHANGELOG.md
#	src/gateway/server-methods/sessions.ts
#	src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard
vincentkoc added a commit that referenced this pull request Mar 30, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
Alix-007 pushed a commit to Alix-007/openclaw that referenced this pull request Mar 30, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (openclaw#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (openclaw#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (openclaw#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (openclaw#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
Tardisyuan pushed a commit to Tardisyuan/openclaw that referenced this pull request Apr 30, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (openclaw#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…oks.md

- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (openclaw#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant