Skip to content

fix(sdk): validate nested apply_patch targets - #1530

Draft
B-A-M-N wants to merge 9 commits into
CodebuffAI:mainfrom
B-A-M-N:security/1450-apply-patch-target-review-r2
Draft

B-A-M-N wants to merge 9 commits into
CodebuffAI:mainfrom
B-A-M-N:security/1450-apply-patch-target-review-r2

Conversation

@B-A-M-N

@B-A-M-N B-A-M-N commented Oct 17, 2026

Copy link
Copy Markdown

Validate nested apply_patch targets

The execution policy must check the target nested in an apply_patch call's operation.path. This follow-up resolves that path against the project root before dispatch: in-project targets are allowed, and outside-project targets are rejected before the write override runs. The regression test explicitly grants external-tool trust so it verifies target confinement after host trust has been granted.

PR branch and scope

GitHub does not support stacked pull requests across forks, so this follow-up targets upstream main; its displayed diff includes the prerequisite SDK change until #1506 is merged. The incremental compare above shows this follow-up's scope.

Verification

The complete stacked suite passed 98 tests across 9 files, including both apply_patch target tests. No typecheck diagnostic was reported in changed files.

Inference for this contribution was provided by FreeInference.org. FreeInference did not review, direct, approve, or otherwise participate in this work; all implementation, testing, and publication decisions are the contributor's own. If you find this useful, consider donating to FreeInference.org — they provide a useful and vital service.

@codebuff-team

Copy link
Copy Markdown
Contributor

The core idea is sound: apply_patch's target lives in operation.path, not a top-level path, so a naive dispatcher could let --project-only be bypassed for patch operations. The fix in run.ts's handleToolCall (resolving operation.path before authorizing fs-write) and the regression test in apply-patch-target.test.ts are both clear and correctly scoped to that gap.

The problem is everything else. This PR's diff is +1488/-13 across 8 files, but the body says the actual incremental change versus PR #1506 is only 3 files / +59/-8. The rest — permissions/execution-policy.ts (408 new lines), authorizeToolCall, guardFileSystem, classifyTool, the README section on noShell/askShell/projectOnly/readOnly, and the 300+ line subagent-execution-policy.test.ts — belongs to #1506, which is not merged here. Since this mirror only accepts standalone, portable changes (each PR is hand-ported into a private tree), a maintainer can't tell what in this diff is new versus already reviewed elsewhere, and can't port "the apply_patch fix" without also re-reviewing an entire permission subsystem it depends on.

Please either:

  1. Wait for feat(security): enforceable execution permissions for shell and filesystem access (#1450) #1506 to land and then submit only the incremental diff (the 3 files / +59/-8 you mention), or
  2. If feat(security): enforceable execution permissions for shell and filesystem access (#1450) #1506 is unlikely to merge soon, inline just the minimal pieces of execution-policy.ts that apply_patch validation actually needs, with a title and diff that match in scope.

As submitted, this isn't reviewable as the "nested apply_patch" fix the title promises.

@codebuff-team codebuff-team added bot:triaged Classified by the community triage bot pr:needs-work Right idea, not mergeable as written labels Oct 18, 2026
canonicalizePath now walks up only for genuinely-missing paths (ENOENT/ENOTDIR) and rethrows permission errors and symlink loops. Replace the root-unsafe slice with path.basename. guardFileSystem refuses strict confinement when a custom adapter lacks a compatible canonicalizer. handleToolCall resolves file-path targets again and refuses an unverifiable mutating target under --project-only. Exempt the audited CLI ask_user UI override from the external-tool gate while keeping MCP/custom name collisions external. Document that projectOnly is not an OS boundary.

Tests cover root-boundary canonicalization, fail-closed realpath errors, the adapter refusal, and ask_user under all four permission modes.
Add a top-level regression that goes through CodebuffClient.run with a restricted policy, spawns a child through the real spawn_agents handler, and proves the child's inherited requestToolCall denies shell and writes. This is the end-to-end inheritance check requested in review, beyond the handler-level unit tests.
Exercise the nested apply_patch target check in project-only mode: in-project paths dispatch, out-of-project and symlink-escape paths are rejected, and a malformed target with no path fails closed. The write override is explicitly trusted so the test isolates target confinement from host trust.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:triaged Classified by the community triage bot pr:needs-work Right idea, not mergeable as written

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants