Skip to content

Experiment with limiting terminal on mac to certain folders #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

wonderwhy-er
Copy link
Owner

@wonderwhy-er wonderwhy-er commented Apr 16, 2025

Summary by CodeRabbit

  • New Features

    • Introduced sandboxed command execution on macOS, restricting file operations to specified directories for enhanced security.
    • Added configurable security features, including path-restricted file access and a blacklist of blocked system commands.
  • Documentation

    • Updated documentation to describe new security features and customization options.
  • Tests

    • Added a comprehensive test script to verify sandboxed command execution and directory access restrictions.
  • Chores

    • Added a new npm script for running sandbox-related tests.

Copy link
Contributor

coderabbitai bot commented Apr 16, 2025

Walkthrough

This update introduces a platform-aware sandboxed command execution feature, primarily targeting macOS. The configuration system is extended to support a useSandbox option, defaulting to true on macOS. A new sandbox module provides secure command execution, restricting file access to specified directories. The terminal manager now prefers sandboxed execution when enabled and available, falling back to regular execution otherwise. Documentation is updated to describe new security features, and a dedicated test script is added to verify sandbox behavior, including access restrictions and command execution outcomes.

Changes

File(s) Change Summary
README.md Added a "Security Features" section detailing path-restricted file access and blocked command mechanisms, including configuration instructions and default behaviors.
package.json Added a new npm script test:sandbox to run the sandbox test after building the project.
src/config-manager.ts Extended ServerConfig interface with a useSandbox boolean property and set its default based on the platform (true for macOS, false otherwise).
src/sandbox/index.ts Introduced platform-aware sandboxed command execution with exported functions executeSandboxedCommand and isSandboxAvailable, currently supporting macOS only.
src/sandbox/mac-sandbox.ts Added macOS-specific sandboxing logic, including dynamic sandbox profile generation and secure command execution within allowed directories. Handles temporary files, timeout detection, and error reporting.
src/terminal-manager.ts Modified executeCommand to prefer sandboxed execution when enabled and available, with fallback to regular execution. Added related imports, configuration loading, and logging.
test/sandbox-test.js Added a comprehensive test script for sandboxed command execution, verifying allowed/restricted directory access and command outcomes, with automatic cleanup and output logging.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TerminalManager
    participant ConfigManager
    participant Sandbox (macOS)
    participant Shell

    User->>TerminalManager: executeCommand(command)
    TerminalManager->>ConfigManager: getConfig()
    ConfigManager-->>TerminalManager: config (with useSandbox, allowedDirectories)
    alt Sandbox enabled and available
        TerminalManager->>Sandbox: executeSandboxedCommand(command, allowedDirectories)
        alt Success (PID != -1)
            Sandbox-->>TerminalManager: result (output, exitCode, pid)
            TerminalManager-->>User: result
        else Failure or not supported
            Sandbox-->>TerminalManager: error result (pid -1)
            TerminalManager->>Shell: spawn(command)
            Shell-->>TerminalManager: result
            TerminalManager-->>User: result
        end
    else Sandbox not enabled/available
        TerminalManager->>Shell: spawn(command)
        Shell-->>TerminalManager: result
        TerminalManager-->>User: result
    end
Loading

Possibly related PRs

  • wonderwhy-er/DesktopCommanderMCP#16: Adds configuration management for blocked commands and allowed directories, directly supporting the security and sandbox features introduced in this PR.

Poem

In a warren safe and sound,
Commands now hop, but can't rebound—
Sandboxed walls, so neat and tight,
Guard the files from rabbit's fright.
Mac burrows first, with doors well locked,
Only trusted paths are stalked.
🐇✨ Secure and spry, the code leaps high!

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 4

🧹 Nitpick comments (6)
src/sandbox/index.ts (1)

9-55: Well-implemented platform-aware command execution.

The executeSandboxedCommand function:

  • Retrieves allowed directories from configuration with a sensible fallback
  • Implements platform detection for routing to the correct implementation
  • Properly handles errors and provides meaningful error messages
  • Returns a consistent result structure regardless of platform support

I would suggest adding a comment explaining the implementation status of other platforms for future maintainers.

- // Add cases for other platforms when implemented
- // case 'linux':
- // case 'win32':
+ // Add cases for other platforms when implemented
+ // TODO: Implement sandbox support for other platforms
+ // case 'linux': // Planned for future implementation
+ // case 'win32': // Planned for future implementation
src/terminal-manager.ts (3)

