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
+25 -1
View File
@@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"strings"
"unicode"
"github.com/safedep/dry/log"
)
@@ -16,6 +17,7 @@ var validSandboxAllowTypes = map[SandboxAllowType]bool{
SandboxAllowExec: true,
SandboxAllowNetConnect: true,
SandboxAllowNetBind: true,
SandboxAllowEnv: true,
}
// parseSandboxAllowOverrides parses raw --sandbox-allow flag values into validated overrides.
@@ -69,7 +71,7 @@ func parseSingleOverride(raw string) (SandboxAllowOverride, error) {
}
if !validSandboxAllowTypes[allowType] {
return SandboxAllowOverride{}, fmt.Errorf("unknown type %q, valid types: read, write, exec, net-connect, net-bind", typStr)
return SandboxAllowOverride{}, fmt.Errorf("unknown type %q, valid types: read, write, exec, net-connect, net-bind, env", typStr)
}
resolved, err := validateAndResolveValue(allowType, value)
@@ -95,11 +97,33 @@ func validateAndResolveValue(typ SandboxAllowType, value string) (string, error)
return validateNetConnect(value)
case SandboxAllowNetBind:
return validateNetBind(value)
case SandboxAllowEnv:
return validateEnvName(value)
default:
return "", fmt.Errorf("unhandled type: %s", typ)
}
}
// validateEnvName validates an env allow value. The value is an environment
// variable name or name glob (e.g. NPM_TOKEN, npm_config_*) and is kept
// verbatim. Unlike filesystem/exec values it is NOT path-resolved, since it
// matches a variable name and not a filesystem location. Matching is
// case-insensitive at scrub time, so the value is not normalized here.
// Whitespace and control characters are rejected because the value is echoed
// back in logs and audit events, and backslash and separators because they
// have no place in a variable name or glob.
func validateEnvName(value string) (string, error) {
invalid := strings.ContainsAny(value, "=/\\") ||
strings.ContainsFunc(value, func(r rune) bool {
return unicode.IsSpace(r) || unicode.IsControl(r)
})
if invalid {
return "", fmt.Errorf("invalid env variable name %q (expected a name or name glob, e.g. NPM_TOKEN or npm_config_*)", value)
}
return value, nil
}
// resolveFilesystemPath resolves a filesystem path for read/write overrides.
// Supports glob patterns. Resolves relative paths to absolute via CWD.
func resolveFilesystemPath(value string) (string, error) {