exec

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2026 License: Apache-2.0 Imports: 32 Imported by: 0

Documentation

Index

Constants

View Source
const (
	Deny  = profile.Deny
	Gated = profile.Gated
	Allow = profile.Allow

	IsolatedHome = profile.IsolatedHome
	RealHome     = profile.RealHome

	Sandboxed  = profile.Sandboxed
	Unconfined = profile.Unconfined

	LevelNone     = profile.LevelNone
	LevelDegraded = profile.LevelDegraded
	LevelFull     = profile.LevelFull

	GuaranteeProcessBoundary = profile.GuaranteeProcessBoundary
	GuaranteeWriteBoundary   = profile.GuaranteeWriteBoundary
	GuaranteeReadBoundary    = profile.GuaranteeReadBoundary
	GuaranteeEnvScrub        = profile.GuaranteeEnvScrub
	GuaranteeNetworkBoundary = profile.GuaranteeNetworkBoundary
	GuaranteeAddressNetwork  = profile.GuaranteeAddressNetwork
	GuaranteeResourceLimits  = profile.GuaranteeResourceLimits
	GuaranteeTargetNetwork   = profile.GuaranteeTargetNetwork
)
View Source
const (
	GrantClassCommandStart        = "command.start.v1"
	GrantClassNetworkProxyTarget  = "network.proxy-target.v1"
	GrantClassNetworkBroad        = "network.broad.v1"
	GrantClassFilesystemPathRead  = "filesystem.path.read.v1"
	GrantClassFilesystemTreeRead  = "filesystem.tree.read.v1"
	GrantClassFilesystemHostRead  = "filesystem.host.read.v1"
	GrantClassFilesystemPathWrite = "filesystem.path.write.v1"
	GrantClassFilesystemTreeWrite = "filesystem.tree.write.v1"
	GrantClassFilesystemHostWrite = "filesystem.host.write.v1"
)

Grant enforcement-class identifiers. These string VALUES are the shipped wire/enforcement contract between the sandbox (which authenticates and enforces grants) and its producers (the harness/tools permission layer, which mints them). They are the single source of truth within sandbox: validateGrantClass and the executor switch on these constants rather than bare literals, and grant_class_test.go pins each value so a rename that silently changes a value fails here. The tools module independently pins the same literals (it must not depend on sandbox), so drift on either side is caught by a value test on that side.

Variables

View Source
var (
	ErrSandboxUnavailable  = enforce.ErrUnavailable
	ErrNetworkTargetDenied = network.ErrTargetDenied
	ErrEgressRouteDenied   = network.ErrRouteDenied
)

ErrSandboxUnavailable and ErrNetworkTargetDenied are re-exported so this package's tests match the same values the facade exposes.

