feat(exec): 本地执行 Provider + 放权终端沙箱(白名单/工作目录/超时/输出截断/默认关)+ 文件沙箱;config 增 ExecSandbox(默认关)
This commit is contained in:
1 parent
88dc12466d
commit
430fc4666b
11 files changed
+1199
-20
No files matched your search
@@ -0,0 +1,129 @@
|
||||
// Package exec implements the unified local execution abstraction for the
|
||||
// Cyrene agent runtime (DevDocs/design/04-运行时.md §8 and design/02-会话与上下文模型.md §4).
|
||||
//
|
||||
// A Provider is the grant-based, sandboxed entry point through which the agent
|
||||
// may run code on its own host. This milestone ships two concrete pieces:
|
||||
//
|
||||
// - [TerminalExecutor], the default command provider, which runs allowlisted
|
||||
// commands inside a locked-down terminal sandbox (command allowlist, working
|
||||
// directory confinement, environment allowlist, timeout, output truncation).
|
||||
// - [FileSandbox], which confines file read/write/list access to a set of
|
||||
// allowed root directories.
|
||||
//
|
||||
// GUI operations and a real virtual machine are reserved for later milestones;
|
||||
// they surface only through the [Provider] interface and are not implemented
|
||||
// here. The sandbox is DISABLED BY DEFAULT: an [TerminalExecutor] created from
|
||||
// a closed config refuses every request with [ErrExecutionDisabled], so the
|
||||
// agent gains the ability to run code only after the owner explicitly enables
|
||||
// it (see config.ExecSandbox).
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultEnabled is the sandbox red line: execution is off unless the owner
|
||||
// explicitly enables it via config.ExecSandbox.Enabled.
|
||||
const DefaultEnabled = false
|
||||
|
||||
// DefaultTimeout is the per-request deadline used when ExecRequest.Timeout is
|
||||
// zero. 30s balances responsiveness with enough room for a real command.
|
||||
const DefaultTimeout = 30 * time.Second
|
||||
|
||||
// DefaultMaxOutputBytes is the combined stdout+stderr cap used when
|
||||
// ExecRequest.MaxOutputBytes is zero (1 MiB).
|
||||
const DefaultMaxOutputBytes = 1 << 20
|
||||
|
||||
// Sentinel errors returned by this package. Use errors.Is to test for them;
|
||||
// they are always wrapped with %w so the underlying cause stays available.
|
||||
var (
|
||||
// ErrExecutionDisabled is returned when the sandbox is closed (config
|
||||
// Enabled is false). This is the security red line: default off.
|
||||
ErrExecutionDisabled = errors.New("exec: sandbox disabled for this role")
|
||||
// ErrEmptyCommand is returned when ExecRequest.Command is empty.
|
||||
ErrEmptyCommand = errors.New("exec: command is empty")
|
||||
// ErrCommandNotAllowed is returned when argv[0] is not in the allowlist.
|
||||
ErrCommandNotAllowed = errors.New("exec: command not in allowlist")
|
||||
// ErrCommandInjection is returned when a command argument contains shell
|
||||
// metacharacters, control characters, or a path-traversal component.
|
||||
ErrCommandInjection = errors.New("exec: command argument rejected")
|
||||
// ErrWorkDirOutsideRoot is returned when the requested working directory
|
||||
// escapes the allowed roots.
|
||||
ErrWorkDirOutsideRoot = errors.New("exec: working directory outside allowed roots")
|
||||
// ErrEnvNotAllowed is returned when ExecRequest.Env names a variable outside
|
||||
// the configured environment allowlist.
|
||||
ErrEnvNotAllowed = errors.New("exec: environment variable not in allowlist")
|
||||
// ErrTimeout is returned when the command exceeds its deadline and is killed.
|
||||
ErrTimeout = errors.New("exec: execution timed out")
|
||||
// ErrExecFailed is returned when a command could not be started or finished.
|
||||
ErrExecFailed = errors.New("exec: process failed")
|
||||
// ErrNoAllowedRoot is returned when an operation needs a root but none is
|
||||
// configured.
|
||||
ErrNoAllowedRoot = errors.New("exec: no allowed root configured")
|
||||
// ErrEmptyPath is returned when a path to resolve is empty.
|
||||
ErrEmptyPath = errors.New("exec: path is empty")
|
||||
// ErrPathOutsideRoot is returned when a path escapes the allowed roots.
|
||||
ErrPathOutsideRoot = errors.New("exec: path outside allowed roots")
|
||||
// ErrSymlinkEscape is returned when a path resolves through a symlink that
|
||||
// points outside the allowed roots.
|
||||
ErrSymlinkEscape = errors.New("exec: path escapes root via symlink")
|
||||
// ErrNullByte is returned when a path contains a NUL byte.
|
||||
ErrNullByte = errors.New("exec: path contains NUL byte")
|
||||
// ErrNilLogger is returned by New when no logger is supplied.
|
||||
ErrNilLogger = errors.New("exec: nil logger")
|
||||
)
|
||||
|
||||
// Provider is the unified local execution abstraction. It covers command
|
||||
// execution now and, in later milestones, GUI operations and virtual machines,
|
||||
// all behind the same grant-based, sandboxed interface.
|
||||
//
|
||||
// Implementations must honor the context, which carries cancellation and
|
||||
// deadlines, and must bound their output to ExecRequest.MaxOutputBytes,
|
||||
// reporting truncation via ExecResult.Truncated.
|
||||
type Provider interface {
|
||||
// Execute runs req and returns its outcome, or an error that is one of the
|
||||
// sentinels above (or wraps one). A non-zero exit code is NOT an error: it
|
||||
// is reported in ExecResult.ExitCode.
|
||||
Execute(ctx context.Context, req ExecRequest) (*ExecResult, error)
|
||||
}
|
||||
|
||||
// ExecRequest describes a single local execution.
|
||||
type ExecRequest struct {
|
||||
// Command is the argv vector. It must be non-empty; argv[0] is the command
|
||||
// name that must appear in the allowlist.
|
||||
Command []string
|
||||
// WorkDir is the process working directory. It must resolve inside an
|
||||
// allowed root. When empty, the provider defaults to the first allowed root.
|
||||
WorkDir string
|
||||
// Env carries extra environment to set on the process. Only names in the
|
||||
// environment allowlist are accepted.
|
||||
Env map[string]string
|
||||
// Timeout bounds the run. Zero uses DefaultTimeout.
|
||||
Timeout time.Duration
|
||||
// Stdin is the optional stdin payload.
|
||||
Stdin []byte
|
||||
// MaxOutputBytes caps combined stdout+stderr. Zero uses
|
||||
// DefaultMaxOutputBytes. Output past the cap is dropped and Truncated is set.
|
||||
MaxOutputBytes int
|
||||
}
|
||||
|
||||
// ExecResult is the outcome of an execution.
|
||||
type ExecResult struct {
|
||||
// Stdout is the captured standard output (truncated to MaxOutputBytes).
|
||||
Stdout string
|
||||
// Stderr is the captured standard error (truncated to MaxOutputBytes).
|
||||
Stderr string
|
||||
// ExitCode is the process exit code. -1 when terminated by a signal.
|
||||
ExitCode int
|
||||
// Truncated reports whether output was dropped because MaxOutputBytes was
|
||||
// reached.
|
||||
Truncated bool
|
||||
// StartedAt is when the process started.
|
||||
StartedAt time.Time
|
||||
// EndedAt is when the process finished (or was killed).
|
||||
EndedAt time.Time
|
||||
// PID is the process identifier assigned by the OS.
|
||||
PID int
|
||||
}
|
||||
Reference in new issue
Block a user