mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Experimental Sandbox Support (#101)
* feat: Sandbox implementation with seatbelt * refactor: Remove concept of PM_CACHE * fix: Misc fixes * refactor: Sandbox for separation of boundaries * fix: Apply API * fix: Add support for sandbox cleanup * test: Add variable interpolation test * fix: Misc cleanup fixes * chore: Cleanup sandbox registry * chore: Cleanup sandbox policy * chore: Cleanup sandbox * fix: Misc cleanup fixes * fix: Remove violation mode * fix: Update config template * chore: Go mod cleanup * fix: Handle the case when package manager policy is explicitly disabled * fix: Sandbox executor * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> * test: Remove unused var * test: Add test for seatbelt sandbox driver * fix: Sandbox profile loader from file should use path for caching * test: Add policy test * feat: Add support for config templates * fix: Seatbelt translator handle glob * fix: Merge conflicts * fix: Fix sandbox policy generator for MacOS min permissions * fix: Sandbox path handling bugs * fix: Deny read to dangerous directories * fix: Deny read to dangerous directories * add sandbox e2e (#112) * fix: Sandbox E2E test * fix: Code review fixes * fix: Code review fixes * doc: Add sandbox debugging guide * doc: Update sandbox doc * docs: Add sandbox usage doc * fix: Use better error for sandbox without policy * fix: Add sandbox for npx * fix: Enable PTY for npm --------- Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Sahil Bansal <bansalsahil315@gmail.com>
This commit is contained in:
co-authored by
Copilot
Sahil Bansal
parent
c0122898ca
commit
9693428171
@@ -0,0 +1,105 @@
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// ExecutionResult represents the result of executing a command in a sandbox.
|
||||
// It contains sandbox internal state and allows for future extension with
|
||||
// additional metadata (e.g., exit codes, resource usage, violation events).
|
||||
// Callers must call Close() after cmd.Run() completes to clean up resources.
|
||||
type ExecutionResult struct {
|
||||
executed bool
|
||||
sandbox Sandbox
|
||||
}
|
||||
|
||||
// ExecutionResultOpt is a function that can be used to configure an ExecutionResult.
|
||||
type ExecutionResultOpt func(*ExecutionResult)
|
||||
|
||||
// WithSandbox sets the sandbox for the ExecutionResult.
|
||||
func WithExecutionResultSandbox(sb Sandbox) ExecutionResultOpt {
|
||||
return func(r *ExecutionResult) {
|
||||
r.sandbox = sb
|
||||
}
|
||||
}
|
||||
|
||||
// WithExecuted sets the executed flag for the ExecutionResult.
|
||||
func WithExecutionResultExecuted(executed bool) ExecutionResultOpt {
|
||||
return func(r *ExecutionResult) {
|
||||
r.executed = executed
|
||||
}
|
||||
}
|
||||
|
||||
// NewExecutionResult creates a new ExecutionResult.
|
||||
func NewExecutionResult(opts ...ExecutionResultOpt) *ExecutionResult {
|
||||
r := &ExecutionResult{}
|
||||
for _, opt := range opts {
|
||||
opt(r)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// ShouldRun returns true if the caller should execute cmd.Run().
|
||||
func (r *ExecutionResult) ShouldRun() bool {
|
||||
return !r.executed
|
||||
}
|
||||
|
||||
// Close cleans up any resources allocated by the sandbox.
|
||||
// Must be called after cmd.Run() completes.
|
||||
func (r *ExecutionResult) Close() error {
|
||||
if r.sandbox != nil {
|
||||
return r.sandbox.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sandbox represents a platform-specific sandbox executor that isolates
|
||||
// package manager processes with controlled access to filesystem, network,
|
||||
// and process execution resources.
|
||||
type Sandbox interface {
|
||||
// Execute prepares or runs a command in the sandbox with the given policy.
|
||||
//
|
||||
// Behavior varies by implementation:
|
||||
// - CLI-based sandboxes (Seatbelt, Bubblewrap): Modify cmd in place by wrapping it
|
||||
// with sandbox CLI (e.g., sandbox-exec). Returns ExecutionResult with executed=false.
|
||||
// - Library-based sandboxes: Execute the command directly within the sandbox.
|
||||
// Returns ExecutionResult with executed=true.
|
||||
//
|
||||
// Returns:
|
||||
// - ExecutionResult: Contains execution state and metadata
|
||||
// - error: Non-nil if sandbox setup or execution failed
|
||||
//
|
||||
// Callers must check result.ShouldRun() and only call cmd.Run() if true.
|
||||
Execute(ctx context.Context, cmd *exec.Cmd, policy *SandboxPolicy) (*ExecutionResult, error)
|
||||
|
||||
// Name returns the sandbox implementation name (e.g., "seatbelt", "bubblewrap").
|
||||
Name() string
|
||||
|
||||
// IsAvailable returns true if the sandbox is available and functional on this platform.
|
||||
IsAvailable() bool
|
||||
|
||||
// Close cleans up any resources allocated by the sandbox (e.g., temporary files).
|
||||
// Must be called after cmd.Run() completes. Idempotent - safe to call multiple times.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// ProfileRegistry manages built-in and custom sandbox policies.
|
||||
type ProfileRegistry interface {
|
||||
// GetProfile retrieves a policy by name.
|
||||
// Name can be a built-in profile (e.g., "npm-restrictive") or a path to a custom YAML file.
|
||||
GetProfile(name string) (*SandboxPolicy, error)
|
||||
|
||||
// LoadCustomProfile loads a policy from a custom YAML file path.
|
||||
LoadCustomProfile(path string) (*SandboxPolicy, error)
|
||||
|
||||
// ListProfiles returns the names of all built-in profiles.
|
||||
ListProfiles() []string
|
||||
}
|
||||
|
||||
// NewProfileRegistry creates a new profile registry with built-in policies.
|
||||
func NewProfileRegistry() (ProfileRegistry, error) {
|
||||
return newDefaultProfileRegistry()
|
||||
}
|
||||
Reference in New Issue
Block a user