mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(outbound): clamp message gateway timeouts
This commit is contained in:
@@ -41,8 +41,6 @@ import { stripUnsupportedCitationControlMarkers } from "../../shared/text/citati
|
||||
import { stripFormattedReasoningMessage } from "../../shared/text/formatted-reasoning-message.js";
|
||||
import { parseInlineDirectives } from "../../utils/directive-tags.js";
|
||||
import {
|
||||
GATEWAY_CLIENT_MODES,
|
||||
GATEWAY_CLIENT_NAMES,
|
||||
INTERNAL_MESSAGE_CHANNEL,
|
||||
type GatewayClientMode,
|
||||
type GatewayClientName,
|
||||
@@ -73,6 +71,7 @@ import {
|
||||
resolveAndApplyOutboundThreadId,
|
||||
} from "./message-action-threading.js";
|
||||
import { maybeApplyTtsToMessageActionSendPayload } from "./message-action-tts.js";
|
||||
import { resolveOutboundMessageGatewayOptions } from "./message-gateway-options.js";
|
||||
import type { MessagePollResult, MessageSendResult } from "./message.js";
|
||||
import {
|
||||
applyCrossContextDecoration,
|
||||
@@ -187,22 +186,7 @@ export function getToolResult(
|
||||
}
|
||||
|
||||
function resolveGatewayActionOptions(gateway?: MessageActionRunnerGateway) {
|
||||
const url =
|
||||
gateway?.mode === GATEWAY_CLIENT_MODES.BACKEND ||
|
||||
gateway?.clientName === GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT
|
||||
? undefined
|
||||
: gateway?.url;
|
||||
return {
|
||||
url,
|
||||
token: gateway?.token,
|
||||
timeoutMs:
|
||||
typeof gateway?.timeoutMs === "number" && Number.isFinite(gateway.timeoutMs)
|
||||
? Math.max(1, Math.floor(gateway.timeoutMs))
|
||||
: 10_000,
|
||||
clientName: gateway?.clientName ?? GATEWAY_CLIENT_NAMES.CLI,
|
||||
clientDisplayName: gateway?.clientDisplayName,
|
||||
mode: gateway?.mode ?? GATEWAY_CLIENT_MODES.CLI,
|
||||
};
|
||||
return resolveOutboundMessageGatewayOptions(gateway);
|
||||
}
|
||||
|
||||
async function callGatewayMessageAction<T>(params: {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { MAX_TIMER_TIMEOUT_MS } from "../../shared/number-coercion.js";
|
||||
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js";
|
||||
import { resolveOutboundMessageGatewayOptions } from "./message-gateway-options.js";
|
||||
|
||||
describe("resolveOutboundMessageGatewayOptions", () => {
|
||||
it("clamps oversized gateway timeouts", () => {
|
||||
expect(
|
||||
resolveOutboundMessageGatewayOptions({ timeoutMs: Number.MAX_SAFE_INTEGER }).timeoutMs,
|
||||
).toBe(MAX_TIMER_TIMEOUT_MS);
|
||||
});
|
||||
|
||||
it("drops caller-provided urls for backend gateway callers", () => {
|
||||
expect(
|
||||
resolveOutboundMessageGatewayOptions({
|
||||
url: "http://attacker.invalid",
|
||||
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
|
||||
mode: GATEWAY_CLIENT_MODES.BACKEND,
|
||||
}).url,
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { resolveTimerTimeoutMs } from "../../shared/number-coercion.js";
|
||||
import {
|
||||
GATEWAY_CLIENT_MODES,
|
||||
GATEWAY_CLIENT_NAMES,
|
||||
type GatewayClientMode,
|
||||
type GatewayClientName,
|
||||
} from "../../utils/message-channel.js";
|
||||
|
||||
export type OutboundMessageGatewayOptionsInput = {
|
||||
url?: string;
|
||||
token?: string;
|
||||
timeoutMs?: number;
|
||||
clientName?: GatewayClientName;
|
||||
clientDisplayName?: string;
|
||||
mode?: GatewayClientMode;
|
||||
};
|
||||
|
||||
export function resolveOutboundMessageGatewayOptions(gateway?: OutboundMessageGatewayOptionsInput) {
|
||||
const clientName = gateway?.clientName ?? GATEWAY_CLIENT_NAMES.CLI;
|
||||
const mode = gateway?.mode ?? GATEWAY_CLIENT_MODES.CLI;
|
||||
const url =
|
||||
mode === GATEWAY_CLIENT_MODES.BACKEND || clientName === GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT
|
||||
? undefined
|
||||
: gateway?.url;
|
||||
return {
|
||||
url,
|
||||
token: gateway?.token,
|
||||
timeoutMs: resolveTimerTimeoutMs(gateway?.timeoutMs, 10_000),
|
||||
clientName,
|
||||
clientDisplayName: gateway?.clientDisplayName,
|
||||
mode,
|
||||
};
|
||||
}
|
||||
@@ -5,12 +5,6 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { OutboundMediaAccess } from "../../media/load-options.js";
|
||||
import type { PollInput } from "../../polls.js";
|
||||
import { normalizePollInput } from "../../polls.js";
|
||||
import {
|
||||
GATEWAY_CLIENT_MODES,
|
||||
GATEWAY_CLIENT_NAMES,
|
||||
type GatewayClientMode,
|
||||
type GatewayClientName,
|
||||
} from "../../utils/message-channel.js";
|
||||
import { resolveOutboundChannelPlugin } from "./channel-resolution.js";
|
||||
import { resolveMessageChannelSelection } from "./channel-selection.js";
|
||||
import {
|
||||
@@ -20,6 +14,10 @@ import {
|
||||
type OutboundDeliveryQueuePolicy,
|
||||
type OutboundSendDeps,
|
||||
} from "./deliver.js";
|
||||
import {
|
||||
resolveOutboundMessageGatewayOptions,
|
||||
type OutboundMessageGatewayOptionsInput,
|
||||
} from "./message-gateway-options.js";
|
||||
import type { OutboundMirror } from "./mirror.js";
|
||||
import {
|
||||
createOutboundPayloadPlan,
|
||||
@@ -44,14 +42,7 @@ function loadMessageGatewayRuntime() {
|
||||
return messageGatewayRuntimePromise;
|
||||
}
|
||||
|
||||
export type MessageGatewayOptions = {
|
||||
url?: string;
|
||||
token?: string;
|
||||
timeoutMs?: number;
|
||||
clientName?: GatewayClientName;
|
||||
clientDisplayName?: string;
|
||||
mode?: GatewayClientMode;
|
||||
};
|
||||
export type MessageGatewayOptions = OutboundMessageGatewayOptionsInput;
|
||||
|
||||
type MessageSendParams = {
|
||||
to: string;
|
||||
@@ -261,24 +252,7 @@ async function assertRequiredMessageSendDurability(params: {
|
||||
}
|
||||
|
||||
function resolveGatewayOptions(opts?: MessageGatewayOptions) {
|
||||
// Security: backend callers (tools/agents) must not accept user-controlled gateway URLs.
|
||||
// Use config-derived gateway target only.
|
||||
const url =
|
||||
opts?.mode === GATEWAY_CLIENT_MODES.BACKEND ||
|
||||
opts?.clientName === GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT
|
||||
? undefined
|
||||
: opts?.url;
|
||||
return {
|
||||
url,
|
||||
token: opts?.token,
|
||||
timeoutMs:
|
||||
typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs)
|
||||
? Math.max(1, Math.floor(opts.timeoutMs))
|
||||
: 10_000,
|
||||
clientName: opts?.clientName ?? GATEWAY_CLIENT_NAMES.CLI,
|
||||
clientDisplayName: opts?.clientDisplayName,
|
||||
mode: opts?.mode ?? GATEWAY_CLIENT_MODES.CLI,
|
||||
};
|
||||
return resolveOutboundMessageGatewayOptions(opts);
|
||||
}
|
||||
|
||||
async function callMessageGateway<T>(params: {
|
||||
|
||||
Reference in New Issue
Block a user