sandbox

package
v1.18.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package sandbox – exec_direct.go implements the direct executor (IsolationNone). Runs scripts via os/exec without any sandboxing. Use only for trusted/builtin skills.

Package sandbox – exec_docker.go implements the Docker-based executor for maximum script isolation.

This executor provides:

  • Full filesystem isolation (container image)
  • Network isolation (configurable)
  • CPU and memory limits via Docker
  • Read-only workspace mount
  • Writable temp directory mount
  • Automatic sandbox image building

Package sandbox – exec_restricted.go implements the restricted executor using Linux namespaces for lightweight process isolation.

This executor provides:

  • PID namespace isolation (process can't see other processes)
  • Network namespace isolation (optional, blocks network by default)
  • Mount namespace with read-only bind mounts
  • Resource limits via setrlimit
  • Filtered environment variables

Requires Linux with user namespaces enabled (most modern distros). Falls back to DirectExecutor on non-Linux systems.

Package sandbox – policy.go implements the security policy for script execution: command allowlisting, environment variable filtering, and script content scanning.

Package sandbox – runner.go implements the main ScriptRunner that dispatches execution to the appropriate sandbox backend.

Package sandbox provides secure script execution for DevClaw.

It supports multiple isolation levels for running Python, Node.js, and Shell scripts from skills (including ClawdHub community skills):

  • none: Direct exec.Command (trusted/builtin skills only)
  • restricted: Linux namespaces + resource limits (community skills)
  • container: Docker-based full isolation (untrusted scripts)

The sandbox enforces:

  • Execution timeouts
  • Memory and CPU limits
  • Filesystem scoping (read-only workspace, writable tmpdir)
  • Environment variable filtering (blocks dangerous vars)
  • Command allowlisting
  • Output size limits

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HasCritical

func HasCritical(results []ScanResult) bool

HasCritical checks if any scan result is critical severity.

Types

type Config

type Config struct {
	// DefaultIsolation is the isolation level for skills that don't
	// specify one. Defaults to IsolationRestricted.
	DefaultIsolation IsolationLevel `yaml:"default_isolation"`

	// Timeout is the maximum execution time for a single script run.
	// Defaults to 60s.
	Timeout time.Duration `yaml:"timeout"`

	// MaxOutputBytes limits stdout+stderr capture size.
	// Defaults to 1MB.
	MaxOutputBytes int64 `yaml:"max_output_bytes"`

	// MaxMemoryMB limits memory usage (restricted/container modes).
	// Defaults to 256MB.
	MaxMemoryMB int `yaml:"max_memory_mb"`

	// MaxCPUPercent limits CPU usage as percentage (0-100).
	// Defaults to 50.
	MaxCPUPercent int `yaml:"max_cpu_percent"`

	// WorkDir is the working directory for script execution.
	// Scripts get read-only access to this directory.
	WorkDir string `yaml:"work_dir"`

	// TempDir is the writable temporary directory for scripts.
	// Each execution gets its own subdirectory.
	TempDir string `yaml:"temp_dir"`

	// Docker contains container-specific settings.
	Docker DockerConfig `yaml:"docker"`

	// AllowedEnv is a list of environment variable names that scripts
	// are allowed to access. If empty, uses the default safe list.
	AllowedEnv []string `yaml:"allowed_env"`

	// BlockedEnv is a list of environment variable names that are
	// always stripped. Takes precedence over AllowedEnv.
	BlockedEnv []string `yaml:"blocked_env"`

	// AllowNetwork controls whether scripts can make network requests.
	// Defaults to false for restricted, true for none.
	AllowNetwork *bool `yaml:"allow_network"`

	// Runtimes maps Runtime to interpreter paths.
	// Defaults: python→python3, node→node, shell→/bin/sh
	Runtimes map[Runtime]string `yaml:"runtimes"`
}

Config holds the sandbox configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that the config is valid.

type DirectExecutor

type DirectExecutor struct {
	// contains filtered or unexported fields
}

DirectExecutor runs scripts directly via exec.Command.

func NewDirectExecutor

func NewDirectExecutor(cfg Config, logger *slog.Logger) *DirectExecutor

NewDirectExecutor creates a new direct executor.

func (*DirectExecutor) Available

func (e *DirectExecutor) Available() bool

Available always returns true — direct execution is always possible.

func (*DirectExecutor) Close

func (e *DirectExecutor) Close() error

Close is a no-op for the direct executor.

func (*DirectExecutor) Execute

func (e *DirectExecutor) Execute(ctx context.Context, req *ExecRequest) (*ExecResult, error)

Execute runs the script directly.

func (*DirectExecutor) Name

func (e *DirectExecutor) Name() string

Name returns the executor name.

type DockerConfig

type DockerConfig struct {
	// Image is the Docker image to use for sandboxed execution.
	// Defaults to "devclaw-sandbox:latest".
	Image string `yaml:"image"`

	// BuildOnStart builds the sandbox image on startup if missing.
	BuildOnStart bool `yaml:"build_on_start"`

	// Network is the Docker network mode.
	// Defaults to "none" (no network access).
	Network string `yaml:"network"`

	// ExtraVolumes are additional volume mounts (host:container:mode).
	ExtraVolumes []string `yaml:"extra_volumes"`
}

DockerConfig holds Docker-specific sandbox settings.

type DockerExecutor

type DockerExecutor struct {
	// contains filtered or unexported fields
}

DockerExecutor runs scripts inside Docker containers.

func NewDockerExecutor

func NewDockerExecutor(cfg Config, logger *slog.Logger) *DockerExecutor

NewDockerExecutor creates a new Docker executor.

func (*DockerExecutor) Available

func (e *DockerExecutor) Available() bool

Available checks if Docker is installed and running.

func (*DockerExecutor) BuildImage

func (e *DockerExecutor) BuildImage(ctx context.Context, dockerfilePath string) error

BuildImage builds the sandbox Docker image.

func (*DockerExecutor) Close

func (e *DockerExecutor) Close() error

Close is a no-op for Docker executor.

func (*DockerExecutor) Execute

func (e *DockerExecutor) Execute(ctx context.Context, req *ExecRequest) (*ExecResult, error)

Execute runs the script inside a Docker container.

func (*DockerExecutor) Name

func (e *DockerExecutor) Name() string

Name returns the executor name.

type ExecRequest

type ExecRequest struct {
	// Runtime is the script interpreter to use.
	Runtime Runtime

	// Isolation overrides the default isolation level.
	// If empty, uses Config.DefaultIsolation.
	Isolation IsolationLevel

	// Script is the path to the script file to execute.
	Script string

	// Args are command-line arguments passed to the script.
	Args []string

	// Stdin provides data to the script's standard input.
	Stdin string

	// Env are additional environment variables for this execution.
	// Subject to filtering by the security policy.
	Env map[string]string

	// WorkDir overrides the working directory for this execution.
	WorkDir string

	// Timeout overrides the default timeout for this execution.
	Timeout time.Duration

	// SkillDir is the skill's base directory (replaces {baseDir}).
	SkillDir string
}

ExecRequest describes a script execution request.

type ExecResult

type ExecResult struct {
	// Stdout is the captured standard output.
	Stdout string

	// Stderr is the captured standard error.
	Stderr string

	// ExitCode is the process exit code.
	ExitCode int

	// Duration is how long the execution took.
	Duration time.Duration

	// Killed is true if the process was killed (timeout, OOM, etc.).
	Killed bool

	// KillReason explains why the process was killed.
	KillReason string

	// OutputFiles lists files created in the temp directory.
	OutputFiles []string
}

ExecResult holds the outcome of a script execution.

type Executor

type Executor interface {
	// Execute runs a script with the given request.
	Execute(ctx context.Context, req *ExecRequest) (*ExecResult, error)

	// Available reports whether this executor is usable on the system.
	Available() bool

	// Name returns the executor name for logging.
	Name() string

	// Close releases resources held by the executor.
	Close() error
}

Executor is the interface for sandbox implementations.

type IsolationLevel

type IsolationLevel string

IsolationLevel defines how strictly a script is sandboxed.

const (
	// IsolationNone runs scripts directly via exec.Command.
	// Use only for trusted/builtin skills.
	IsolationNone IsolationLevel = "none"

	// IsolationRestricted uses Linux namespaces, seccomp, and cgroups
	// for lightweight isolation without Docker.
	IsolationRestricted IsolationLevel = "restricted"

	// IsolationContainer runs scripts inside a Docker container
	// with a purpose-built sandbox image.
	IsolationContainer IsolationLevel = "container"
)

type Policy

type Policy struct {
	// contains filtered or unexported fields
}

Policy enforces security rules on script execution requests.

func NewPolicy

func NewPolicy(cfg Config) *Policy

NewPolicy creates a new Policy from the sandbox config.

func (*Policy) AddAllowedBin

func (p *Policy) AddAllowedBin(bin string)

AddAllowedBin adds a binary to the safe execution list.

func (*Policy) FilterEnv

func (p *Policy) FilterEnv(env map[string]string) map[string]string

FilterEnv filters environment variables based on the policy. Returns a new map with only allowed variables.

func (*Policy) IsBinAllowed

func (p *Policy) IsBinAllowed(bin string) bool

IsBinAllowed checks if a binary is in the allowlist.

func (*Policy) IsEnvAllowed

func (p *Policy) IsEnvAllowed(name string) bool

IsEnvAllowed checks if an environment variable is allowed.

func (*Policy) ScanScript

func (p *Policy) ScanScript(content string) []ScanResult

ScanScript analyzes script content for dangerous patterns. Returns a list of findings. Critical findings should block execution.

func (*Policy) ScanShellScript

func (p *Policy) ScanShellScript(content string) []ScanResult

ScanShellScript analyzes shell script content for dangerous patterns. Shell scripts are scanned with a separate rule set because $VAR syntax is valid in shell and should not trigger the shell-env-injection rule.

func (*Policy) Validate

func (p *Policy) Validate(req *ExecRequest) error

Validate checks whether an execution request is allowed.

type RestrictedExecutor

type RestrictedExecutor struct {
	// contains filtered or unexported fields
}

RestrictedExecutor runs scripts with Linux namespace isolation.

func NewRestrictedExecutor

func NewRestrictedExecutor(cfg Config, logger *slog.Logger) *RestrictedExecutor

NewRestrictedExecutor creates a new restricted executor.

func (*RestrictedExecutor) Available

func (e *RestrictedExecutor) Available() bool

Available checks if Linux namespaces are supported.

func (*RestrictedExecutor) Close

func (e *RestrictedExecutor) Close() error

Close is a no-op.

func (*RestrictedExecutor) Execute

func (e *RestrictedExecutor) Execute(ctx context.Context, req *ExecRequest) (*ExecResult, error)

Execute runs the script with namespace isolation.

func (*RestrictedExecutor) Name

func (e *RestrictedExecutor) Name() string

Name returns the executor name.

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner is the main script execution manager. It selects the appropriate sandbox backend based on isolation level and dispatches execution requests.

func NewRunner

func NewRunner(cfg Config, logger *slog.Logger) (*Runner, error)

NewRunner creates a new script runner with the given configuration.

func (*Runner) Close

func (r *Runner) Close() error

Close releases all executor resources.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, req *ExecRequest) (*ExecResult, error)