25-28: Log the error when config retrieval fails.
Currently, the catch block quietly assigns a fallback without logging the underlying error. Logging helps debugging.

 try {
   config = await configManager.getConfig();
 } catch (error) {
+  console.warn('Failed to retrieve config:', error);
   config = { allowedDirectories: [os.homedir()] };
 }

30-34: Consider masking command if it may contain secrets.
Logging the command in plain text could expose credentials or sensitive data.


56-57: Consider capturing error details for analytics.
Instead of only logging the error, you could also reuse your capture function for consistency with line 180.

} catch (error) {
-  console.error('Error in sandbox execution:', error);
+  capture('sandbox_error', { error, message: 'Error in sandbox execution' });
   console.warn('Falling back to regular execution due to error');
}
test/sandbox-test.js (2)

8-22: Use caution with direct shell exec calls.
For test code, this is fine, but relying on shell-based commands can pose injection risks if user input is involved.


38-52: Improve test clarity with assertions.
Logging works, but actual assertions would be clearer in a formal test environment. For instance, check file existence with a testing framework's assert.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa860bb and 8f91fd3.

📒 Files selected for processing (7)
  • README.md (1 hunks)
  • package.json (1 hunks)
  • src/config-manager.ts (2 hunks)
  • src/sandbox/index.ts (1 hunks)
  • src/sandbox/mac-sandbox.ts (1 hunks)
  • src/terminal-manager.ts (2 hunks)
  • test/sandbox-test.js (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/sandbox/index.ts (3)
src/sandbox/mac-sandbox.ts (1)
  • executeSandboxedCommand (101-196)
src/types.ts (1)
  • CommandExecutionResult (18-22)
src/config-manager.ts (1)
  • configManager (187-187)
🪛 Biome (1.9.4)
src/sandbox/index.ts

[error] 59-60: Expected a catch clause but instead found 'export'.

Expected a catch clause here.

(parse)


[error] 65-65: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (18)
package.json (1)

35-35: Good addition of a dedicated sandbox test script.

The new test:sandbox script provides a convenient way to verify the sandbox behavior, which is important for this security-focused feature.

src/config-manager.ts (2)

11-11: Well-documented configuration option.

Clear comment explaining the purpose of the useSandbox option.


123-125: Good platform-aware default configuration.

Enabling sandbox by default on macOS (the only supported platform) is a sensible security-first approach, while maintaining compatibility with other platforms by disabling it where not supported.

README.md (1)

190-205: Excellent security documentation.

The new Security Features section clearly explains the two main security mechanisms:

  1. Path Restricted File Access - Informs users about default directory restrictions and configuration options
  2. Blocked Commands - Lists categories of potentially dangerous commands that are blocked by default

This documentation helps users understand the security model and how to configure it to their needs.

src/sandbox/index.ts (2)

1-5: Good imports and module organization.

The file correctly imports the platform-specific implementation and necessary dependencies. The organization with separate modules for platform-specific code is a good architectural decision.


57-65: Simple and effective platform detection function.

The isSandboxAvailable function correctly identifies if sandboxing is available on the current platform, which will be useful for conditional execution paths.

🧰 Tools
🪛 Biome (1.9.4)

[error] 59-60: Expected a catch clause but instead found 'export'.

Expected a catch clause here.

(parse)


[error] 65-65: expected } but instead the file ends

the file ends here

(parse)

src/terminal-manager.ts (4)

5-7: No issues with these new imports.
They cleanly introduce required dependencies for sandboxing and error capturing.


22-23: Trivial variable initialization.
This looks fine and straightforward.


48-51: Successful sandbox fallback.
This logic correctly distinguishes success from error states (PID == -1).


36-46:

❓ Verification inconclusive

Potential mismatch with sandbox function parameters.
Your call to executeSandboxedCommand(command, timeoutMs, shell) may not align with the signature in mac-sandbox.ts, which expects (command, allowedDirectories, timeoutMs). Verify that the wrapper function in ./sandbox/index.js properly forwards arguments.


🏁 Script executed:

#!/bin/bash
# Search for the definition and usage of executeSandboxedCommand across the codebase
rg -A 5 "function executeSandboxedCommand"
rg -A 5 "export function executeSandboxedCommand"

Length of output: 771


