mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 21:58:26 +00:00
fix(gateway): bind approval ids to device identity
This commit is contained in:
@@ -154,7 +154,22 @@ export function sanitizeSystemRunParamsForForwarding(opts: {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot.requestedByConnId && snapshot.requestedByConnId !== (opts.client?.connId ?? null)) {
|
// Prefer binding by device identity (stable across reconnects / per-call clients like callGateway()).
|
||||||
|
// Fallback to connId only when device identity is not available.
|
||||||
|
const snapshotDeviceId = snapshot.requestedByDeviceId ?? null;
|
||||||
|
const clientDeviceId = opts.client?.connect?.device?.id ?? null;
|
||||||
|
if (snapshotDeviceId) {
|
||||||
|
if (snapshotDeviceId !== clientDeviceId) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
message: "approval id not valid for this device",
|
||||||
|
details: { code: "APPROVAL_DEVICE_MISMATCH", runId },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
snapshot.requestedByConnId &&
|
||||||
|
snapshot.requestedByConnId !== (opts.client?.connId ?? null)
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
message: "approval id not valid for this client",
|
message: "approval id not valid for this client",
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ describe("node.invoke approval bypass", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ws = await connectOperator(["operator.write", "operator.approvals"]);
|
const ws = await connectOperator(["operator.write", "operator.approvals"]);
|
||||||
|
const ws2 = await connectOperator(["operator.write"]);
|
||||||
|
|
||||||
const nodes = await rpcReq<{ nodes?: Array<{ nodeId: string; connected?: boolean }> }>(
|
const nodes = await rpcReq<{ nodes?: Array<{ nodeId: string; connected?: boolean }> }>(
|
||||||
ws,
|
ws,
|
||||||
@@ -159,7 +160,9 @@ describe("node.invoke approval bypass", () => {
|
|||||||
const requested = await requestP;
|
const requested = await requestP;
|
||||||
expect(requested.ok).toBe(true);
|
expect(requested.ok).toBe(true);
|
||||||
|
|
||||||
const invoke = await rpcReq(ws, "node.invoke", {
|
// Use a second WebSocket connection to simulate per-call clients (callGatewayTool/callGatewayCli).
|
||||||
|
// Approval binding should be based on device identity, not the ephemeral connId.
|
||||||
|
const invoke = await rpcReq(ws2, "node.invoke", {
|
||||||
nodeId,
|
nodeId,
|
||||||
command: "system.run",
|
command: "system.run",
|
||||||
params: {
|
params: {
|
||||||
@@ -179,6 +182,7 @@ describe("node.invoke approval bypass", () => {
|
|||||||
expect(lastInvokeParams?.approvalDecision).toBe("allow-once");
|
expect(lastInvokeParams?.approvalDecision).toBe("allow-once");
|
||||||
|
|
||||||
ws.close();
|
ws.close();
|
||||||
|
ws2.close();
|
||||||
node.stop();
|
node.stop();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user