security(gateway): block webchat session mutators (#20800)

* chore(ci): local claude settings gitignore

* Gateway: block webchat session mutators

* Changelog: note webchat session mutator guard

* Changelog: credit report for webchat mutator guard
This commit is contained in:
Vincent Koc
2026-02-19 01:54:02 -08:00
committed by GitHub
parent 32ba62dc69
commit 981d266480
4 changed files with 76 additions and 2 deletions

View File

@@ -231,7 +231,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
}
respond(true, { ok: true, key: resolved.key }, undefined);
},
"sessions.patch": async ({ params, respond, context }) => {
"sessions.patch": async ({ params, respond, context, client, isWebchatConnect }) => {
if (!assertValidParams(params, validateSessionsPatchParams, "sessions.patch", respond)) {
return;
}
@@ -240,6 +240,17 @@ export const sessionsHandlers: GatewayRequestHandlers = {
if (!key) {
return;
}
if (client?.connect && isWebchatConnect(client.connect)) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
"webchat clients cannot patch sessions; use chat.send for session-scoped updates",
),
);
return;
}
const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
const applied = await updateSessionStore(storePath, async (store) => {
@@ -346,7 +357,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
});
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;
}
@@ -355,6 +366,17 @@ export const sessionsHandlers: GatewayRequestHandlers = {
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);