Skip to content

Commit 8da4343

Browse files
committed
fix: normalize message action bridge provenance
1 parent 0b3b33f commit 8da4343

2 files changed

Lines changed: 69 additions & 2 deletions

File tree

src/infra/outbound/message-action-runner.plugin-dispatch.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,71 @@ describe("runMessageAction plugin dispatch", () => {
674674
);
675675
});
676676

677+
it("keeps blank backend requester provenance least-privileged", async () => {
678+
const handleActionEntry = vi.fn(async () => jsonResult({ ok: true, local: true }));
679+
const gatewayPlugin = createGatewayActionPlugin({
680+
pluginId: "gatewaychat",
681+
label: "Gateway Chat",
682+
blurb: "Gateway Chat blank requester test plugin.",
683+
actions: ["react"],
684+
capabilities: { chatTypes: ["direct"], reactions: true },
685+
handleAction: handleActionEntry,
686+
});
687+
setActivePluginRegistry(
688+
createTestRegistry([
689+
{
690+
pluginId: "gatewaychat",
691+
source: "test",
692+
plugin: gatewayPlugin,
693+
},
694+
]),
695+
);
696+
mocks.callGatewayLeastPrivilege.mockResolvedValue({
697+
ok: true,
698+
added: "✅",
699+
});
700+
701+
await runMessageAction({
702+
cfg: {
703+
channels: {
704+
gatewaychat: {
705+
enabled: true,
706+
},
707+
},
708+
} as OpenClawConfig,
709+
action: "react",
710+
params: {
711+
channel: "gatewaychat",
712+
to: "+15551234567",
713+
chatJid: "+15551234567",
714+
messageId: "wamid.1",
715+
emoji: "✅",
716+
},
717+
requesterSenderId: " ",
718+
gateway: {
719+
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
720+
mode: GATEWAY_CLIENT_MODES.BACKEND,
721+
},
722+
dryRun: false,
723+
});
724+
725+
const gatewayCall = readMockCallArg(
726+
mocks.callGatewayLeastPrivilege,
727+
"gateway least privilege call",
728+
);
729+
expectRecordFields(
730+
gatewayCall,
731+
{
732+
method: "message.action",
733+
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
734+
mode: GATEWAY_CLIENT_MODES.BACKEND,
735+
},
736+
"gateway call",
737+
);
738+
expect(mocks.callGateway).not.toHaveBeenCalled();
739+
expect(handleActionEntry).not.toHaveBeenCalled();
740+
});
741+
677742
it("keeps CLI gateway-executed actions least-privileged when they carry sender ownership", async () => {
678743
const handleActionEntry = vi.fn(async () => jsonResult({ ok: true, local: true }));
679744
const gatewayPlugin = createGatewayActionPlugin({

src/infra/outbound/message-action-runner.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,12 @@ async function callGatewayMessageAction<T>(params: {
219219
const isTrustedBackendBridge =
220220
gateway.clientName === GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT &&
221221
gateway.mode === GATEWAY_CLIENT_MODES.BACKEND;
222+
const requesterAccountId = normalizeOptionalString(params.actionParams.requesterAccountId);
223+
const requesterSenderId = normalizeOptionalString(params.actionParams.requesterSenderId);
222224
const carriesTrustedRequester =
223225
isTrustedBackendBridge &&
224-
(params.actionParams.requesterAccountId !== undefined ||
225-
params.actionParams.requesterSenderId !== undefined ||
226+
(requesterAccountId !== undefined ||
227+
requesterSenderId !== undefined ||
226228
params.actionParams.senderIsOwner !== undefined);
227229
if (!carriesTrustedRequester) {
228230
return await callGatewayLeastPrivilege<T>(callParams);

0 commit comments

Comments
 (0)