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:
Abhisek Datta
2026-01-13 14:52:02 +05:30
committed by GitHub
co-authored by Copilot Sahil Bansal
parent c0122898ca
commit 9693428171
35 changed files with 3826 additions and 29 deletions
+92
View File
@@ -0,0 +1,92 @@
package sandbox
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestNewDefaultProfileRegistry(t *testing.T) {
registry, err := newDefaultProfileRegistry()
assert.NoError(t, err)
assert.NotNil(t, registry)
assert.Greater(t, len(registry.profiles), 0)
npmRestrictive, err := registry.GetProfile("npm-restrictive")
assert.NoError(t, err)
assert.NotNil(t, npmRestrictive)
pypiRestrictive, err := registry.GetProfile("pypi-restrictive")
assert.NoError(t, err)
assert.NotNil(t, pypiRestrictive)
}
func TestLoadCustomProfile(t *testing.T) {
cases := []struct {
name string
policy *SandboxPolicy
assert func(t *testing.T, policy *SandboxPolicy, err error)
}{
{
name: "valid policy",
policy: &SandboxPolicy{
Name: "test",
Description: "test",
PackageManagers: []string{"npm"},
// At least some rules are required for a policy to be valid
Filesystem: FilesystemPolicy{
AllowRead: []string{"/tmp"},
AllowWrite: []string{"/tmp"},
DenyRead: []string{"/private/var"},
DenyWrite: []string{"/private/var"},
},
},
assert: func(t *testing.T, policy *SandboxPolicy, err error) {
assert.NoError(t, err)
assert.NotNil(t, policy)
assert.Equal(t, "test", policy.Name)
assert.Equal(t, "test", policy.Description)
assert.Equal(t, []string{"npm"}, policy.PackageManagers)
assert.Equal(t, FilesystemPolicy{
AllowRead: []string{"/tmp"},
AllowWrite: []string{"/tmp"},
DenyRead: []string{"/private/var"},
DenyWrite: []string{"/private/var"},
}, policy.Filesystem)
},
},
{
name: "invalid policy without any rules",
policy: &SandboxPolicy{
Name: "test",
Description: "test",
PackageManagers: []string{"npm"},
},
assert: func(t *testing.T, policy *SandboxPolicy, err error) {
assert.Error(t, err)
assert.Nil(t, policy)
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
registry, err := newDefaultProfileRegistry()
assert.NoError(t, err)
assert.NotNil(t, registry)
tempFile, err := os.CreateTemp(t.TempDir(), "sandbox-policy-*.yml")
assert.NoError(t, err)
defer tempFile.Close()
err = yaml.NewEncoder(tempFile).Encode(c.policy)
assert.NoError(t, err)
policy, err := registry.LoadCustomProfile(tempFile.Name())
c.assert(t, policy, err)
})
}
}