Skip to content

Conversation

@riatzukiza
Copy link
Owner

@riatzukiza riatzukiza commented Nov 9, 2025

Summary

  • Add optional cwd parameter to bash tool allowing users to specify working directory for commands
  • Enables running commands in specific subdirectories without needing cd commands
  • Maintains security by restricting access to within project directory
  • Includes comprehensive test coverage and updated documentation

Why this change

Users previously had to use cd commands or absolute paths to run commands in specific directories, which was cumbersome and could lead to working directory confusion. The new cwd parameter provides precise control over command execution location while maintaining the existing security model.

Changes

  • Added cwd parameter to bash tool schema with validation
  • Updated spawn execution to use validated working directory
  • Enhanced documentation with usage examples
  • Added comprehensive test coverage for all scenarios

Mirrored from sst/opencode PR anomalyco#3588

Summary by CodeRabbit

  • New Features

    • Added an optional working-directory setting to control where bash commands run, validated to stay inside the project.
  • Documentation

    • Updated usage notes and examples to show the working-directory option.
    • Added a short chat-transcript note to the docs.
  • Tests

    • Added tests covering working-directory behavior: valid, relative, default, and out-of-bounds cases.

Users can now specify a working directory for bash commands using the cwd parameter, allowing commands to run in specific subdirectories without needing cd commands. This provides more precise control over where commands execute while maintaining security by restricting access to within the project directory.
…ommand-paths

fix: resolve bash tool path validation relative to cwd
@coderabbitai
Copy link

coderabbitai bot commented Nov 9, 2025

Walkthrough

Adds an optional cwd parameter to BashTool, validates/resolves it with realpath ensuring it exists and is inside the project, uses the resolved working directory for argument path resolution and process spawn, and updates docs and tests accordingly.

Changes

Cohort / File(s) Summary
Documentation notes
docs/notes/2025.10.30.13.09.02.md
Added a short timestamped chat-transcript excerpt with two image placeholders (editorial/text-only change).
BashTool implementation
packages/opencode/src/tool/bash.ts
Adds optional cwd input (must be within project); resolves via realpath; validates existence and project-bound constraint; uses resolved workingDirectory for argument realpath checks and as spawn cwd; throws on invalid/out-of-bounds cwd.
BashTool documentation
packages/opencode/src/tool/bash.txt
Documents the optional cwd parameter, shows example using cwd="/foo/bar", and updates usage guidance mentioning cwd alongside path constraints.
BashTool tests
packages/opencode/test/tool/bash.test.ts
Adds tests covering valid cwd, out-of-project cwd rejection, relative-path cwd handling, and default behavior without cwd.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant BashTool
    participant RealpathValidator as PathValidation
    participant Spawn as ProcessSpawner

    User->>BashTool: run(command, cwd?)
    BashTool->>PathValidation: resolve & validate cwd via realpath
    alt cwd valid & inside project
        PathValidation-->>BashTool: resolved workingDirectory
        BashTool->>PathValidation: resolve argument paths relative to workingDirectory
        BashTool->>Spawn: spawn process (cwd = workingDirectory)
        Spawn-->>BashTool: exit code & output
        BashTool-->>User: return result
    else cwd missing -> default to project root
        PathValidation-->>BashTool: project root
        BashTool->>Spawn: spawn process (cwd = project root)
        Spawn-->>BashTool: result
        BashTool-->>User: return result
    else cwd invalid or outside project
        PathValidation-->>BashTool: error (out-of-bounds or not found)
        BashTool-->>User: throw/reject with error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review path validation and boundary checks in packages/opencode/src/tool/bash.ts
  • Verify spawn cwd usage and argument realpath resolution
  • Confirm tests in packages/opencode/test/tool/bash.test.ts cover edge cases

Possibly related PRs

Poem

🐰 I hop where paths are tidy and sound,

realpath guards the project ground.
With cwd set and tests in line,
Commands now run where they should shine. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a cwd parameter to the bash tool for directory control, which is the core focus of all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/cwd-arg-for-bash-tool

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 258df6c and a9b4548.

