mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 17:57:54 -10:00
* feat(sandbox): unify command execution and enforcement * test(sandbox): make platform contracts portable * fix(sandbox): close execution lifecycle races Bound retained interactive output, preserve interruption state, and start workspace observation before process launch. Protect active runtime roots with cross-platform leases, capture transient protected-path creation events, and restore process attributes after PTY fallback. * fix(sandbox): restore strict macOS isolation Remove Seatbelt host-local network allowances after native validation showed localhost filters can reach host interfaces. Track process reap completion so retained sessions cannot signal a stale numeric PID.
249 lines
8.1 KiB
Go
249 lines
8.1 KiB
Go
// Package execution defines the platform-neutral command execution contract.
|
|
// Frontends and tools use these types without depending on native sandbox or
|
|
// process-management details.
|
|
package execution
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const PolicyVersion = 1
|
|
|
|
type Origin string
|
|
|
|
const (
|
|
OriginInteractiveCommand Origin = "interactive_command"
|
|
OriginHook Origin = "hook"
|
|
OriginPlugin Origin = "plugin"
|
|
OriginMCPServer Origin = "mcp_server"
|
|
OriginBackgroundTask Origin = "background_task"
|
|
)
|
|
|
|
type Mode string
|
|
|
|
const (
|
|
ModeCaptured Mode = "captured"
|
|
ModeInteractive Mode = "interactive"
|
|
ModeDurable Mode = "durable"
|
|
)
|
|
|
|
type Command struct {
|
|
Name string `json:"name"`
|
|
Args []string `json:"args,omitempty"`
|
|
Env []string `json:"env,omitempty"`
|
|
}
|
|
|
|
type CapabilityKind string
|
|
|
|
const (
|
|
CapabilityRead CapabilityKind = "filesystem_read"
|
|
CapabilityWorkspaceWrite CapabilityKind = "workspace_write"
|
|
CapabilityProtectedMetadata CapabilityKind = "protected_metadata"
|
|
CapabilityExternalNetwork CapabilityKind = "external_network"
|
|
CapabilityIsolatedLoopback CapabilityKind = "isolated_loopback"
|
|
CapabilityHostVisibleBinding CapabilityKind = "host_visible_binding"
|
|
CapabilityProcessSpawn CapabilityKind = "process_spawn"
|
|
CapabilityProcessControl CapabilityKind = "process_control"
|
|
CapabilityEnvironment CapabilityKind = "environment"
|
|
CapabilityUnrestricted CapabilityKind = "unrestricted"
|
|
)
|
|
|
|
type Capability struct {
|
|
Kind CapabilityKind `json:"kind"`
|
|
Scope string `json:"scope,omitempty"`
|
|
}
|
|
|
|
type ApprovalContext struct {
|
|
SessionID string `json:"sessionId,omitempty"`
|
|
PolicyVersion int `json:"policyVersion"`
|
|
}
|
|
|
|
type Request struct {
|
|
Origin Origin `json:"origin"`
|
|
Mode Mode `json:"mode"`
|
|
Command Command `json:"command"`
|
|
WorkingDirectory string `json:"workingDirectory"`
|
|
WorkspaceRoots []string `json:"workspaceRoots"`
|
|
Capabilities []Capability `json:"capabilities,omitempty"`
|
|
Approval ApprovalContext `json:"approval"`
|
|
}
|
|
|
|
func (request Request) Validate() error {
|
|
if !validOrigin(request.Origin) {
|
|
return errors.New("execution request requires a valid origin")
|
|
}
|
|
if request.Mode != ModeCaptured && request.Mode != ModeInteractive && request.Mode != ModeDurable {
|
|
return errors.New("execution request requires a valid mode")
|
|
}
|
|
if strings.TrimSpace(request.Command.Name) == "" {
|
|
return errors.New("execution request requires a command")
|
|
}
|
|
if strings.TrimSpace(request.WorkingDirectory) == "" {
|
|
return errors.New("execution request requires a working directory")
|
|
}
|
|
if len(request.WorkspaceRoots) == 0 {
|
|
return errors.New("execution request requires at least one workspace root")
|
|
}
|
|
for _, root := range request.WorkspaceRoots {
|
|
if strings.TrimSpace(root) == "" {
|
|
return errors.New("execution request contains an empty workspace root")
|
|
}
|
|
}
|
|
for _, capability := range request.Capabilities {
|
|
if !validCapabilityKind(capability.Kind) {
|
|
return fmt.Errorf("execution request contains invalid capability %q", capability.Kind)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type State string
|
|
|
|
const (
|
|
StateRequested State = "requested"
|
|
StateEvaluated State = "evaluated"
|
|
StateAwaitingApproval State = "awaiting_approval"
|
|
StatePrepared State = "prepared"
|
|
StateRunning State = "running"
|
|
StateRetained State = "retained"
|
|
StateCompleted State = "completed"
|
|
StateDenied State = "denied"
|
|
StateFailed State = "failed"
|
|
StateCancelled State = "cancelled"
|
|
)
|
|
|
|
type OutcomeKind string
|
|
|
|
const (
|
|
OutcomeRunning OutcomeKind = "running"
|
|
OutcomeSuccess OutcomeKind = "success"
|
|
OutcomePolicyDenied OutcomeKind = "policy_denied"
|
|
OutcomeEnforcementDenied OutcomeKind = "enforcement_denied"
|
|
OutcomeApplicationFailure OutcomeKind = "application_failure"
|
|
OutcomeSandboxSetupFailure OutcomeKind = "sandbox_setup_failure"
|
|
OutcomeExecutableNotFound OutcomeKind = "executable_not_found"
|
|
OutcomeCancelled OutcomeKind = "cancelled"
|
|
OutcomeTimedOut OutcomeKind = "timed_out"
|
|
OutcomeEnforcementDegraded OutcomeKind = "enforcement_degraded"
|
|
)
|
|
|
|
type Exit struct {
|
|
Code int `json:"code"`
|
|
Signal string `json:"signal,omitempty"`
|
|
}
|
|
|
|
type DenialSource string
|
|
|
|
const (
|
|
DenialSourceConfiguredPolicy DenialSource = "configured_policy"
|
|
DenialSourcePlatformSandbox DenialSource = "platform_sandbox"
|
|
DenialSourceApproval DenialSource = "approval"
|
|
)
|
|
|
|
type DenialNextAction string
|
|
|
|
const (
|
|
DenialNextActionNone DenialNextAction = "none"
|
|
DenialNextActionRequestApproval DenialNextAction = "request_approval"
|
|
DenialNextActionChangeCommand DenialNextAction = "change_command"
|
|
DenialNextActionEnableSandbox DenialNextAction = "enable_sandbox"
|
|
)
|
|
|
|
type Denial struct {
|
|
Capability Capability `json:"capability"`
|
|
Source DenialSource `json:"source"`
|
|
Reason string `json:"reason"`
|
|
Recoverable bool `json:"recoverable"`
|
|
NextAction DenialNextAction `json:"nextAction"`
|
|
}
|
|
|
|
type Enforcement struct {
|
|
Backend string `json:"backend,omitempty"`
|
|
Level string `json:"level,omitempty"`
|
|
Degraded bool `json:"degraded,omitempty"`
|
|
DowngradeReason string `json:"downgradeReason,omitempty"`
|
|
}
|
|
|
|
type Outcome struct {
|
|
State State `json:"state"`
|
|
Kind OutcomeKind `json:"kind"`
|
|
ProcessID string `json:"processId,omitempty"`
|
|
Exit *Exit `json:"exit,omitempty"`
|
|
Denial *Denial `json:"denial,omitempty"`
|
|
Enforcement Enforcement `json:"enforcement"`
|
|
Changes []Change `json:"changes,omitempty"`
|
|
}
|
|
|
|
// AdapterReport is the structured, machine-readable result emitted by a
|
|
// platform enforcement adapter. It is separate from stdout and stderr so
|
|
// command text cannot impersonate a policy decision.
|
|
type AdapterReport struct {
|
|
Denial *Denial `json:"denial,omitempty"`
|
|
}
|
|
|
|
func (outcome Outcome) Validate() error {
|
|
if outcome.State == "" || outcome.Kind == "" {
|
|
return errors.New("execution outcome requires state and kind")
|
|
}
|
|
switch outcome.State {
|
|
case StateRetained, StateRunning:
|
|
if outcome.Kind != OutcomeRunning || strings.TrimSpace(outcome.ProcessID) == "" {
|
|
return errors.New("running execution outcome requires a process id")
|
|
}
|
|
case StateCompleted:
|
|
if outcome.Kind != OutcomeSuccess || outcome.Exit == nil || outcome.Exit.Code != 0 {
|
|
return errors.New("completed execution outcome requires a successful exit")
|
|
}
|
|
case StateDenied:
|
|
if (outcome.Kind != OutcomePolicyDenied && outcome.Kind != OutcomeEnforcementDenied) || outcome.Denial == nil {
|
|
return errors.New("denied execution outcome requires structured denial details")
|
|
}
|
|
if err := outcome.Denial.Validate(); err != nil {
|
|
return err
|
|
}
|
|
case StateFailed:
|
|
if outcome.Kind == OutcomeRunning || outcome.Kind == OutcomeSuccess || outcome.Exit == nil {
|
|
return errors.New("failed execution outcome requires a failure kind and exit")
|
|
}
|
|
case StateCancelled:
|
|
if outcome.Kind != OutcomeCancelled {
|
|
return errors.New("cancelled execution outcome requires cancelled kind")
|
|
}
|
|
default:
|
|
return fmt.Errorf("execution outcome state %q is not terminal or externally observable", outcome.State)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (denial Denial) Validate() error {
|
|
if !validCapabilityKind(denial.Capability.Kind) {
|
|
return errors.New("execution denial requires a valid capability")
|
|
}
|
|
if denial.Source == "" || strings.TrimSpace(denial.Reason) == "" || denial.NextAction == "" {
|
|
return errors.New("execution denial requires source, reason, and next action")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validOrigin(origin Origin) bool {
|
|
switch origin {
|
|
case OriginInteractiveCommand, OriginHook, OriginPlugin, OriginMCPServer, OriginBackgroundTask:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func validCapabilityKind(kind CapabilityKind) bool {
|
|
switch kind {
|
|
case CapabilityRead, CapabilityWorkspaceWrite, CapabilityProtectedMetadata,
|
|
CapabilityExternalNetwork, CapabilityIsolatedLoopback, CapabilityHostVisibleBinding,
|
|
CapabilityProcessSpawn, CapabilityProcessControl, CapabilityEnvironment, CapabilityUnrestricted:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|