View Source
var (
	ErrExecutorLimit     = errors.New("sandbox: executor set limit reached")
	ErrExecutorSetClosed = errors.New("sandbox: executor set closed")
)
View Source
var (
	ErrGrantMalformed             = policy.ErrMalformed
	ErrGrantBadMAC                = errors.New("sandbox: grant token MAC mismatch")
	ErrGrantExpired               = errors.New("sandbox: grant token expired")
	ErrGrantWrongCommand          = errors.New("sandbox: grant token command mismatch")
	ErrGrantWrongExecution        = errors.New("sandbox: grant token execution mismatch")
	ErrGrantWrongWorkingDirectory = errors.New("sandbox: grant token working directory mismatch")
	ErrGrantProfileMismatch       = errors.New("sandbox: grant token profile mismatch")
	ErrGrantGuaranteeMismatch     = errors.New("sandbox: grant token guarantee mismatch")
	ErrGrantRouteMismatch         = errors.New("sandbox: grant token route mismatch")
	ErrGrantTargetChanged         = policy.ErrTargetChanged
	ErrGrantReplay                = errors.New("sandbox: grant token replay")
	ErrGrantRequired              = errors.New("sandbox: approval grant required")
	ErrGrantDenied                = errors.New("sandbox: capability denied")
	ErrGrantUnsupported           = policy.ErrUnsupportedClass
	// ErrExecutorClosed is defined by the egress layer and re-used verbatim here
	// so that a refusal raised inside the proxy and one raised by the executor
	// are the same value under errors.Is.
	ErrExecutorClosed = network.ErrClosed
)
View Source
var (
	// ErrOutputLimit reports that a bounded direct-argv run exceeded its
	// caller-supplied combined stdout/stderr limit and was terminated.
	ErrOutputLimit = errors.New("sandbox: process output limit exceeded")

	// ErrProcessClosed reports that a PreparedProcess or Process was already
	// closed and no further preparation, start, or stream operation may
	// proceed through it.
	ErrProcessClosed = errors.New("sandbox: process closed")

	// ErrProcessAlreadyStarted reports that a PreparedProcess's single-use
	// Start was already consumed by an earlier call.
	ErrProcessAlreadyStarted = errors.New("sandbox: process already started")

	// ErrProcessTTYUnsupported reports a TTY-backed process request that
	// cannot be honored, for either of two independent reasons: (1) a
	// prepare-time, platform-wide reason — a platform/build with no real PTY
	// primitive wired at all (ttySupported is false — see terminal_other.go);
	// Unix (terminal_unix.go) and Windows (terminal_windows.go, ConPTY) both
	// admit ProcessOptions.TTY == true at PrepareProcess and spawn a real
	// terminal instead of returning this error — or (2) a Start-time,
	// backend-specific reason — PrepareProcess's ttySupported check cannot
	// know which of Start's two dispatch branches (startConfined vs
	// startBackendOwned, process.go) a given preparation will resolve to, so
	// a backend that compiles a Launch-carrying spec but has no terminal
	// wiring of its own (today: the Windows elevated/broker backend) rejects
	// a TTY request here instead, at Start (startBackendOwned's own
	// top-of-function guard) — never silently downgrading to a plain
	// pipe-backed Process. This never silently downgrades a TTY request to
	// pipes on any platform or backend.
	ErrProcessTTYUnsupported = errors.New("sandbox: process TTY mode is not yet supported")

	// ErrProcessConPTYUnavailable reports that this Windows host does not
	// export the CreatePseudoConsole API a ConPTY-backed TTY request needs
	// (Windows 10 1809+ / Windows Server 2019+ only). Unlike
	// ErrProcessTTYUnsupported — decided once, at compile time, from the
	// ttySupported constant, before any reservation is made (PrepareProcess)
	// — this is a runtime capability check specific to the actual host:
	// Windows generically supports ConPTY (ttySupported is true there), but
	// a specific old host may not, so this surfaces later, from Start, once
	// terminal_windows.go's openTerminal actually probes for the API. See
	// probeConPTYAvailable (terminal_windows.go) for the probe itself.
	// Named ErrProcessConPTYUnavailable, not ErrConPTYUnavailable, to match
	// this file's own ErrProcess* naming convention: it is raised through
	// the same Process/PreparedProcess surface as every other sentinel here.
	ErrProcessConPTYUnavailable = errors.New("sandbox: ConPTY (pseudo console) is not available on this host")

	// ErrProcessStdinClosed reports a write attempted after the process's
	// stdin was closed (explicitly or by a prior EOF).
	ErrProcessStdinClosed = errors.New("sandbox: process stdin closed")

	// ErrProcessSignalUnsupported reports that a not-yet-terminal Process has
	// no real signal-delivery implementation wired in yet (see
	// processSignalTarget in process.go): Signal fails closed with this
	// error rather than silently succeeding. A later microtask (the Unix
	// lifetime shim, the Windows Job signal mapping) wires production
	// Process values to a real implementation; a Process already confirmed
	// terminal never reaches this error regardless.
	ErrProcessSignalUnsupported = errors.New("sandbox: process signal delivery is not yet supported")

	// ErrProcessResizeUnsupported reports that a not-yet-terminal Process has
	// no terminal-resize implementation wired (see processTerminalTarget,
	// terminal.go): a pipe-mode Process, or a TTY request this platform
	// cannot yet honor at all (PrepareProcess already fails those closed
	// with ErrProcessTTYUnsupported before a Process ever exists, so this
	// sentinel is unreachable through that path). Mirrors
	// ErrProcessSignalUnsupported's fail-closed contract exactly.
	ErrProcessResizeUnsupported = errors.New("sandbox: process resize is not yet supported")
)

Process/PreparedProcess sentinels. Each is the single value raised anywhere this pipe-backed API rejects a call, so errors.Is answers the same regardless of which method refused. They join the executor-level and grant-level sentinels already declared in executor_set.go and grant.go; keeping them in their own file mirrors how this microtask's public types live in their own process.go rather than growing executor.go.

View Source
var ErrInvalidProfile = profile.ErrInvalidProfile

ErrInvalidProfile is re-exported so a profile rejection raised here is the same value consumers match at the facade.

Functions

This section is empty.

Types

type Access

type Access = profile.Access

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type CompileReport

type CompileReport = profile.CompileReport

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type ConPTYAttribute

type ConPTYAttribute struct {
	PseudoConsoleHandle uintptr
}

ConPTYAttribute is the PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE attribute a ConPTY-backed CreateProcess call must attach, sourced from a real CreatePseudoConsole result. See ConPTYPipes's own doc comment for why this is a plain uintptr rather than a Windows-only handle type.

type ConPTYBrokerCredentials

type ConPTYBrokerCredentials struct {
	TokenHandle uintptr
	Desktop     string
}

ConPTYBrokerCredentials is the restricted primary token and private desktop name the elevated broker path already threads through elevatedRunnerLaunch.Token/Desktop (internal/windows/elevated_runner_launcher_windows.go) — reproduced here as this package's own neutral fields rather than an import of that Windows-only package or its win.Token type (see this file's own top-of-file doc comment). The zero value means "no broker": a ConPTY- backed launch under the restricted (non-elevated) path, process_tree_windows.go's own processTree, never receives a broker token or desktop at all, and must stay just as valid a plan as the elevated, broker-backed case.

type ConPTYJobAssignment

type ConPTYJobAssignment struct {
	JobHandle uintptr
}

ConPTYJobAssignment names the Job Object a ConPTY-backed child must be assigned to before it is ever resumed — the identical Job process_tree_windows.go's own newProcessTree already creates and configures via internal/windows.NewJob before any child exists. This plan never creates, configures, or closes that Job itself; it only records which one ConPTYStepAssignJob targets.

type ConPTYLaunchPlan

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

ConPTYLaunchPlan is the immutable, validated description of one ConPTY- backed Windows launch: the pipe pair, the pseudo-console attribute, the Job it must be assigned to, its optional broker credentials, and the fixed order those pieces are consumed in. NewConPTYLaunchPlan is the only production constructor and never returns a plan that fails its own Validate; every field is unexported and every accessor below returns a value or a defensive copy, so a caller holding a *ConPTYLaunchPlan can never mutate it into something Validate would reject.

func NewConPTYLaunchPlan

func NewConPTYLaunchPlan(pipes ConPTYPipes, attribute ConPTYAttribute, job ConPTYJobAssignment, broker ConPTYBrokerCredentials) (*ConPTYLaunchPlan, error)

NewConPTYLaunchPlan validates pipes, attribute, and job as non-zero, validates broker's all-or-nothing shape, and builds the one canonical step order (allocate pipes, create the pseudo-console attribute, create suspended, assign the Job, resume) before returning. It always calls Validate on the result before returning it, so a caller never observes a plan this constructor itself would consider invalid.

func (*ConPTYLaunchPlan) Attribute

func (plan *ConPTYLaunchPlan) Attribute() ConPTYAttribute

Attribute returns the plan's pseudo-console attribute.

func (*ConPTYLaunchPlan) Broker

Broker returns the plan's broker credentials — the zero value when this plan targets the restricted (non-broker) path.

func (*ConPTYLaunchPlan) Job

Job returns the plan's Job assignment target.

func (*ConPTYLaunchPlan) Pipes

func (plan *ConPTYLaunchPlan) Pipes() ConPTYPipes

Pipes returns the plan's pipe endpoints.

func (*ConPTYLaunchPlan) Steps

func (plan *ConPTYLaunchPlan) Steps() []ConPTYLaunchStep

Steps returns a defensive copy of the plan's launch order: mutating the returned slice can never reach back into the plan's own immutable state, and mutating the plan's own backing array (impossible from outside this package, since steps is unexported) is likewise never observable through a previously returned copy.

func (*ConPTYLaunchPlan) Validate

func (plan *ConPTYLaunchPlan) Validate() error

Validate reports whether this plan's fields and step order are both internally consistent: pipes, attribute, and job must each be non-zero; broker must be all-or-nothing; and steps must satisfy validateConPTYLaunchStepOrder. It is exported so a later Windows-only consumer (Task 22B) can re-check a plan it did not itself construct — e.g. one received across a boundary — and so this package's own tests can prove the type's own logic rejects a bad order directly, rather than relying only on NewConPTYLaunchPlan ever calling it.

type ConPTYLaunchStep

type ConPTYLaunchStep uint8

ConPTYLaunchStep identifies one distinguishable stage of a ConPTY-backed Windows process launch. Values name the same operations this package's existing Windows paths already perform — AllocatePipes and CreatePseudoConsole are new to ConPTY, but CreateSuspended, AssignJob, and Resume are the exact vocabulary process_tree_windows.go and internal/windows/elevated_runner_launcher_windows.go already use (CreateSuspended, Job.Assign/api.Assign, ntResumeProcess/api.Resume).

const (

	// ConPTYStepAllocatePipes creates the two pipe pairs a pseudo console
	// needs — one endpoint of each handed to CreatePseudoConsole
	// (ConPTYStepCreatePseudoConsole), the other retained by the parent for
	// the process's whole lifetime, exactly like openProcessTerminal
	// (terminal_unix.go) retains the PTY master while handing the slave to
	// the child.
	ConPTYStepAllocatePipes ConPTYLaunchStep

	// ConPTYStepCreatePseudoConsole turns the pipe endpoints
	// ConPTYStepAllocatePipes produced into the
	// PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE attribute (ConPTYAttribute, below)
	// the eventual CreateProcess call attaches. Must run after pipes exist
	// and before the suspended create that consumes it.
	ConPTYStepCreatePseudoConsole

	// ConPTYStepCreateSuspended creates the child process with
	// CREATE_SUSPENDED, exactly like the restricted path's own
	// newProcessTree (process_tree_windows.go, which ORs
	// windows.CREATE_SUSPENDED into cmd.SysProcAttr.CreationFlags) and the
	// elevated path's own CreateSuspended
	// (internal/windows/elevated_runner_native_windows.go, which passes
	// win.CREATE_SUSPENDED to CreateProcessAsUser). Must run after
	// ConPTYStepCreatePseudoConsole, since a ConPTY-backed create attaches
	// that attribute at creation time, and before ConPTYStepAssignJob: a
	// process is never meaningfully Job-assignable once it may already be
	// running unconfined code.
	ConPTYStepCreateSuspended

	// ConPTYStepAssignJob assigns the still-suspended child to its Job
	// Object, exactly like processTree.start's tree.job.Assign call
	// (process_tree_windows.go) and elevatedRunnerLauncher.Launch's
	// api.Assign call
	// (internal/windows/elevated_runner_launcher_windows.go). Must run
	// before ConPTYStepResume — this is the one invariant
	// TestConPTYLaunchPlanOrdersJobBeforeResume exists to prove is enforced
	// by this type's own logic, not merely documented here.
	ConPTYStepAssignJob

	// ConPTYStepResume resumes the child's main thread, exactly like
	// processTree.start's ntResumeProcess.Call (process_tree_windows.go) and
	// elevatedRunnerLauncher.Launch's api.Resume call
	// (internal/windows/elevated_runner_launcher_windows.go). A process must
	// never run a single instruction of its own code before it is already
	// contained by its Job — that is the entire reason this step is ordered
	// last.
	ConPTYStepResume
)