📒 Files selected for processing (2)
  • docs/notes/2025.10.30.13.09.02.md (1 hunks)
  • packages/opencode/src/tool/bash.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/notes/2025.10.30.13.09.02.md
🧰 Additional context used
🧬 Code graph analysis (1)
packages/opencode/src/tool/bash.ts (1)
packages/opencode/src/project/instance.ts (1)
  • Instance (14-57)
🔇 Additional comments (4)
packages/opencode/src/tool/bash.ts (4)

46-51: LGTM! Clear schema definition with appropriate constraints.

The optional cwd parameter is properly defined with a descriptive explanation of its purpose and constraints. The documentation clearly states it must be within the project directory and defaults to the project root.


64-83: Robust validation maintains security model.

The cwd validation logic is well-implemented:

  • Resolves paths using realpath relative to the project root, handling both absolute and relative paths correctly
  • Validates the resolved path exists (line 74-76)
  • Ensures the resolved directory stays within the project boundary via Filesystem.contains (line 78-80)
  • Handles symlinks correctly by resolving them before validation

The security model is properly maintained—no paths outside the project directory can be used as the working directory.


115-115: Correct: argument paths now resolved relative to the working directory.

This change ensures that command arguments are resolved in the same directory context as the command execution, which is the expected behavior when cwd is specified. The security check at line 121 still validates against the project root.


170-170: LGTM! Feature implementation complete.

The spawn call now uses the validated workingDirectory, enabling commands to execute in the specified directory. This is the core of the feature and is correctly implemented.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/opencode/src/tool/bash.ts (1)

60-82: Consider using filesystem APIs instead of shell command for better security.

While Bun's $ template literal provides escaping, using a shell command like realpath with user-supplied input (params.cwd) introduces potential security risks. Using native filesystem APIs would be safer and more direct.

Consider this refactor using Node.js filesystem APIs:

-    // Validate and resolve cwd parameter
     let workingDirectory = Instance.directory
     if (params.cwd) {
-      const resolvedCwd = await $`realpath ${params.cwd}`
-        .cwd(Instance.directory)
-        .quiet()
-        .nothrow()
-        .text()
-        .then((x) => x.trim())
+      const { realpathSync } = await import("fs")
+      const { resolve, isAbsolute } = await import("path")
+      
+      let resolvedCwd: string
+      try {
+        const cwdPath = isAbsolute(params.cwd) 
+          ? params.cwd 
+          : resolve(Instance.directory, params.cwd)
+        resolvedCwd = realpathSync(cwdPath)
+      } catch (error) {
+        throw new Error(
+          `Working directory does not exist or is not accessible: ${params.cwd}`
+        )
+      }
       
-      if (!resolvedCwd) {
-        throw new Error(`Invalid working directory: ${params.cwd}`)
-      }
-      
       if (!Filesystem.contains(Instance.directory, resolvedCwd)) {
         throw new Error(
           `Working directory ${resolvedCwd} is outside of project directory ${Instance.directory}`,
         )
       }
       
       workingDirectory = resolvedCwd
     }

This approach:

  • Eliminates shell command execution with user input
  • Provides clearer error messages (distinguishing between non-existent paths and permission issues)
  • Is more performant (no process spawning)
  • Maintains the same security guarantees
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 969af4d and 258df6c.

📒 Files selected for processing (4)
  • docs/notes/2025.10.30.13.09.02.md (1 hunks)
  • packages/opencode/src/tool/bash.ts (4 hunks)
  • packages/opencode/src/tool/bash.txt (1 hunks)
  • packages/opencode/test/tool/bash.test.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/opencode/src/tool/bash.ts (1)
packages/opencode/src/project/instance.ts (1)
  • Instance (14-61)
packages/opencode/test/tool/bash.test.ts (1)
packages/opencode/src/project/instance.ts (1)
  • Instance (14-61)
🔇 Additional comments (9)
packages/opencode/src/tool/bash.txt (3)

22-22: LGTM! Clear documentation of the cwd parameter.