Run executes a script with the configured sandbox.

func (*Runner) RunNode

func (r *Runner) RunNode(ctx context.Context, script string, args []string, skillDir string) (*ExecResult, error)

RunNode is a convenience method for running a Node.js script.

func (*Runner) RunPython

func (r *Runner) RunPython(ctx context.Context, script string, args []string, skillDir string) (*ExecResult, error)

RunPython is a convenience method for running a Python script.

func (*Runner) RunShell

func (r *Runner) RunShell(ctx context.Context, script string, args []string, skillDir string) (*ExecResult, error)

RunShell is a convenience method for running a shell script.

type Runtime

type Runtime string

Runtime identifies the script interpreter.

const (
	RuntimePython Runtime = "python"
	RuntimeNode   Runtime = "node"
	RuntimeShell  Runtime = "shell"
	RuntimeBinary Runtime = "binary"
)

func DetectRuntime

func DetectRuntime(path string) Runtime

DetectRuntime guesses the runtime from a script path.

type ScanResult

type ScanResult struct {
	Rule     string
	Severity string
	Message  string
	Line     int
	Content  string
}

ScanResult reports a detected issue in script content.

type ScanRule

type ScanRule struct {
	Name     string
	Severity string // "critical", "warn"
	Pattern  *regexp.Regexp
	Message  string
}

ScanRule defines a pattern to detect in script content.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL