feat: Add support for environment protection (scrubbing) (#327)

* feat: Add support for environment variable protection for sandbox

* chore: Update dangerous env var list

* fix: Split profiles for improved environment protection

* fix: pipx sandbox profile separation

* chore: Show sandbox scrub info on error exit

* fix: Code review fixes

* test: Add e2e for sandbox environment scrubbing
This commit is contained in:
Abhisek Datta
2026-06-11 11:40:33 +05:30
committed by GitHub
parent 7620097613
commit c7244f921a
39 changed files with 1385 additions and 49 deletions
+20 -4
View File
@@ -19,9 +19,10 @@ type SandboxPolicy struct {
// These fields are affected by inheritance and are merged with the parent policy.
// Any new values added here should be handled in the MergeWithParent method.
Filesystem FilesystemPolicy `yaml:"filesystem" json:"filesystem"`
Network NetworkPolicy `yaml:"network" json:"network"`
Process ProcessPolicy `yaml:"process" json:"process"`
Filesystem FilesystemPolicy `yaml:"filesystem" json:"filesystem"`
Network NetworkPolicy `yaml:"network" json:"network"`
Process ProcessPolicy `yaml:"process" json:"process"`
Environment EnvironmentPolicy `yaml:"environment" json:"environment"`
// The boolean fields are pointers to allow for nil values so that the YAML parser
// can set the values from the child policy if present. We can differentiate between
@@ -63,6 +64,15 @@ type ProcessPolicy struct {
DenyExec []string `yaml:"deny_exec" json:"deny_exec"`
}
// EnvironmentPolicy controls which environment variables are scrubbed from the
// child process. Deny extends the built-in util.DANGEROUS_ENV_VARS list; Allow
// suppresses matching denies (allow wins). Patterns are case-insensitive name
// globs. Enforcement happens in sandbox/util.ScrubEnv at process spawn.
type EnvironmentPolicy struct {
Allow []string `yaml:"allow" json:"allow"`
Deny []string `yaml:"deny" json:"deny"`
}
// Validate validates the sandbox policy for correctness before inheritance resolution.
// Returns an error if the policy is invalid.
// Note: Validation for "at least one rule" check is deferred to ValidateResolved(),
@@ -94,7 +104,9 @@ func (p *SandboxPolicy) ValidateResolved() error {
len(p.Network.AllowOutbound) > 0 ||
len(p.Network.DenyOutbound) > 0 ||
len(p.Process.AllowExec) > 0 ||
len(p.Process.DenyExec) > 0
len(p.Process.DenyExec) > 0 ||
len(p.Environment.Allow) > 0 ||
len(p.Environment.Deny) > 0
if !hasRules {
return fmt.Errorf("policy must define at least one access rule (after inheritance resolution)")
@@ -134,6 +146,10 @@ func (child *SandboxPolicy) MergeWithParent(parent *SandboxPolicy) {
child.Process.AllowExec = unionStringSlices(parent.Process.AllowExec, child.Process.AllowExec)
child.Process.DenyExec = unionStringSlices(parent.Process.DenyExec, child.Process.DenyExec)
// Union environment lists
child.Environment.Allow = unionStringSlices(parent.Environment.Allow, child.Environment.Allow)
child.Environment.Deny = unionStringSlices(parent.Environment.Deny, child.Environment.Deny)
// Set boolean fields by duplicating the parent value if not present in the child.
if child.AllowPTY == nil {
child.AllowPTY = utils.PtrTo(utils.SafelyGetValue(parent.AllowPTY))