The documentation accurately describes the parameter's constraints and default behavior.


28-28: LGTM! Appropriate integration of cwd into usage guidance.

The updated guidance correctly positions the cwd parameter as an alternative to cd commands.


32-34: LGTM! Helpful example demonstrating cwd usage.

The example clearly shows how to use the cwd parameter with a realistic command.

packages/opencode/test/tool/bash.test.ts (3)

53-69: LGTM! Valid test for cwd with absolute path.

The test correctly validates that commands execute in the specified working directory.


71-87: LGTM! Important security test for directory containment.

The test correctly validates that the tool rejects working directories outside the project boundary.


107-122: LGTM! Good test for default behavior.

The test correctly validates backward compatibility when the cwd parameter is not provided.

packages/opencode/src/tool/bash.ts (3)

46-46: LGTM! Well-documented parameter definition.

The schema correctly defines the cwd parameter as optional with a clear description of its constraints and behavior.


114-114: LGTM! Correct use of workingDirectory for path resolution.

The change correctly resolves command arguments relative to the specified working directory, maintaining proper security validation.


172-172: LGTM! Core implementation of cwd feature.

The spawned process now correctly executes in the validated working directory.

Comment on lines 1 to 5
[4:08 PM]AKTK: And now I've just started getting this too, did you find a cause/solution at all?
[4:10 PM]shuv: what was the prompt/session that triggered it? based on the token counts, it looks like it was trying to print the entire previous context back to you again
[4:13 PM]AKTK: Sent out via a subagent. It was running fine for a while, then just randomly goes 💩 and errors. Which then means it delegates the task back out and leaves the repo in an awful state (That /undo didn't fix the first time)
Image
Image
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the relevance of this file to the PR.

This file contains a chat transcript that appears unrelated to the PR's stated objective of adding a cwd parameter to the bash tool. Was this file accidentally included in this PR?

Comment on lines +89 to +105
test("cwd parameter with relative path", async () => {
await Instance.provide({
directory: projectRoot,
fn: async () => {
const result = await bash.execute(
{
command: "pwd",
cwd: path.join(projectRoot, "src"),
description: "Use absolute path for cwd",
},
ctx,
)
expect(result.metadata.exit).toBe(0)
expect(result.metadata.output).toContain("/src")
},
})
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix test naming and add coverage for actual relative paths.

The test is named "cwd parameter with relative path" but uses an absolute path (path.join(projectRoot, "src")). The description also contradicts the test name by saying "Use absolute path for cwd".

Additionally, there's no test coverage for actual relative paths like "src" or "./src".

Apply this diff to fix the test name:

-  test("cwd parameter with relative path", async () => {
+  test("cwd parameter with subdirectory path", async () => {
     await Instance.provide({
       directory: projectRoot,
       fn: async () => {
         const result = await bash.execute(
           {
             command: "pwd",
             cwd: path.join(projectRoot, "src"),
-            description: "Use absolute path for cwd",
+            description: "Use subdirectory path for cwd",
           },
           ctx,
         )

Consider adding a test for actual relative paths:

test("cwd parameter with relative path", async () => {
  await Instance.provide({
    directory: projectRoot,
    fn: async () => {
      const result = await bash.execute(
        {
          command: "pwd",
          cwd: "./src",
          description: "Use relative path for cwd",
        },
        ctx,
      )
      expect(result.metadata.exit).toBe(0)
      expect(result.metadata.output).toContain("/src")
    },
  })
})
🤖 Prompt for AI Agents
In packages/opencode/test/tool/bash.test.ts around lines 89 to 105, the test is
named "cwd parameter with relative path" but uses an absolute path and the
description says "Use absolute path for cwd"; rename this test to indicate it's
testing an absolute path (e.g., "cwd parameter with absolute path") and update
the description accordingly while keeping cwd: path.join(projectRoot, "src").
Then add a new test that actually exercises a relative cwd (e.g., cwd: "./src"
or "src") with description "Use relative path for cwd", asserting exit is 0 and
output contains "/src" to provide coverage for relative paths.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants