mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 17:40:24 +00:00
fix: default and gate apply_patch like write
This commit is contained in:
@@ -56,12 +56,9 @@ describe("Agent-specific tool filtering", () => {
|
||||
try {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
allow: ["read", "exec"],
|
||||
allow: ["read", "write", "exec"],
|
||||
exec: {
|
||||
applyPatch: {
|
||||
enabled: true,
|
||||
...(opts.workspaceOnly === false ? { workspaceOnly: false } : {}),
|
||||
},
|
||||
applyPatch: opts.workspaceOnly === false ? { workspaceOnly: false } : {},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -188,13 +185,10 @@ describe("Agent-specific tool filtering", () => {
|
||||
expect(toolNames).not.toContain("apply_patch");
|
||||
});
|
||||
|
||||
it("should allow apply_patch when exec is allow-listed and applyPatch is enabled", () => {
|
||||
it("should allow apply_patch for OpenAI models when write is allow-listed", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
allow: ["read", "exec"],
|
||||
exec: {
|
||||
applyPatch: { enabled: true },
|
||||
},
|
||||
allow: ["read", "write", "exec"],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -213,6 +207,30 @@ describe("Agent-specific tool filtering", () => {
|
||||
expect(toolNames).toContain("apply_patch");
|
||||
});
|
||||
|
||||
it("should allow disabling apply_patch explicitly", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
allow: ["read", "write", "exec"],
|
||||
exec: {
|
||||
applyPatch: { enabled: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tools = createOpenClawCodingTools({
|
||||
config: cfg,
|
||||
sessionKey: "agent:main:main",
|
||||
workspaceDir: "/tmp/test",
|
||||
agentDir: "/tmp/agent",
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-5.2",
|
||||
});
|
||||
|
||||
const toolNames = tools.map((t) => t.name);
|
||||
expect(toolNames).toContain("exec");
|
||||
expect(toolNames).not.toContain("apply_patch");
|
||||
});
|
||||
|
||||
it("defaults apply_patch to workspace-only (blocks traversal)", async () => {
|
||||
await withApplyPatchEscapeCase({}, async ({ applyPatchTool, escapedPath, patch }) => {
|
||||
await expect(applyPatchTool.execute("tc1", { input: patch })).rejects.toThrow(
|
||||
|
||||
@@ -7,7 +7,11 @@ const defaultTools = createOpenClawCodingTools({ senderIsOwner: true });
|
||||
|
||||
describe("createOpenClawCodingTools", () => {
|
||||
it("preserves action enums in normalized schemas", () => {
|
||||
const toolNames = ["browser", "canvas", "nodes", "cron", "gateway", "message"];
|
||||
const toolNames = ["canvas", "nodes", "cron", "gateway", "message"];
|
||||
const missingNames = toolNames.filter(
|
||||
(name) => !defaultTools.some((candidate) => candidate.name === name),
|
||||
);
|
||||
expect(missingNames).toEqual([]);
|
||||
|
||||
const collectActionValues = (schema: unknown, values: Set<string>): void => {
|
||||
if (!schema || typeof schema !== "object") {
|
||||
@@ -33,7 +37,6 @@ describe("createOpenClawCodingTools", () => {
|
||||
|
||||
for (const name of toolNames) {
|
||||
const tool = defaultTools.find((candidate) => candidate.name === name);
|
||||
expect(tool).toBeDefined();
|
||||
const parameters = tool?.parameters as {
|
||||
properties?: Record<string, unknown>;
|
||||
};
|
||||
@@ -56,22 +59,34 @@ describe("createOpenClawCodingTools", () => {
|
||||
expect(defaultTools.some((tool) => tool.name === "process")).toBe(true);
|
||||
expect(defaultTools.some((tool) => tool.name === "apply_patch")).toBe(false);
|
||||
|
||||
const enabledConfig: OpenClawConfig = {
|
||||
tools: {
|
||||
exec: {
|
||||
applyPatch: { enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
const openAiTools = createOpenClawCodingTools({
|
||||
config: enabledConfig,
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-5.2",
|
||||
});
|
||||
expect(openAiTools.some((tool) => tool.name === "apply_patch")).toBe(true);
|
||||
|
||||
const codexTools = createOpenClawCodingTools({
|
||||
modelProvider: "openai-codex",
|
||||
modelId: "gpt-5.4",
|
||||
});
|
||||
expect(codexTools.some((tool) => tool.name === "apply_patch")).toBe(true);
|
||||
|
||||
const disabledConfig: OpenClawConfig = {
|
||||
tools: {
|
||||
exec: {
|
||||
applyPatch: { enabled: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
const disabledOpenAiTools = createOpenClawCodingTools({
|
||||
config: disabledConfig,
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-5.2",
|
||||
});
|
||||
expect(disabledOpenAiTools.some((tool) => tool.name === "apply_patch")).toBe(false);
|
||||
|
||||
const anthropicTools = createOpenClawCodingTools({
|
||||
config: enabledConfig,
|
||||
config: disabledConfig,
|
||||
modelProvider: "anthropic",
|
||||
modelId: "claude-opus-4-5",
|
||||
});
|
||||
@@ -80,7 +95,7 @@ describe("createOpenClawCodingTools", () => {
|
||||
const allowModelsConfig: OpenClawConfig = {
|
||||
tools: {
|
||||
exec: {
|
||||
applyPatch: { enabled: true, allowModels: ["gpt-5.2"] },
|
||||
applyPatch: { allowModels: ["gpt-5.2"] },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -30,8 +30,12 @@ describe("pi-tools.policy", () => {
|
||||
expect(isToolAllowedByPolicyName("web_search", { deny: ["web_*"] })).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps apply_patch when exec is allowlisted", () => {
|
||||
expect(isToolAllowedByPolicyName("apply_patch", { allow: ["exec"] })).toBe(true);
|
||||
it("keeps apply_patch when write is allowlisted", () => {
|
||||
expect(isToolAllowedByPolicyName("apply_patch", { allow: ["write"] })).toBe(true);
|
||||
});
|
||||
|
||||
it("blocks apply_patch when write is denylisted", () => {
|
||||
expect(isToolAllowedByPolicyName("apply_patch", { deny: ["write"] })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ describe("tools.fs.workspaceOnly", () => {
|
||||
workspaceDir: sandboxRoot,
|
||||
config: {
|
||||
tools: {
|
||||
allow: ["read", "exec"],
|
||||
exec: { applyPatch: { enabled: true } },
|
||||
allow: ["read", "write", "exec"],
|
||||
exec: { applyPatch: {} },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
});
|
||||
@@ -113,8 +113,8 @@ describe("tools.fs.workspaceOnly", () => {
|
||||
workspaceDir: sandboxRoot,
|
||||
config: {
|
||||
tools: {
|
||||
allow: ["read", "exec"],
|
||||
exec: { applyPatch: { enabled: true, workspaceOnly: false } },
|
||||
allow: ["read", "write", "exec"],
|
||||
exec: { applyPatch: { workspaceOnly: false } },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
});
|
||||
|
||||
@@ -360,7 +360,7 @@ export function createOpenClawCodingTools(options?: {
|
||||
// (tools.fs.workspaceOnly is a separate umbrella flag for read/write/edit/apply_patch.)
|
||||
const applyPatchWorkspaceOnly = workspaceOnly || applyPatchConfig?.workspaceOnly !== false;
|
||||
const applyPatchEnabled =
|
||||
!!applyPatchConfig?.enabled &&
|
||||
applyPatchConfig?.enabled !== false &&
|
||||
isOpenAIProvider(options?.modelProvider) &&
|
||||
isApplyPatchAllowedForModel({
|
||||
modelProvider: options?.modelProvider,
|
||||
|
||||
@@ -214,9 +214,7 @@ describe("FS tools with workspaceOnly=false", () => {
|
||||
config: {
|
||||
tools: {
|
||||
exec: {
|
||||
applyPatch: {
|
||||
enabled: true,
|
||||
},
|
||||
applyPatch: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -63,7 +63,7 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
|
||||
{
|
||||
id: "apply_patch",
|
||||
label: "apply_patch",
|
||||
description: "Patch files (OpenAI)",
|
||||
description: "Patch files",
|
||||
sectionId: "fs",
|
||||
profiles: ["coding"],
|
||||
},
|
||||
|
||||
@@ -16,13 +16,16 @@ function makeToolPolicyMatcher(policy: SandboxToolPolicy) {
|
||||
if (matchesAnyGlobPattern(normalized, deny)) {
|
||||
return false;
|
||||
}
|
||||
if (normalized === "apply_patch" && matchesAnyGlobPattern("write", deny)) {
|
||||
return false;
|
||||
}
|
||||
if (allow.length === 0) {
|
||||
return true;
|
||||
}
|
||||
if (matchesAnyGlobPattern(normalized, allow)) {
|
||||
return true;
|
||||
}
|
||||
if (normalized === "apply_patch" && matchesAnyGlobPattern("exec", allow)) {
|
||||
if (normalized === "apply_patch" && matchesAnyGlobPattern("write", allow)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user