Action Required: Verify Parameter Forwarding for executeSandboxedCommand
It appears that the call in src/terminal-manager.tsexecuteSandboxedCommand(command, timeoutMs, shell)—matches the parameter order of the wrapper function exported from src/sandbox/index.ts (i.e. (command, timeoutMs, shell?)). However, the underlying implementation in src/sandbox/mac-sandbox.ts expects parameters in the order (command, allowedDirectories, timeoutMs). Please confirm that the wrapper in src/sandbox/index.ts is correctly transforming or inserting the missing allowedDirectories (possibly retrieved from configuration) when forwarding the call to the mac-specific implementation.

  • Review in src/sandbox/index.ts: Ensure that allowed directories are obtained (e.g., from config.allowedDirectories || [os.homedir()]) and passed in place of the second parameter.
  • Double-check parameter order: The current call from src/terminal-manager.ts must ultimately align with the signature expected by mac-sandbox.ts.
test/sandbox-test.js (6)

24-28: Good setup of test directories.
You properly prepare separate allowed and restricted directories before tests.


54-70: Clear negative test approach.
This successfully demonstrates restricted access.


72-80: Cross-platform note.
Using ls -la is acceptable on macOS, but note that it might behave differently on other platforms if extended for cross-platform tests.


81-89: Another negative test for restricted directory.
Well-structured scenario confirming the sandbox denies access.


90-99: Cleanup logic is comprehensive.
Removing the test directory ensures minimal clutter after test completion.


103-106: Wrap test steps in promise chain.
The code flows logically, printing final results upon completion. Looks good.

src/sandbox/mac-sandbox.ts (2)

48-67: Ensure essential system paths coverage.
Allowing /usr, /bin, etc. is often necessary, but confirm no unneeded directories are excessively whitelisted. Overly broad rules can undermine sandbox restrictions.


107-185: Cleanup of temporary files.
You remove the sandbox profile and wrapper script regardless of success. This is a good practice. However, if concurrency is expected, also handle partial cleanup or naming collisions more robustly.

Comment on lines +63 to +67
let shellToUse: string | boolean | undefined = shell;
if (!shellToUse) {
shellToUse = config.defaultShell || true;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Validate shell existence.
If config.defaultShell is invalid or missing from the user's system, this might fail. Consider verifying or gracefully handling shell errors.


🏁 Script executed:

#!/bin/bash
# Confirm references to defaultShell and check if there's logic handling invalid shells
rg -A 5 "defaultShell"

Length of output: 2683


Review Comment: Validate Shell Existence in Terminal Manager

At src/terminal-manager.ts (lines 63–67), the current approach assigns the shell via:

let shellToUse: string | boolean | undefined = shell;
if (!shellToUse) {
  shellToUse = config.defaultShell || true;
}

Currently, it directly uses config.defaultShell (or falls back to true, which relies on Node’s default behavior) without validating that the specified shell actually exists on the user's system. If config.defaultShell is invalid or missing, the subsequent process execution may fail unexpectedly.

Recommendations:

  • Validate the Shell: Incorporate a check (e.g., using fs.access or a similar method) to verify that config.defaultShell exists and is executable before assigning it to shellToUse.
  • Graceful Fallback: If the validation fails, consider falling back to a safe default shell or handling the error gracefully (e.g., logging a warning and using Node’s default shell).

Comment on lines +1 to +7
// Final test of the updated sandbox implementation
import { executeSandboxedCommand } from '../dist/sandbox/index.js';
import os from 'os';
import path from 'path';
import fs from 'fs/promises';
import { exec } from 'child_process';

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Check build output path for correctness.
Referencing ../dist/sandbox/index.js might cause issues if the dist folder is absent or if the script runs before a build. Ensure the build step precedes these tests.


🏁 Script executed:

#!/bin/bash
# Check whether ../dist/sandbox/index.js exists or is generated by the build script.
[ -f "../dist/sandbox/index.js" ] && echo "dist file found" || echo "dist file missing"

Length of output: 82


Build Output File Missing: Action Required
The build output file ../dist/sandbox/index.js was not found, indicating that the build step either wasn’t executed or is misconfigured. Please ensure that:

  • The build process runs before the tests.
  • The output path is correctly set up in your build configuration.
  • Test order is adjusted such that these tests run only after the build step completes successfully.

Comment on lines +101 to +105
export async function executeSandboxedCommand(
command: string,
allowedDirectories: string[],
timeoutMs: number = 30000
): Promise<{ output: string; exitCode: number | null; isBlocked: boolean; pid: number }> {
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Check for sandbox-exec deprecation.
sandbox-exec is rumored to be deprecated in newer macOS versions. Confirm continued support.


🌐 Web query:

Is sandbox-exec deprecated in the latest macOS versions?

💡 Result:

Yes, sandbox-exec is deprecated in the latest versions of macOS. The deprecation is explicitly indicated in the official documentation and man pages, which state: "The sandbox-exec command is DEPRECATED. Developers who wish to sandbox an app should instead adopt the App Sandbox feature" as outlined in Apple's App Sandbox Design Guide[5][3]. The command has been marked as deprecated since at least macOS 10.12, and the lower-level sandbox.h APIs are noted as "No longer supported"[1][2][7].

While sandbox-exec may still function on some versions of macOS, it is no longer maintained, and there is no supported command-line alternative provided for custom sandbox profiles in recent releases. Key points include:

  • The sandbox profile language and related APIs are private and not supported for third-party development[9].
  • Apple recommends using the App Sandbox for application sandboxing, primarily for apps distributed through the Mac App Store or with proper entitlements[5].
  • There is no documented replacement for sandbox-exec for general-purpose or ad hoc sandboxing from the command line, although advanced users sometimes rely on virtualization or third-party tools as a workaround[1][3].

In summary, sandbox-exec is deprecated and should not be used for new development or relied upon for future compatibility with macOS[5][1][3].

Citations:


Action: Update sandboxing approach due to deprecation

The web query confirms that sandbox-exec has been deprecated since macOS 10.12 and is no longer supported for future macOS releases. As the current implementation in src/sandbox/mac-sandbox.ts may rely on this deprecated functionality, please consider revising the approach.

  • In src/sandbox/mac-sandbox.ts (around lines 101–105), review whether the implementation still depends on sandbox-exec.
  • If so, evaluate alternative sandboxing strategies (e.g., adopting Apple's App Sandbox or integrating a supported virtualization/containment mechanism).
  • If continued support for legacy macOS versions is required, add clear documentation and fallback mechanisms, but plan for deprecation mitigation in future releases.

Comment on lines +13 to +31
const tempDir = path.join(os.tmpdir(), 'claude-server-sandbox');

// Ensure temp directory exists
try {
// Check if directory exists first
try {
await fs.access(tempDir);
console.log(`Temp directory exists: ${tempDir}`);
} catch {
// Directory doesn't exist, create it
console.log(`Creating temp directory: ${tempDir}`);
await fs.mkdir(tempDir, { recursive: true });
console.log(`Temp directory created: ${tempDir}`);
}
} catch (error) {
console.error('Error creating temp directory for sandbox:', error);
throw new Error(`Failed to create sandbox temp directory: ${error}`);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Concurrent usage might cause collisions.
Using a fixed temp path (sandbox-profile.sb) in one directory could overwrite profiles if multiple commands run simultaneously. Consider generating a unique file name with a random suffix.

-const profilePath = path.join(tempDir, 'sandbox-profile.sb');
+const profilePath = path.join(
+  tempDir,
+  `sandbox-profile-${Date.now()}-${Math.random().toString(36).slice(2)}.sb`
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const tempDir = path.join(os.tmpdir(), 'claude-server-sandbox');
// Ensure temp directory exists
try {
// Check if directory exists first
try {
await fs.access(tempDir);
console.log(`Temp directory exists: ${tempDir}`);
} catch {
// Directory doesn't exist, create it
console.log(`Creating temp directory: ${tempDir}`);
await fs.mkdir(tempDir, { recursive: true });
console.log(`Temp directory created: ${tempDir}`);
}
} catch (error) {
console.error('Error creating temp directory for sandbox:', error);
throw new Error(`Failed to create sandbox temp directory: ${error}`);
}
const tempDir = path.join(os.tmpdir(), 'claude-server-sandbox');
const profilePath = path.join(
tempDir,
`sandbox-profile-${Date.now()}-${Math.random().toString(36).slice(2)}.sb`
);
// Ensure temp directory exists
try {
// Check if directory exists first
try {
await fs.access(tempDir);
console.log(`Temp directory exists: ${tempDir}`);
} catch {
// Directory doesn't exist, create it
console.log(`Creating temp directory: ${tempDir}`);
await fs.mkdir(tempDir, { recursive: true });
console.log(`Temp directory created: ${tempDir}`);
}
} catch (error) {
console.error('Error creating temp directory for sandbox:', error);
throw new Error(`Failed to create sandbox temp directory: ${error}`);
}

@wonderwhy-er wonderwhy-er marked this pull request as draft April 16, 2025 10:18
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.

1 participant