From e72c1d6324cbf985126e21c329e13c8f101f5492 Mon Sep 17 00:00:00 2001 From: Abhisek Datta Date: Fri, 9 Jan 2026 15:00:24 +0530 Subject: [PATCH] test: Add policy test --- docs/sandbox.md | 27 ++++++++++++++++ sandbox/registry_test.go | 69 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 docs/sandbox.md diff --git a/docs/sandbox.md b/docs/sandbox.md new file mode 100644 index 0000000..62fd08c --- /dev/null +++ b/docs/sandbox.md @@ -0,0 +1,27 @@ +# Sandbox + +Design goal for sandbox in PMG context is to protect against unknown supply chain attacks using principles of least privilege. +We do not want to re-invent sandbox and likely rely on OS native sandbox primitives. This is at the cost of developer experience, +where we have to work within the limitations of the sandbox implementations that we use. + +## Policy + +PMG defines its own policy model. The design goal is simplicity and ease of use. Sandbox implementations are expected to translate +the policy model into their own native policy format. Rules for policy are: + +- Deny by default unless explicitly allowed +- Deny rules have higher priority than allow rules +- Policy profile allows binding package managers to a specific sandbox policy +- Package manager must have a sandbox profile when sandbox is enabled +- Package manager specific sandbox profile may be disabled to skip sandbox for the package manager + +## Enforcement + +The sandbox implementation currently only support `block` mode. This means, any policy violation will block the execution of the +package manager command. + +## References + +- https://github.com/anthropic-experimental/sandbox-runtime +- https://geminicli.com/docs/cli/sandbox/ +- https://github.com/containers/bubblewrap \ No newline at end of file diff --git a/sandbox/registry_test.go b/sandbox/registry_test.go index 05ad9c9..9d31ba1 100644 --- a/sandbox/registry_test.go +++ b/sandbox/registry_test.go @@ -1,9 +1,11 @@ package sandbox import ( + "os" "testing" "github.com/stretchr/testify/assert" + "go.yaml.in/yaml/v3" ) func TestNewDefaultProfileRegistry(t *testing.T) { @@ -21,3 +23,70 @@ func TestNewDefaultProfileRegistry(t *testing.T) { 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) + }) + } +}