mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 03:04:32 +00:00
fix(security): fail-close node camera URL downloads
This commit is contained in:
@@ -28,7 +28,7 @@ import { optionalStringEnum, stringEnum } from "../schema/typebox.js";
|
||||
import { sanitizeToolResultImages } from "../tool-images.js";
|
||||
import { type AnyAgentTool, jsonResult, readStringParam } from "./common.js";
|
||||
import { callGatewayTool, readGatewayCallOptions } from "./gateway.js";
|
||||
import { listNodes, resolveNodeIdFromList, resolveNodeId } from "./nodes-utils.js";
|
||||
import { listNodes, resolveNode, resolveNodeId, resolveNodeIdFromList } from "./nodes-utils.js";
|
||||
|
||||
const NODES_TOOL_ACTIONS = [
|
||||
"status",
|
||||
@@ -230,7 +230,8 @@ export function createNodesTool(options?: {
|
||||
}
|
||||
case "camera_snap": {
|
||||
const node = readStringParam(params, "node", { required: true });
|
||||
const nodeId = await resolveNodeId(gatewayOpts, node);
|
||||
const resolvedNode = await resolveNode(gatewayOpts, node);
|
||||
const nodeId = resolvedNode.nodeId;
|
||||
const facingRaw =
|
||||
typeof params.facing === "string" ? params.facing.toLowerCase() : "front";
|
||||
const facings: CameraFacing[] =
|
||||
@@ -295,7 +296,12 @@ export function createNodesTool(options?: {
|
||||
ext: isJpeg ? "jpg" : "png",
|
||||
});
|
||||
if (payload.url) {
|
||||
await writeUrlToFile(filePath, payload.url);
|
||||
if (!resolvedNode.remoteIp) {
|
||||
throw new Error("camera URL payload requires node remoteIp");
|
||||
}
|
||||
await writeUrlToFile(filePath, payload.url, {
|
||||
expectedHost: resolvedNode.remoteIp,
|
||||
});
|
||||
} else if (payload.base64) {
|
||||
await writeBase64ToFile(filePath, payload.base64);
|
||||
}
|
||||
@@ -373,7 +379,8 @@ export function createNodesTool(options?: {
|
||||
}
|
||||
case "camera_clip": {
|
||||
const node = readStringParam(params, "node", { required: true });
|
||||
const nodeId = await resolveNodeId(gatewayOpts, node);
|
||||
const resolvedNode = await resolveNode(gatewayOpts, node);
|
||||
const nodeId = resolvedNode.nodeId;
|
||||
const facing =
|
||||
typeof params.facing === "string" ? params.facing.toLowerCase() : "front";
|
||||
if (facing !== "front" && facing !== "back") {
|
||||
@@ -407,6 +414,7 @@ export function createNodesTool(options?: {
|
||||
const filePath = await writeCameraClipPayloadToFile({
|
||||
payload,
|
||||
facing,
|
||||
expectedHost: resolvedNode.remoteIp,
|
||||
});
|
||||
return {
|
||||
content: [{ type: "text", text: `FILE:${filePath}` }],
|
||||
|
||||
@@ -160,6 +160,15 @@ export async function resolveNodeId(
|
||||
query?: string,
|
||||
allowDefault = false,
|
||||
) {
|
||||
const nodes = await loadNodes(opts);
|
||||
return resolveNodeIdFromList(nodes, query, allowDefault);
|
||||
return (await resolveNode(opts, query, allowDefault)).nodeId;
|
||||
}
|
||||
|
||||
export async function resolveNode(
|
||||
opts: GatewayCallOptions,
|
||||
query?: string,
|
||||
allowDefault = false,
|
||||
): Promise<NodeListNode> {
|
||||
const nodes = await loadNodes(opts);
|
||||
const nodeId = resolveNodeIdFromList(nodes, query, allowDefault);
|
||||
return nodes.find((node) => node.nodeId === nodeId) ?? { nodeId };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user