mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-09 03:22:40 +00:00
fix(gateway/approvals): treat turnSourceTo as optional in chat approval bridge (#82132)
canBridgeNoDeviceChatApprovalFromBackend used matchesRequiredString for turnSourceTo, which returns false when expected is null. Channels without a recipient concept (webchat, control-ui) leave turnSourceTo null on both the approval snapshot and the replay params, so every backend gateway-client replay was rejected with APPROVAL_CLIENT_MISMATCH after the approval prompt was answered. turnSourceAccountId and turnSourceThreadId in the same function already use matchesOptionalString for the same reason; turnSourceTo was missed when PR #78728 added the helper. Switch to matchesOptionalString so null-on-both-sides matches. Cross-channel replay protection is preserved by the existing required turnSourceChannel and sessionKey checks. Added a regression test asserting webchat replay with null turnSourceTo is accepted.
This commit is contained in:
committed by
Peter Steinberger
parent
1e31bd2ac2
commit
2d91a3b200
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Gateway/approvals: treat `turnSourceTo` as optional in `canBridgeNoDeviceChatApprovalFromBackend`, matching the existing optional handling of `turnSourceAccountId` and `turnSourceThreadId`. Channels without a recipient concept (webchat, control-ui) leave `turnSourceTo` null on both the approval snapshot and the replay params, so the prior required-string check rejected every backend replay with `APPROVAL_CLIENT_MISMATCH`. Cross-channel replay is still gated by the required `turnSourceChannel` and `sessionKey` checks. Fixes #82132. (#82136) Thanks @ottodeng.
|
||||
- Cron: load runtime plugins before isolated cron model and delivery resolution so external channels can be selected for scheduled runs. (#82111) Thanks @medns.
|
||||
- Twitch: keep gateway accounts running until shutdown instead of treating successful monitor startup as a clean channel exit, preventing immediate auto-restart loops. Fixes #60071. (#81853) Thanks @edenfunf.
|
||||
- Agents/auto-reply: honor `agents.defaults.silentReply` and per-surface group silent-reply policy when generic agent-run failure fallbacks decide whether to send visible fallback text. Fixes #82060. (#82086) Thanks @taozengabc.
|
||||
|
||||
@@ -689,6 +689,52 @@ describe("sanitizeSystemRunParamsForForwarding", () => {
|
||||
expectAllowOnceForwardingResult(result);
|
||||
});
|
||||
|
||||
test("accepts trusted backend webchat replay when turnSourceTo is null on both sides (regression #82132)", () => {
|
||||
const sessionKey = "agent:main:main";
|
||||
const record = makeChatRecord({
|
||||
sessionKey,
|
||||
turnSourceChannel: "webchat",
|
||||
turnSourceTo: null,
|
||||
turnSourceAccountId: null,
|
||||
turnSourceThreadId: null,
|
||||
systemRunPlan: {
|
||||
argv: ["echo", "SAFE"],
|
||||
cwd: null,
|
||||
commandText: "echo SAFE",
|
||||
agentId: "main",
|
||||
sessionKey,
|
||||
},
|
||||
systemRunBinding: buildSystemRunApprovalBinding({
|
||||
argv: ["echo", "SAFE"],
|
||||
cwd: null,
|
||||
agentId: "main",
|
||||
sessionKey,
|
||||
}).binding,
|
||||
});
|
||||
|
||||
const result = sanitizeSystemRunParamsForForwarding({
|
||||
rawParams: {
|
||||
command: ["echo", "SAFE"],
|
||||
rawCommand: "echo SAFE",
|
||||
agentId: "main",
|
||||
sessionKey,
|
||||
turnSourceChannel: "webchat",
|
||||
turnSourceTo: null,
|
||||
turnSourceAccountId: null,
|
||||
turnSourceThreadId: null,
|
||||
runId: "approval-1",
|
||||
approved: true,
|
||||
approvalDecision: "allow-once",
|
||||
},
|
||||
nodeId: "node-1",
|
||||
client: trustedBackendClient,
|
||||
execApprovalManager: manager(record),
|
||||
nowMs: now,
|
||||
});
|
||||
|
||||
expectAllowOnceForwardingResult(result);
|
||||
});
|
||||
|
||||
test("rejects trusted backend chat replay when session binding changes", () => {
|
||||
const result = sanitizeSystemRunParamsForForwarding({
|
||||
rawParams: {
|
||||
|
||||
@@ -158,7 +158,13 @@ function canBridgeNoDeviceChatApprovalFromBackend(params: {
|
||||
actual: params.rawParams.turnSourceChannel,
|
||||
lowercase: true,
|
||||
}) &&
|
||||
matchesRequiredString({
|
||||
// turnSourceTo is channel-specific: required for messaging channels with a
|
||||
// recipient (e.g. telegram chat id), null for channels without a "to"
|
||||
// concept (webchat, control-ui). matchesRequiredString returns false on
|
||||
// null expected, which broke webchat node exec approval replay. Treat it
|
||||
// as optional so null-on-both-sides matches; required fields below
|
||||
// (turnSourceChannel, sessionKey) still gate cross-channel replays.
|
||||
matchesOptionalString({
|
||||
expected: request.turnSourceTo,
|
||||
actual: params.rawParams.turnSourceTo,
|
||||
}) &&
|
||||
|
||||
Reference in New Issue
Block a user