mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 16:41:23 +00:00
fix(security): harden channel auth path checks and exec approval routing
This commit is contained in:
@@ -40,6 +40,10 @@ describe("requestExecApprovalDecision", () => {
|
||||
agentId: "main",
|
||||
resolvedPath: "/usr/bin/echo",
|
||||
sessionKey: "session",
|
||||
turnSourceChannel: "whatsapp",
|
||||
turnSourceTo: "+15555550123",
|
||||
turnSourceAccountId: "work",
|
||||
turnSourceThreadId: "1739201675.123",
|
||||
});
|
||||
|
||||
expect(result).toBe("allow-once");
|
||||
@@ -57,6 +61,10 @@ describe("requestExecApprovalDecision", () => {
|
||||
agentId: "main",
|
||||
resolvedPath: "/usr/bin/echo",
|
||||
sessionKey: "session",
|
||||
turnSourceChannel: "whatsapp",
|
||||
turnSourceTo: "+15555550123",
|
||||
turnSourceAccountId: "work",
|
||||
turnSourceThreadId: "1739201675.123",
|
||||
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
twoPhase: true,
|
||||
},
|
||||
|
||||
@@ -17,6 +17,10 @@ export type RequestExecApprovalDecisionParams = {
|
||||
agentId?: string;
|
||||
resolvedPath?: string;
|
||||
sessionKey?: string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceTo?: string;
|
||||
turnSourceAccountId?: string;
|
||||
turnSourceThreadId?: string | number;
|
||||
};
|
||||
|
||||
type ParsedDecision = { present: boolean; value: string | null };
|
||||
@@ -72,6 +76,10 @@ export async function registerExecApprovalRequest(
|
||||
agentId: params.agentId,
|
||||
resolvedPath: params.resolvedPath,
|
||||
sessionKey: params.sessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
twoPhase: true,
|
||||
},
|
||||
@@ -127,6 +135,10 @@ export async function requestExecApprovalDecisionForHost(params: {
|
||||
agentId?: string;
|
||||
resolvedPath?: string;
|
||||
sessionKey?: string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceTo?: string;
|
||||
turnSourceAccountId?: string;
|
||||
turnSourceThreadId?: string | number;
|
||||
}): Promise<string | null> {
|
||||
return await requestExecApprovalDecision({
|
||||
id: params.approvalId,
|
||||
@@ -140,6 +152,10 @@ export async function requestExecApprovalDecisionForHost(params: {
|
||||
agentId: params.agentId,
|
||||
resolvedPath: params.resolvedPath,
|
||||
sessionKey: params.sessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -155,6 +171,10 @@ export async function registerExecApprovalRequestForHost(params: {
|
||||
agentId?: string;
|
||||
resolvedPath?: string;
|
||||
sessionKey?: string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceTo?: string;
|
||||
turnSourceAccountId?: string;
|
||||
turnSourceThreadId?: string | number;
|
||||
}): Promise<ExecApprovalRegistration> {
|
||||
return await registerExecApprovalRequest({
|
||||
id: params.approvalId,
|
||||
@@ -168,5 +188,9 @@ export async function registerExecApprovalRequestForHost(params: {
|
||||
agentId: params.agentId,
|
||||
resolvedPath: params.resolvedPath,
|
||||
sessionKey: params.sessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ export type ProcessGatewayAllowlistParams = {
|
||||
safeBinProfiles: Readonly<Record<string, SafeBinProfile>>;
|
||||
agentId?: string;
|
||||
sessionKey?: string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceTo?: string;
|
||||
turnSourceAccountId?: string;
|
||||
turnSourceThreadId?: string | number;
|
||||
scopeKey?: string;
|
||||
warnings: string[];
|
||||
notifySessionKey?: string;
|
||||
@@ -159,6 +163,10 @@ export async function processGatewayAllowlist(
|
||||
agentId: params.agentId,
|
||||
resolvedPath,
|
||||
sessionKey: params.sessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
});
|
||||
expiresAtMs = registration.expiresAtMs;
|
||||
preResolvedDecision = registration.finalDecision;
|
||||
|
||||
@@ -35,6 +35,10 @@ export type ExecuteNodeHostCommandParams = {
|
||||
requestedNode?: string;
|
||||
boundNode?: string;
|
||||
sessionKey?: string;
|
||||
turnSourceChannel?: string;
|
||||
turnSourceTo?: string;
|
||||
turnSourceAccountId?: string;
|
||||
turnSourceThreadId?: string | number;
|
||||
agentId?: string;
|
||||
security: ExecSecurity;
|
||||
ask: ExecAsk;
|
||||
@@ -202,6 +206,10 @@ export async function executeNodeHostCommand(
|
||||
ask: hostAsk,
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
turnSourceChannel: params.turnSourceChannel,
|
||||
turnSourceTo: params.turnSourceTo,
|
||||
turnSourceAccountId: params.turnSourceAccountId,
|
||||
turnSourceThreadId: params.turnSourceThreadId,
|
||||
});
|
||||
expiresAtMs = registration.expiresAtMs;
|
||||
preResolvedDecision = registration.finalDecision;
|
||||
|
||||
@@ -21,6 +21,9 @@ export type ExecToolDefaults = {
|
||||
scopeKey?: string;
|
||||
sessionKey?: string;
|
||||
messageProvider?: string;
|
||||
currentChannelId?: string;
|
||||
currentThreadTs?: string;
|
||||
accountId?: string;
|
||||
notifyOnExit?: boolean;
|
||||
notifyOnExitEmptySuccess?: boolean;
|
||||
cwd?: string;
|
||||
|
||||
@@ -407,6 +407,10 @@ export function createExecTool(
|
||||
requestedNode: params.node?.trim(),
|
||||
boundNode: defaults?.node?.trim(),
|
||||
sessionKey: defaults?.sessionKey,
|
||||
turnSourceChannel: defaults?.messageProvider,
|
||||
turnSourceTo: defaults?.currentChannelId,
|
||||
turnSourceAccountId: defaults?.accountId,
|
||||
turnSourceThreadId: defaults?.currentThreadTs,
|
||||
agentId,
|
||||
security,
|
||||
ask,
|
||||
@@ -433,6 +437,10 @@ export function createExecTool(
|
||||
safeBinProfiles,
|
||||
agentId,
|
||||
sessionKey: defaults?.sessionKey,
|
||||
turnSourceChannel: defaults?.messageProvider,
|
||||
turnSourceTo: defaults?.currentChannelId,
|
||||
turnSourceAccountId: defaults?.accountId,
|
||||
turnSourceThreadId: defaults?.currentThreadTs,
|
||||
scopeKey: defaults?.scopeKey,
|
||||
warnings,
|
||||
notifySessionKey,
|
||||
|
||||
@@ -116,6 +116,10 @@ export function createOpenClawTools(options?: {
|
||||
createCanvasTool({ config: options?.config }),
|
||||
createNodesTool({
|
||||
agentSessionKey: options?.agentSessionKey,
|
||||
agentChannel: options?.agentChannel,
|
||||
agentAccountId: options?.agentAccountId,
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
config: options?.config,
|
||||
}),
|
||||
createCronTool({
|
||||
|
||||
@@ -401,6 +401,9 @@ export function createOpenClawCodingTools(options?: {
|
||||
scopeKey,
|
||||
sessionKey: options?.sessionKey,
|
||||
messageProvider: options?.messageProvider,
|
||||
currentChannelId: options?.currentChannelId,
|
||||
currentThreadTs: options?.currentThreadTs,
|
||||
accountId: options?.agentAccountId,
|
||||
backgroundMs: options?.exec?.backgroundMs ?? execConfig.backgroundMs,
|
||||
timeoutSec: options?.exec?.timeoutSec ?? execConfig.timeoutSec,
|
||||
approvalRunningNoticeMs:
|
||||
|
||||
@@ -20,6 +20,7 @@ import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { formatExecCommand } from "../../infra/system-run-command.js";
|
||||
import { imageMimeFromFormat } from "../../media/mime.js";
|
||||
import type { GatewayMessageChannel } from "../../utils/message-channel.js";
|
||||
import { resolveSessionAgentId } from "../agent-scope.js";
|
||||
import { resolveImageSanitizationLimits } from "../image-sanitization.js";
|
||||
import { optionalStringEnum, stringEnum } from "../schema/typebox.js";
|
||||
@@ -128,9 +129,17 @@ const NodesToolSchema = Type.Object({
|
||||
|
||||
export function createNodesTool(options?: {
|
||||
agentSessionKey?: string;
|
||||
agentChannel?: GatewayMessageChannel;
|
||||
agentAccountId?: string;
|
||||
currentChannelId?: string;
|
||||
currentThreadTs?: string | number;
|
||||
config?: OpenClawConfig;
|
||||
}): AnyAgentTool {
|
||||
const sessionKey = options?.agentSessionKey?.trim() || undefined;
|
||||
const turnSourceChannel = options?.agentChannel?.trim() || undefined;
|
||||
const turnSourceTo = options?.currentChannelId?.trim() || undefined;
|
||||
const turnSourceAccountId = options?.agentAccountId?.trim() || undefined;
|
||||
const turnSourceThreadId = options?.currentThreadTs;
|
||||
const agentId = resolveSessionAgentId({
|
||||
sessionKey: options?.agentSessionKey,
|
||||
config: options?.config,
|
||||
@@ -512,6 +521,10 @@ export function createNodesTool(options?: {
|
||||
host: "node",
|
||||
agentId,
|
||||
sessionKey,
|
||||
turnSourceChannel,
|
||||
turnSourceTo,
|
||||
turnSourceAccountId,
|
||||
turnSourceThreadId,
|
||||
timeoutMs: APPROVAL_TIMEOUT_MS,
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user