func (ConPTYLaunchStep) String

func (step ConPTYLaunchStep) String() string

String supports readable failure messages from validateConPTYLaunchStepOrder and %v formatting in tests, never anything platform-specific.

type ConPTYPipes

type ConPTYPipes struct {
	// ConsoleInputRead is the pipe's read endpoint, handed to
	// CreatePseudoConsole as its input source; the pseudo console reads
	// from it. Closed by the parent once ConPTYStepCreatePseudoConsole has
	// handed it off, exactly like openProcessTerminal's closeSlave
	// (terminal_unix.go) drops the parent's own slave reference once the
	// child holds its inherited copy.
	ConsoleInputRead uintptr
	// ConsoleInputWrite is the pipe's write endpoint, retained by the parent
	// for the process's whole lifetime — the eventual terminalStdin
	// (terminal.go) write target, mirroring terminalMaster's role on Unix.
	ConsoleInputWrite uintptr
	// ConsoleOutputRead is the pipe's read endpoint, retained by the parent
	// for the process's whole lifetime — the eventual pumpPTYOutput
	// (process.go) drain source, mirroring terminalMaster's role on Unix.
	ConsoleOutputRead uintptr
	// ConsoleOutputWrite is the pipe's write endpoint, handed to
	// CreatePseudoConsole as its output target; the pseudo console writes
	// rendered output to it. Closed by the parent once handed off, exactly
	// like ConsoleInputRead above.
	ConsoleOutputWrite uintptr
}

