From 7857dfabcc6bdbbce584382f43a203b171926642 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 3 May 2026 18:12:31 +0100 Subject: [PATCH] fix: align apply_patch deny policy docs (#76795) --- CHANGELOG.md | 1 + docs/gateway/config-tools.md | 8 ++++++++ docs/tools/exec.md | 1 + src/agents/pi-tools.policy.test.ts | 4 ++-- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a0dc2b1df4..19eab32f9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Agents/tools: stop treating `tools.deny: ["write"]` as an implicit `apply_patch` deny; operators who want to block patch writes should deny `apply_patch` or `group:fs` explicitly. Fixes #76749. (#76795) Thanks @Nek-12 and @hclsys. - Gateway/update: recover an installed-but-unloaded macOS LaunchAgent after package updates, rerun Gateway health/version/channel readiness checks, and print restart, reinstall, and rollback guidance before reporting update failure. (#76790) Thanks @jonathanlindsay. - Google Meet: route stateful CLI session commands through the gateway-owned runtime so joined realtime sessions survive after the starting CLI process exits. Fixes #76344. Thanks @coltonharris-wq. - Memory/status: split builtin sqlite-vec store readiness from embedding-provider readiness in `memory status --deep` and `openclaw status`, so local vector-store failures no longer look like provider failures and provider failures no longer hide a healthy local vector store. diff --git a/docs/gateway/config-tools.md b/docs/gateway/config-tools.md index 82f2a4157b7..32b031659ad 100644 --- a/docs/gateway/config-tools.md +++ b/docs/gateway/config-tools.md @@ -54,6 +54,14 @@ Global tool allow/deny policy (deny wins). Case-insensitive, supports `*` wildca } ``` +`write` and `apply_patch` are separate tool ids. `allow: ["write"]` also enables `apply_patch` for compatible models, but `deny: ["write"]` does not deny `apply_patch`. To block all file mutation, deny `group:fs` or list each mutating tool explicitly: + +```json5 +{ + tools: { deny: ["write", "edit", "apply_patch"] }, +} +``` + ### `tools.byProvider` Further restrict tools for specific providers or models. Order: base profile → provider profile → allow/deny. diff --git a/docs/tools/exec.md b/docs/tools/exec.md index ef6985901e7..24a9c9a452a 100644 --- a/docs/tools/exec.md +++ b/docs/tools/exec.md @@ -264,6 +264,7 @@ Notes: - Only available for OpenAI/OpenAI Codex models. - Tool policy still applies; `allow: ["write"]` implicitly allows `apply_patch`. +- `deny: ["write"]` does not deny `apply_patch`; deny `apply_patch` explicitly or use `deny: ["group:fs"]` when patch writes should also be blocked. - Config lives under `tools.exec.applyPatch`. - `tools.exec.applyPatch.enabled` defaults to `true`; set it to `false` to disable the tool for OpenAI models. - `tools.exec.applyPatch.workspaceOnly` defaults to `true` (workspace-contained). Set it to `false` only if you intentionally want `apply_patch` to write/delete outside the workspace directory. diff --git a/src/agents/pi-tools.policy.test.ts b/src/agents/pi-tools.policy.test.ts index ef107d4e810..0ec3a496c93 100644 --- a/src/agents/pi-tools.policy.test.ts +++ b/src/agents/pi-tools.policy.test.ts @@ -37,8 +37,8 @@ describe("pi-tools.policy", () => { expect(isToolAllowedByPolicyName("apply_patch", { allow: ["write"] })).toBe(true); }); - it("blocks apply_patch when write is denylisted", () => { - expect(isToolAllowedByPolicyName("apply_patch", { deny: ["write"] })).toBe(false); + it("keeps apply_patch when write is denylisted", () => { + expect(isToolAllowedByPolicyName("apply_patch", { deny: ["write"] })).toBe(true); }); });