fix: expand ${CWD}/${HOME}/${TMPDIR} in --sandbox-allow path overrides (#344)

The runtime --sandbox-allow CLI override path never expanded the supported
sandbox variables (${CWD}, ${HOME}, ${TMPDIR}), so a value like
write='${CWD}/**' was treated as a literal path segment and the allow rule
never matched. Profile-loaded sandbox paths already expand these via
sandbox/util.ExpandVariables.

Expand the variables in resolveToAbsolute, the shared chokepoint for
read/write/exec overrides, before resolving to an absolute path. Glob
characters are preserved through expansion and filepath.Clean.

Fixes #257

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
Matt Van Horn
2026-06-18 12:17:33 +05:30
committed by GitHub
co-authored by Matt Van Horn
parent 55f3f2a252
commit c17b941ac3
2 changed files with 73 additions and 0 deletions
+6
View File
@@ -8,6 +8,7 @@ import (
"unicode"
"github.com/safedep/dry/log"
sandboxutil "github.com/safedep/pmg/sandbox/util"
)
// validSandboxAllowTypes is the set of recognized --sandbox-allow type prefixes.
@@ -217,6 +218,11 @@ func isLocalhostAddress(host string) bool {
// resolveToAbsolute resolves a path to an absolute path relative to CWD.
// Glob characters are preserved. The path is cleaned via filepath.Clean().
func resolveToAbsolute(value string) (string, error) {
value, err := sandboxutil.ExpandVariables(value)
if err != nil {
return "", err
}
if filepath.IsAbs(value) {
return filepath.Clean(value), nil
}
+67
View File
@@ -94,6 +94,73 @@ func TestParseSandboxAllowOverrides_ValidFormats(t *testing.T) {
}
}
func TestParseSingleOverride_ExpandsSandboxVariables(t *testing.T) {
cwd, err := os.Getwd()
require.NoError(t, err)
home, err := os.UserHomeDir()
require.NoError(t, err)
tmpDir := os.TempDir()
tests := []struct {
name string
raw string
expectedType SandboxAllowType
expectedValue string
notContains string
}{
{
name: "write expands CWD glob",
raw: "write=${CWD}/**",
expectedType: SandboxAllowWrite,
expectedValue: filepath.Clean(filepath.Join(cwd, "**")),
notContains: "${CWD}",
},
{
name: "read expands HOME",
raw: "read=${HOME}/x",
expectedType: SandboxAllowRead,
expectedValue: filepath.Clean(filepath.Join(home, "x")),
notContains: "${HOME}",
},
{
name: "write expands TMPDIR",
raw: "write=${TMPDIR}/pmg-cache",
expectedType: SandboxAllowWrite,
expectedValue: filepath.Clean(filepath.Join(tmpDir, "pmg-cache")),
notContains: "${TMPDIR}",
},
{
name: "exec expands CWD",
raw: "exec=${CWD}/bin/tool",
expectedType: SandboxAllowExec,
expectedValue: filepath.Clean(filepath.Join(cwd, "bin", "tool")),
notContains: "${CWD}",
},
{
name: "absolute path without variables is unchanged",
raw: "write=/tmp/pmg-output",
expectedType: SandboxAllowWrite,
expectedValue: filepath.Clean("/tmp/pmg-output"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseSingleOverride(tt.raw)
require.NoError(t, err)
assert.Equal(t, tt.expectedType, got.Type)
assert.Equal(t, tt.expectedValue, got.Value)
assert.Equal(t, tt.raw, got.Raw)
if tt.notContains != "" {
assert.NotContains(t, got.Value, tt.notContains)
}
})
}
}
func TestParseSandboxAllowOverrides_Env(t *testing.T) {
tests := []struct {
name string