ConPTYPipes holds the two pipe pairs a ConPTY-backed launch allocates before creating the pseudo console: one for the console's input (the parent writes; the pseudo console reads), one for its output (the pseudo console writes; the parent reads). Each field is the OS pipe handle as a plain uintptr — exactly the representation the standard library's own os.Process.WithHandle callback already uses for a live process handle (func(handle uintptr)) — never a golang.org/x/sys/windows.Handle or any internal/windows package type, so this file never imports a Windows-only package (see this file's own top-of-file doc comment). Task 22B (terminal_windows.go — not part of this file) is what will populate these from a real CreatePipe call and drive the actual ReadFile/WriteFile traffic through them; this type only records the shape and non-zero-ness of what a real launch needs.

type EgressRoute

type EgressRoute = network.Route

Egress vocabulary, aliased for the same reason as the profile vocabulary above.

func NewDirectEgressRoute

func NewDirectEgressRoute() (EgressRoute, error)

Route constructors, aliased for this package's tests.

func NewUpstreamEgressRoute

func NewUpstreamEgressRoute(rawURL string, trustedAddressGuarantee bool) (EgressRoute, error)

type Executor

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

Executor compiles a policy.Effective once via the platform backend and then runs commands under the resulting reusable spawn transform (SPEC §6, §7). It holds the compiled policy, the chosen backend, its enforce.Spec, the compilation report, the achieved level and guarantee bits, and the assembled child environment — everything a spawn needs, precomputed at construction.

func (*Executor) GrantVersion

func (e *Executor) GrantVersion() uint16

GrantVersion reports the scalar grant ABI implemented by this executor.

func (*Executor) GuaranteeBits

func (e *Executor) GuaranteeBits() uint64

GuaranteeBits returns the same guarantees as the seam-facing bitmask so a consumer can probe interface{ GuaranteeBits() uint64 } without importing this package.

func (*Executor) Guarantees

func (e *Executor) Guarantees() Guarantees

Guarantees returns the rich per-property statement of what the enforce.Backend actually enforced. Each field is fail-closed.

func (*Executor) IssueGrant

func (e *Executor) IssueGrant(ctx context.Context, executionID, command, cwd, kind, scope, class, target string, expiryUnixMilli int64) (string, error)

IssueGrant mints a single-use, executor-bound capability grant.

func (*Executor) Level

func (e *Executor) Level() uint8

Level reports the achieved (probed + compiled, not requested) isolation level (SPEC §6). The zero value LevelNone is fail-closed.

func (*Executor) PrepareProcess

func (e *Executor) PrepareProcess(ctx context.Context, opts ProcessOptions) (*PreparedProcess, error)

PrepareProcess validates opts and performs the complete grant-redemption and resource-reservation transaction for this process without spawning anything: the executor's command authority is checked (Deny always refuses; Gated refuses without at least one supplied grant), any supplied grants are cryptographically verified and consumed exactly like Executor.RunCommandWithGrants, retained filesystem path handles are borrowed and re-acquired, a route/proxy credential is authorized when a network grant requires one, and the confined backend spec is compiled. A failure at any point releases every partial reservation exactly once and leaves nothing consumed that could later replay. Supplying no grants resolves the same plain (non-grant) access RunCommand/RunArgv already use.

func (*Executor) Report

func (e *Executor) Report() CompileReport

Report returns the per-feature compilation outcomes for the chosen enforce.Backend what was enforced, narrowed, or left unenforced.

func (*Executor) RunArgv

func (e *Executor) RunArgv(ctx context.Context, dir string, argv []string) ([]byte, int, error)

RunArgv runs a direct argv in dir under the compiled policy, with no shell interposed — for tools that already build argv safely. Same exit-code/error convention as RunCommand: key on err, not the numeric code, to detect a process that did not complete normally (spawn failure, signal kill, or context cancellation all report code -1).

func (*Executor) RunArgvLimited

func (e *Executor) RunArgvLimited(ctx context.Context, dir string, argv []string, maxOutputBytes int64) ([]byte, int, error)

RunArgvLimited runs direct argv under the compiled policy while enforcing a combined stdout/stderr byte limit during capture. Once the limit is reached the process is killed and ErrOutputLimit is returned; output beyond the limit is discarded instead of retained in memory.

func (*Executor) RunCommand

func (e *Executor) RunCommand(ctx context.Context, dir, command string) ([]byte, int, error)

RunCommand runs a shell command string in dir under the compiled policy (SPEC It wraps the command via the backend's enforce.Spec, sets the working directory and the assembled environment, applies any spawn attributes, and runs to completion capturing combined stdout+stderr.

Convention: if the process RAN to a normal exit — even a non-zero one — the return is (output, exitCode, nil) carrying the real exit code. A non-nil error signals that the process did NOT complete normally: a spawn/setup failure (missing dir, binary not found), a signal kill (e.g. SIGKILL), or a context timeout/cancel. Callers MUST key on err (not the numeric code) to detect "didn't run / was killed": the exit code is -1 in every such case, so -1 does not uniquely mean "didn't spawn" — it also arises from signal death and context cancellation.

func (*Executor) RunCommandWithGrants

func (e *Executor) RunCommandWithGrants(ctx context.Context, executionID, dir, command string, grants []string) ([]byte, int, error)

RunCommandWithGrants verifies all grants, compiles their least-authority deltas for this spawn, atomically consumes them, and then starts the command.

type ExecutorSet

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

ExecutorSet owns per-key executors, their grant keys, and isolated HOME dirs.

func NewExecutorSet

func NewExecutorSet(prof *Profile, options ...ExecutorSetOption) (*ExecutorSet, error)

NewExecutorSet creates one owner-only child beneath a required scratch root.

func (*ExecutorSet) Close

func (set *ExecutorSet) Close() error

Close revokes all executor grant keys and removes only the set-owned child.

func (*ExecutorSet) For

func (set *ExecutorSet) For(key string) (*Executor, error)

For memoizes an executor with a distinct grant key and child HOME per key.

type ExecutorSetOption

type ExecutorSetOption func(*executorSetConfig)

ExecutorSetOption configures executor ownership and resource limits.

func WithEgressRoute

func WithEgressRoute(route EgressRoute) ExecutorSetOption

WithEgressRoute configures the explicit route used by target-scoped grants.

func WithGrantTTL

func WithGrantTTL(duration time.Duration) ExecutorSetOption

WithGrantTTL sets the maximum lifetime of grants minted by every executor in the set. The duration must be positive when explicitly configured.

func WithMaxExecutors

func WithMaxExecutors(max int) ExecutorSetOption

WithMaxExecutors sets the hard number of memoized executor identities.

func WithScratchRoot

func WithScratchRoot(path string) ExecutorSetOption

WithScratchRoot supplies the caller-owned parent for the set's owned child.

func WithWindowsSandboxMode

func WithWindowsSandboxMode(mode windows.SandboxMode) ExecutorSetOption

WithWindowsSandboxMode selects the Windows confinement tier.

func WithWindowsSandboxStateRoot

func WithWindowsSandboxStateRoot(path string) ExecutorSetOption

WithWindowsSandboxStateRoot selects the Windows elevated installation root.

type Guarantees

type Guarantees = profile.Guarantees

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type Home

type Home = profile.Home

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type Isolation

type Isolation = profile.Isolation

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type LifetimeContainment

type LifetimeContainment uint8

LifetimeContainment reports which process-tree teardown contract a Supervised spawn actually received — achieved enforcement, not requested policy (the same honesty rule as profile.Guarantees).

const (
	// LifetimeContainmentUnspecified (zero value): the spawn carries no
	// lifetime containment claim at all — an Unconfined/null-backend or
	// test-double spawn, whose teardown is the escapable process-group
	// signal-and-poll sweep. Deliberately NOT Enforced: reporting kernel
	// enforcement for a spawn that has none would violate the honesty rule.
	LifetimeContainmentUnspecified LifetimeContainment = iota
	// LifetimeContainmentEnforced: the kernel itself guarantees teardown —
	// Linux Rung 1 PID namespace, Linux Rung 2 delegated cgroup v2, or a
	// Windows Job. No descendant can escape it.
	LifetimeContainmentEnforced
	// LifetimeContainmentBestEffort: teardown is process-group SIGKILL plus
	// proc-table closure descendant tracking (Darwin Seatbelt). A descendant
	// that daemonizes through a tracking gap can survive as an orphan —
	// still fully confined by the spawn's Seatbelt profile, but outliving
	// the session. See docs/lifetime-containment.md.
	LifetimeContainmentBestEffort
)

func (LifetimeContainment) String

func (c LifetimeContainment) String() string

String never panics. An unrecognized value (out-of-range constructions are possible since LifetimeContainment is a public alias, sandbox.LifetimeContainment) reports as LifetimeContainment(N) rather than silently aliasing to a real member's string, so it surfaces as visibly wrong instead of misreporting a containment contract.

type NetworkTarget

type NetworkTarget = network.Target

Egress vocabulary, aliased for the same reason as the profile vocabulary above.

func ParseNetworkTarget

func ParseNetworkTarget(raw string) (NetworkTarget, error)

ParseNetworkTarget parses a normalized "transport:host:port" egress target.

type PreparedProcess

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

PreparedProcess is a validated, single-use process start. Its effective workspace access (EffectiveAccess) is authoritative and immutable for the lifetime of the value. PrepareProcess performs the entire grant-redemption and resource-reservation transaction (any supplied grants are validated, authenticated, and consumed; retained path handles are borrowed and re-acquired; a route/proxy credential is authorized; the confined backend spec is compiled) before this value is ever returned, so by the time a caller holds a *PreparedProcess every reservation it needs is already final and a single-spawn grant can never become replayable regardless of whether Start is ever called. Start consumes the preparation at most once, atomically transferring every reservation to the spawned Process's background supervisor. Close releases an unstarted preparation's reservations and is otherwise idempotent and safe to call at any time, including after Start (a no-op once Start has consumed it).

func (*PreparedProcess) Close

func (p *PreparedProcess) Close() error

Close releases an unstarted preparation's reservations and is idempotent. Once Start has consumed the preparation, ownership of anything reserved has already transferred to the returned Process's background supervisor, so a later Close is a harmless no-op rather than an error.

func (*PreparedProcess) EffectiveAccess

func (p *PreparedProcess) EffectiveAccess() ProcessAccess

EffectiveAccess returns the authoritative workspace access reserved for this preparation. The returned value shares no mutable backing storage with the preparation or with any earlier or later call, and never changes across the preparation's lifetime (including after Start or Close).

func (*PreparedProcess) Start

func (p *PreparedProcess) Start(ctx context.Context) (*Process, error)

Start consumes the preparation and spawns the process, confining it through the identical processTree/backend machinery Executor.run and Executor.runBackendOwned already use. A second Start call on the same PreparedProcess, or any Start call after Close, fails without spawning. A spawn/setup failure (missing directory, binary not found) is returned as an error and no Process is returned; a process that subsequently runs to a non-zero exit is not an error and is reported through Process.Wait's ProcessResult instead. ctx governs only this call's own setup window through the decision to hand off — exactly like PrepareProcess's own ctx, it never governs the returned Process's lifetime, so a caller canceling the ctx it passed to Start after a Process has already been handed back must not kill that process. On success, ownership of every reservation PrepareProcess made is transferred atomically to a background goroutine that outlives this call and guarantees terminal cleanup runs even if the caller never calls Process.Wait or Process.Close.

type Process

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

Process is a running asynchronous process. Stdout/Stderr/Stdin are real, live pipes available immediately after Start — the caller reads and writes incrementally, not only after the process exits. Methods other than Wait are safe to call concurrently with each other and with stream I/O. Wait is cached: every caller (concurrent or sequential) observes the same result, and the underlying OS wait happens exactly once. Process deliberately exposes no OS process identifier; a model-facing process handle belongs in a higher layer, not this one.

func (*Process) Activities

func (p *Process) Activities() <-chan ProcessActivity

Activities returns the optional typed workspace-activity stream. It closes before Wait returns to any caller.

func (*Process) Close

func (p *Process) Close(ctx context.Context) error

Close closes the process's stream handles. It is idempotent. It does not itself signal or wait for the OS process — use Signal to request interrupt/terminate/kill, and Wait to observe the eventual exit — so a caller that wants the process to actually stop must still arrange that separately (e.g. via Signal, closing stdin, or waiting for natural completion). For a PTY-backed Process this additionally closes the real terminal master (terminalCloser) — a genuine hangup, delivering SIGHUP to the terminal's whole foreground process group — which is deliberately distinct from Stdin().Close() (delivers EOF in-band as the VEOF byte instead; see terminalStdin, terminal.go) and ends the output-draining pump (pumpPTYOutput, above), which in turn closes Stdout's pipe so any blocked reader observes EOF.

func (*Process) LifetimeContainment

func (p *Process) LifetimeContainment() LifetimeContainment

LifetimeContainment reports the process-tree teardown contract this spawn actually received: Enforced (Linux namespace/cgroup, Windows Job), BestEffort (Darwin Seatbelt — see docs/lifetime-containment.md), or Unspecified (an Unconfined/null-backend spawn making no claim). A nil Process reports Unspecified, matching every other nil-receiver accessor on this type returning its harmless zero value.

func (*Process) Resize

func (p *Process) Resize(ctx context.Context, rows, cols uint16) error

Resize changes a PTY-backed Process's terminal window size. It is a no-op (nil error) once the process is confirmed terminal, exactly like Signal's identical treatment of a concurrent natural exit — a resize request racing the process's own exit is not itself an error. A pipe-backed Process (or a PTY-backed one on a platform whose terminal has no resize primitive wired) has no resizer and fails closed with ErrProcessResizeUnsupported instead of silently succeeding, exactly like Signal's own fail-closed default for an unwired signaler.

func (*Process) Signal

func (p *Process) Signal(ctx context.Context, kind ProcessSignal) error

Signal delivers a portable interrupt/terminate/kill request. It is safe to call concurrently with itself and with every other Process method.

ProcessSignalInterrupt delivers immediately and never decides the process is terminal by itself — only an actual exit, observed through Wait, does that. ProcessSignalKill idempotently dispatches at most one kill for this Process's entire lifetime, immediately, with no grace period. ProcessSignalTerminate delivers at most one terminate signal — its first call only; later Terminate calls are no-ops that return the identical result — and, on that same first call, starts a background escalation that dispatches at most one kill once the terminate grace period elapses without a confirmed natural exit; it never re-sends terminate and never escalates more than once, and the eventual kill (whether from escalation or from a later explicit Signal(ProcessSignalKill) call) is the same single dispatch either path can trigger.

A concurrent natural exit — observed by Wait's runWait closing p.done, which every PreparedProcess.Start spawn's background supervisor already guarantees happens by calling Wait itself even if no other caller ever does — always wins over a pending or in-flight signal: once the process is confirmed terminal, Signal is a safe no-op for every kind, and a pending terminate escalation is skipped rather than delivered to a process that is already gone.

ctx governs only this call's own validation; it is never consulted again once delivery has started, so a ctx canceled after a Terminate call returns does not stop that call's already-started background escalation.

func (*Process) Stderr

func (p *Process) Stderr() io.ReadCloser

Stderr returns the process's live standard-error pipe, distinct from Stdout in this pipe-backed mode.

func (*Process) Stdin

func (p *Process) Stdin() io.WriteCloser

Stdin returns the process's standard-input pipe. Concurrent Write and Close calls are supported; Close is idempotent, delivers EOF to the process at most once, and causes later writes to fail with ErrProcessStdinClosed.

func (*Process) Stdout

func (p *Process) Stdout() io.ReadCloser

Stdout returns the process's live standard-output pipe.

func (*Process) StreamMode

func (p *Process) StreamMode() ProcessStreamMode

StreamMode reports this Process's stream topology: distinct pipes (ProcessStreamModePipes, every Process this package constructed before PTY support existed and every pipe-backed Process since) or one combined PTY stream (ProcessStreamModePTY, newPTYProcess). A nil Process reports ProcessStreamModePipes, matching every other nil-receiver accessor on this type returning its harmless zero value.

func (*Process) Wait

func (p *Process) Wait(ctx context.Context) (ProcessResult, error)

Wait blocks until the process reaches a terminal state or ctx is done, whichever comes first. Multiple concurrent (or sequential) callers observe the identical result; the real OS wait happens exactly once regardless of how many callers or contexts are involved. A ctx that is done before the process exits does not stop or kill the process — it only stops this call from waiting for it — so a caller with a fresh context can still retrieve the eventual result.

type ProcessAccess

type ProcessAccess struct {
	Kind ProcessAccessKind
	// contains filtered or unexported fields
}

ProcessAccess is the authoritative, immutable description of a prepared process's workspace access, captured once during PrepareProcess. WritePaths/WriteTrees return a defensive copy sharing no backing storage with the receiver or with any other call, so a caller can never mutate the preparation's authoritative state through the returned value.

func (ProcessAccess) WritePaths

func (a ProcessAccess) WritePaths() []string

WritePaths returns a defensive copy of the canonical individual write paths. Meaningful only for ProcessAccessScopedWrite.

func (ProcessAccess) WriteTrees

func (a ProcessAccess) WriteTrees() []string

WriteTrees returns a defensive copy of the canonical write directory trees. Meaningful only for ProcessAccessScopedWrite.

type ProcessAccessKind

type ProcessAccessKind uint8

ProcessAccessKind classifies the authoritative workspace write access reserved for a prepared process. It mirrors, structurally only, Harness's tool.WorkspaceAccessKind vocabulary (ReadOnly/ScopedWrite/BroadWrite) so a later adapter can translate directly; Sandbox defines its own stdlib-only type rather than importing Harness's.

const (
	// ProcessAccessReadOnly permits reads but no writes.
	ProcessAccessReadOnly ProcessAccessKind = iota + 1
	// ProcessAccessScopedWrite permits writes only to the paths and trees
	// reserved for this process: WritePaths/WriteTrees report the exact
	// canonical filesystem write grants folded into this preparation.
	ProcessAccessScopedWrite
	// ProcessAccessBroadWrite permits writes anywhere the executor's
	// workspace authority allows.
	ProcessAccessBroadWrite
)

func (ProcessAccessKind) Valid

func (k ProcessAccessKind) Valid() bool

Valid reports whether k is a recognized process access classification.

type ProcessActivity

type ProcessActivity struct {
	Kind ProcessActivityKind
}

ProcessActivity reports one unit of workspace activity from a running process.

func (ProcessActivity) EffectiveKind

func (a ProcessActivity) EffectiveKind() ProcessActivityKind

EffectiveKind returns the conservative activity classification. Invalid activity always maps to broad invalidation and can never narrow the immutable lifetime workspace access reserved by ProcessAccess.

type ProcessActivityKind

type ProcessActivityKind uint8

ProcessActivityKind classifies process-reported workspace activity. Mirrors, structurally only, Harness's tool.WorkspaceActivityKind.

const (
	// ProcessActivityWrite reports filesystem activity within the immutable
	// access reserved by the prepared process.
	ProcessActivityWrite ProcessActivityKind = iota + 1
	// ProcessActivityBroadWrite requests conservative broad invalidation.
	ProcessActivityBroadWrite
)

func (ProcessActivityKind) Valid

func (k ProcessActivityKind) Valid() bool

Valid reports whether k is a recognized process activity kind.

type ProcessOptions

type ProcessOptions struct {
	Directory   string
	Command     string
	ExecutionID string
	Grants      []string
	TTY         bool
	Deadline    time.Time
	// TerminateGrace bounds how long a terminate signal (see
	// ProcessSignalTerminate) is given to produce a natural exit before
	// Process.Signal escalates to exactly one kill. Zero or negative selects
	// defaultProcessTerminateGrace.
	TerminateGrace time.Duration
}

ProcessOptions describes one asynchronous process admission request. Grants are opaque, execution-bound tokens; this microtask does not verify them beyond a bare presence check when the executor's command authority is Gated; a later microtask consumes and cryptographically verifies them before spawn. A zero Deadline means no process-lifetime deadline.

type ProcessResult

type ProcessResult struct {
	ExitCode   int
	StartedAt  time.Time
	FinishedAt time.Time
}

ProcessResult is the terminal result of an asynchronous process. ExitCode is the portable executable exit status. OS process identifiers are intentionally excluded. A ran-but-non-zero process is reported here with a nil error, exactly like the synchronous RunCommand/RunArgv convention; a process that never spawned is reported by PreparedProcess.Start's error instead, never through this type.

type ProcessSignal

type ProcessSignal uint8

ProcessSignal is a portable process-tree signal request. Mirrors, structurally only, Harness's tool.ProcessSignal vocabulary (ProcessSignalInterrupt/ProcessSignalTerminate/ProcessSignalKill) so a later adapter can translate directly; Sandbox defines its own stdlib-only type rather than importing Harness's.

const (
	// ProcessSignalInterrupt requests cooperative interruption. It never by
	// itself decides the process is terminal — only an actual exit, observed
	// through Wait, does that.
	ProcessSignalInterrupt ProcessSignal = iota + 1
	// ProcessSignalTerminate requests cooperative termination, escalating to
	// exactly one ProcessSignalKill if the process has not exited by the end
	// of its resolved terminate grace period (ProcessOptions.TerminateGrace).
	ProcessSignalTerminate
	// ProcessSignalKill force-terminates immediately, with no grace period.
	ProcessSignalKill
)

func (ProcessSignal) Valid

func (s ProcessSignal) Valid() bool

Valid reports whether s is a recognized portable process signal.

type ProcessStreamMode

type ProcessStreamMode uint8

ProcessStreamMode describes a running Process's stream topology, mirroring Harness's tool.ProcessStreamMode vocabulary structurally (see process.go's own doc comment on why this package defines its own stdlib-only types rather than importing Harness's).

const (
	// ProcessStreamModePipes exposes distinct non-nil Stdout and Stderr pipe
	// readers. Every Process this package constructed before PTY support
	// existed, and every pipe-backed Process constructed since, is this mode.
	ProcessStreamModePipes ProcessStreamMode = iota + 1
	// ProcessStreamModePTY exposes combined terminal bytes through Stdout;
	// Stderr stays non-nil but is already closed and permanently empty (see
	// closedEmptyReadCloser). A PTY-backed Process never silently falls back
	// to separate pipes for either stream.
	ProcessStreamModePTY
)

func (ProcessStreamMode) Valid

func (m ProcessStreamMode) Valid() bool

Valid reports whether m is a recognized process stream mode.

type Profile

type Profile = profile.Profile

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

func NewProfile

func NewProfile(config ProfileConfig) (*Profile, error)

NewProfile is re-exported for this package's tests, which build profiles directly rather than through the facade.

func Restrict

func Restrict(base, ceiling *Profile) (*Profile, error)

Restrict returns the component-wise intersection of base and ceiling.

type ProfileConfig

type ProfileConfig = profile.ProfileConfig

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type ReportEntry

type ReportEntry = profile.ReportEntry

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

type RootAccess

type RootAccess = profile.RootAccess

The executor and its grant layer are written against the profile vocabulary, and the root facade re-exports the same names. Aliasing them here — rather than qualifying several hundred call sites — keeps this package's code and the facade spelled identically, which matters because the two are read together whenever the public contract is being checked.

Jump to

Keyboard shortcuts

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