Skip to content

[Enhancement]: Code Mode Sandboxing via -toolexec #132

@spachava753

Description

@spachava753

Enhancement Category

Code mode improvements

Problem or Use Case

Code mode currently executes LLM-generated Go code with full system access. While powerful, this poses security risks:

  • Accidental damage: LLM could mistakenly delete important files (e.g., os.Remove("/important/file"))
  • Unintended network access: Code could make unexpected API calls or exfiltrate data
  • Process execution: Could run dangerous commands (exec.Command("rm", "-rf", "/"))
  • User trust: Users may hesitate to use code mode without safeguards, especially in production environments

Current alternatives (running CPE in containers or VMs) are heavy and platform-specific. We need a lightweight, cross-platform solution that:

  • Allows users to define permissions for file access, process execution, and network operations
  • Works on macOS, Linux, and Windows without additional dependencies
  • Doesn't require Docker or virtualization
  • Aligns with CPE's philosophy of being a lightweight CLI tool

Proposed Solution

Use Go's -toolexec flag to inject a compilation wrapper that rewrites LLM-generated code to include permission checks before sensitive operations.

How it works:

  1. Wrapper intercepts compilation: When go build runs, a wrapper program sits between the build command and the actual compiler
  2. AST rewriting: The wrapper parses the generated run.go file, detects calls to sensitive functions (os.ReadFile, exec.Command, http.Get, etc.), and injects permission checks
  3. Runtime enforcement: A permission checking library (injected into main.go) validates operations against user-defined rules
  4. Clear failures: Operations that violate policies fail with descriptive error messages

Example transformation:

Before (LLM-generated):

data, err := os.ReadFile("config.json")

After (rewritten by wrapper):

if err := __cpe_check_file_read("config.json"); err != nil {
    return fmt.Errorf("permission denied: %w", err)
}
data, err := os.ReadFile("config.json")

Configuration:

defaults:
  codeMode:
    enabled: true
    sandboxing:
      enabled: false  # Opt-in initially
      
      allowedPaths:
        - "/tmp/cpe-*"
        - "./data/*"
      deniedPaths:
        - "/etc/*"
        - "~/.ssh/*"
      
      allowedCommands:
        - "git"
        - "rg"
      deniedCommands:
        - "rm"
        - "sudo"
      
      allowNetwork: true
      interactive: false  # If true, prompt user for each operation

Sensitive operations to track:

  • File: os.Open, os.ReadFile, os.WriteFile, os.Remove, os.RemoveAll, etc.
  • Exec: exec.Command, exec.CommandContext
  • Network: http.Get, http.Post, net.Dial
  • Dangerous: import "unsafe", import "reflect", import "C"

Alternatives Considered

Alternative Pros Cons Verdict
Containers (Docker/Podman) Strong isolation, proven technology Requires Docker daemon, heavy, platform-specific, slow startup Too heavy for CPE's use case
VMs (Firecracker) Ultimate isolation Very heavy, Linux-only, slow startup, complex setup Overkill for code mode
Static analysis Pre-execution validation, no runtime cost Can't catch all cases (dynamic paths), high false positive rate Could complement but not replace runtime checks
ptrace/seccomp Kernel-level enforcement Linux-only, complex configuration, can break legitimate programs Platform limitation is a dealbreaker
Filesystem overlays Clean file isolation Doesn't cover network/exec, requires OS-specific features Only partial solution
-toolexec approach Lightweight, cross-platform, native Go, fine-grained control Implementation complexity, potential bypasses via unsafe/reflect ✅ Best fit for CPE

The -toolexec approach is proven in production by the xgo project, which uses the same technique for monkey patching Go functions.

Impact Scope

Everyone using CPE

While the feature would be opt-in initially, it addresses a fundamental security concern that affects all code mode users. Benefits:

  • Current users: Can use code mode more confidently, especially in production environments
  • New users: More likely to adopt code mode knowing safeguards exist
  • Enterprise users: Meets security requirements for using AI-assisted tools
  • All platforms: Works on macOS, Linux, and Windows without additional dependencies

Additional Context

Implementation phases:

  1. Phase 1 - PoC (1-2 days): Basic wrapper that detects and logs os.ReadFile calls
  2. Phase 2 - Basic permissions (3-5 days): File read/write allowlist/denylist enforcement
  3. Phase 3 - Extended coverage (1 week): Process execution, network, edge cases
  4. Phase 4 - Production ready (1-2 weeks): Tests, benchmarks, docs, feature flag

Documentation created:

  • Analysis: docs/toolexec-sandboxing-analysis.md
  • Summary: docs/toolexec-summary.md
  • Working PoC: docs/toolexec-poc/ (includes AST rewriter example)

Key advantages:
✅ Lightweight (no containers/VMs)
✅ Cross-platform (macOS, Linux, Windows)
✅ Fine-grained control (read vs write, specific paths)
✅ Native Go integration (uses -toolexec flag)
✅ Optional (can be disabled for trusted scenarios)
✅ Zero overhead (only checks sensitive operations)

Key challenges & mitigations:
⚠️ AST rewriting complexity → Comprehensive tests, incremental rollout
⚠️ Potential bypasses (unsafe/reflect) → Detect and flag/deny these imports
⚠️ Maintenance across Go versions → Version locking, regular updates
⚠️ Dynamic path validation → Hook syscall-level functions for runtime checks

Open questions:

  1. Should we support interactive mode (prompt for each sensitive operation)?
  2. Default policy: allow-by-default or deny-by-default?
  3. How to handle unsafe/reflect: ban outright or warn?
  4. Per-invocation permissions or global config?
  5. Should we provide presets (read-only mode, network-only mode)?

References:

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions