mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 07:07:27 +00:00
125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
import type { ClawdbotConfig } from "../../config/config.js";
|
|
import { resolveAgentConfig } from "../agent-scope.js";
|
|
import { expandToolGroups } from "../tool-policy.js";
|
|
import { DEFAULT_TOOL_ALLOW, DEFAULT_TOOL_DENY } from "./constants.js";
|
|
import type {
|
|
SandboxToolPolicy,
|
|
SandboxToolPolicyResolved,
|
|
SandboxToolPolicySource,
|
|
} from "./types.js";
|
|
|
|
type CompiledPattern =
|
|
| { kind: "all" }
|
|
| { kind: "exact"; value: string }
|
|
| { kind: "regex"; value: RegExp };
|
|
|
|
function compilePattern(pattern: string): CompiledPattern {
|
|
const normalized = pattern.trim().toLowerCase();
|
|
if (!normalized) return { kind: "exact", value: "" };
|
|
if (normalized === "*") return { kind: "all" };
|
|
if (!normalized.includes("*")) return { kind: "exact", value: normalized };
|
|
const escaped = normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
return {
|
|
kind: "regex",
|
|
value: new RegExp(`^${escaped.replaceAll("\\*", ".*")}$`),
|
|
};
|
|
}
|
|
|
|
function compilePatterns(patterns?: string[]): CompiledPattern[] {
|
|
if (!Array.isArray(patterns)) return [];
|
|
return expandToolGroups(patterns)
|
|
.map(compilePattern)
|
|
.filter((pattern) => pattern.kind !== "exact" || pattern.value);
|
|
}
|
|
|
|
function matchesAny(name: string, patterns: CompiledPattern[]): boolean {
|
|
for (const pattern of patterns) {
|
|
if (pattern.kind === "all") return true;
|
|
if (pattern.kind === "exact" && name === pattern.value) return true;
|
|
if (pattern.kind === "regex" && pattern.value.test(name)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function isToolAllowed(policy: SandboxToolPolicy, name: string) {
|
|
const normalized = name.trim().toLowerCase();
|
|
const deny = compilePatterns(policy.deny);
|
|
if (matchesAny(normalized, deny)) return false;
|
|
const allow = compilePatterns(policy.allow);
|
|
if (allow.length === 0) return true;
|
|
return matchesAny(normalized, allow);
|
|
}
|
|
|
|
export function resolveSandboxToolPolicyForAgent(
|
|
cfg?: ClawdbotConfig,
|
|
agentId?: string,
|
|
): SandboxToolPolicyResolved {
|
|
const agentConfig = cfg && agentId ? resolveAgentConfig(cfg, agentId) : undefined;
|
|
const agentAllow = agentConfig?.tools?.sandbox?.tools?.allow;
|
|
const agentDeny = agentConfig?.tools?.sandbox?.tools?.deny;
|
|
const globalAllow = cfg?.tools?.sandbox?.tools?.allow;
|
|
const globalDeny = cfg?.tools?.sandbox?.tools?.deny;
|
|
|
|
const allowSource = Array.isArray(agentAllow)
|
|
? ({
|
|
source: "agent",
|
|
key: "agents.list[].tools.sandbox.tools.allow",
|
|
} satisfies SandboxToolPolicySource)
|
|
: Array.isArray(globalAllow)
|
|
? ({
|
|
source: "global",
|
|
key: "tools.sandbox.tools.allow",
|
|
} satisfies SandboxToolPolicySource)
|
|
: ({
|
|
source: "default",
|
|
key: "tools.sandbox.tools.allow",
|
|
} satisfies SandboxToolPolicySource);
|
|
|
|
const denySource = Array.isArray(agentDeny)
|
|
? ({
|
|
source: "agent",
|
|
key: "agents.list[].tools.sandbox.tools.deny",
|
|
} satisfies SandboxToolPolicySource)
|
|
: Array.isArray(globalDeny)
|
|
? ({
|
|
source: "global",
|
|
key: "tools.sandbox.tools.deny",
|
|
} satisfies SandboxToolPolicySource)
|
|
: ({
|
|
source: "default",
|
|
key: "tools.sandbox.tools.deny",
|
|
} satisfies SandboxToolPolicySource);
|
|
|
|
const deny = Array.isArray(agentDeny)
|
|
? agentDeny
|
|
: Array.isArray(globalDeny)
|
|
? globalDeny
|
|
: [...DEFAULT_TOOL_DENY];
|
|
const allow = Array.isArray(agentAllow)
|
|
? agentAllow
|
|
: Array.isArray(globalAllow)
|
|
? globalAllow
|
|
: [...DEFAULT_TOOL_ALLOW];
|
|
|
|
const expandedDeny = expandToolGroups(deny);
|
|
let expandedAllow = expandToolGroups(allow);
|
|
|
|
// `image` is essential for multimodal workflows; always include it in sandboxed
|
|
// sessions unless explicitly denied.
|
|
if (
|
|
!expandedDeny.map((v) => v.toLowerCase()).includes("image") &&
|
|
!expandedAllow.map((v) => v.toLowerCase()).includes("image")
|
|
) {
|
|
expandedAllow = [...expandedAllow, "image"];
|
|
}
|
|
|
|
return {
|
|
allow: expandedAllow,
|
|
deny: expandedDeny,
|
|
sources: {
|
|
allow: allowSource,
|
|
deny: denySource,
|
|
},
|
|
};
|
|
}
|