Skip to content

Commit 1bf1f1f

Browse files
committed
fix: harden access checks for apply_patch move operations and fix flaky test
- Add validation for destination path (movePath) in ApplyPatchTool: - Check rooignore access permissions for destination - Check write protection for destination path - Validate destination is within workspace boundaries - Fix flaky ChatView focus test on Windows by waiting for component to settle before clearing mock focus calls
1 parent c141d7e commit 1bf1f1f

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

src/core/tools/ApplyPatchTool.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,39 @@ export class ApplyPatchTool extends BaseTool<"apply_patch"> {
350350
if (change.movePath) {
351351
const moveAbsolutePath = path.resolve(task.cwd, change.movePath)
352352

353+
// Validate destination path access permissions
354+
const moveAccessAllowed = task.rooIgnoreController?.validateAccess(change.movePath)
355+
if (!moveAccessAllowed) {
356+
await task.say("rooignore_error", change.movePath)
357+
pushToolResult(formatResponse.rooIgnoreError(change.movePath))
358+
await task.diffViewProvider.reset()
359+
return
360+
}
361+
362+
// Check if destination path is write-protected
363+
const isMovePathWriteProtected = task.rooProtectedController?.isWriteProtected(change.movePath) || false
364+
if (isMovePathWriteProtected) {
365+
task.consecutiveMistakeCount++
366+
task.recordToolError("apply_patch")
367+
const errorMessage = `Cannot move file to write-protected path: ${change.movePath}`
368+
await task.say("error", errorMessage)
369+
pushToolResult(formatResponse.toolError(errorMessage))
370+
await task.diffViewProvider.reset()
371+
return
372+
}
373+
374+
// Check if destination path is outside workspace
375+
const isMoveOutsideWorkspace = isPathOutsideWorkspace(moveAbsolutePath)
376+
if (isMoveOutsideWorkspace) {
377+
task.consecutiveMistakeCount++
378+
task.recordToolError("apply_patch")
379+
const errorMessage = `Cannot move file to path outside workspace: ${change.movePath}`
380+
await task.say("error", errorMessage)
381+
pushToolResult(formatResponse.toolError(errorMessage))
382+
await task.diffViewProvider.reset()
383+
return
384+
}
385+
353386
// Save new content to the new path
354387
if (isPreventFocusDisruptionEnabled) {
355388
await task.diffViewProvider.saveDirectly(

webview-ui/src/components/chat/__tests__/ChatView.spec.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,12 @@ describe("ChatView - Focus Grabbing Tests", () => {
463463
],
464464
})
465465

466-
// Clear any initial calls
466+
// Wait for the component to fully render and settle before clearing mocks
467+
await waitFor(() => {
468+
expect(getByTestId("chat-textarea")).toBeInTheDocument()
469+
})
470+
471+
// Clear any initial calls after state has settled
467472
mockFocus.mockClear()
468473

469474
// Add follow-up question
@@ -484,7 +489,7 @@ describe("ChatView - Focus Grabbing Tests", () => {
484489
],
485490
})
486491

487-
// Wait a bit to ensure any focus operations would have occurred
492+
// Wait for state update to complete
488493
await waitFor(() => {
489494
expect(getByTestId("chat-textarea")).toBeInTheDocument()
490495
})

0 commit comments

Comments
 (0)