mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
215 lines
6.4 KiB
Go
215 lines
6.4 KiB
Go
package sandbox
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/safedep/pmg/config"
|
||
|
|
pmgsandbox "github.com/safedep/pmg/sandbox"
|
||
|
|
"github.com/stretchr/testify/assert"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
type allowDeps struct {
|
||
|
|
overlayDir string
|
||
|
|
repoRoot string
|
||
|
|
cache *pmgsandbox.ViolationCache
|
||
|
|
}
|
||
|
|
|
||
|
|
func newAllowDeps(t *testing.T) *allowDeps {
|
||
|
|
t.Helper()
|
||
|
|
return &allowDeps{
|
||
|
|
overlayDir: t.TempDir(),
|
||
|
|
repoRoot: filepath.Clean(t.TempDir()),
|
||
|
|
cache: pmgsandbox.NewViolationCache(t.TempDir()),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func runAllowCmd(t *testing.T, deps *allowDeps, args ...string) (string, string, error) {
|
||
|
|
t.Helper()
|
||
|
|
cmd := newAllowCommand(allowFactory{
|
||
|
|
overlayDir: func() string { return deps.overlayDir },
|
||
|
|
repoRoot: func() (string, error) { return deps.repoRoot, nil },
|
||
|
|
cache: func() *pmgsandbox.ViolationCache { return deps.cache },
|
||
|
|
})
|
||
|
|
var stdout, stderr bytes.Buffer
|
||
|
|
cmd.SetOut(&stdout)
|
||
|
|
cmd.SetErr(&stderr)
|
||
|
|
cmd.SetArgs(args)
|
||
|
|
err := cmd.Execute()
|
||
|
|
return stdout.String(), stderr.String(), err
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_PositionalSaved(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
stdout, stderr, err := runAllowCmd(t, deps, "net-bind=localhost:4321")
|
||
|
|
require.NoError(t, err, "stderr: %s", stderr)
|
||
|
|
assert.Contains(t, stdout, "Saved 1 allowance")
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.NotNil(t, overlay)
|
||
|
|
require.Len(t, overlay.Allow, 1)
|
||
|
|
assert.Equal(t, config.SandboxAllowNetBind, overlay.Allow[0].Type)
|
||
|
|
assert.Equal(t, "localhost:4321", overlay.Allow[0].Value)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_LastAllPromotesFromCache(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
writeTarget := filepath.Join(deps.repoRoot, ".astro")
|
||
|
|
execTarget := "/usr/bin/sh"
|
||
|
|
report := &pmgsandbox.ViolationReport{
|
||
|
|
SandboxName: pmgsandbox.DriverSeatbelt,
|
||
|
|
Violations: []pmgsandbox.Violation{
|
||
|
|
{Kind: pmgsandbox.ViolationKindFSWrite, Target: writeTarget},
|
||
|
|
{Kind: pmgsandbox.ViolationKindExec, Target: execTarget},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
_, err := deps.cache.Write(report)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
_, stderr, err := runAllowCmd(t, deps, "--last", "--all")
|
||
|
|
require.NoError(t, err, "stderr: %s", stderr)
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.NotNil(t, overlay)
|
||
|
|
assert.ElementsMatch(t, []pmgsandbox.OverlayAllow{
|
||
|
|
{Type: config.SandboxAllowWrite, Value: writeTarget},
|
||
|
|
{Type: config.SandboxAllowExec, Value: execTarget},
|
||
|
|
}, overlay.Allow)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_LastPromotesPrimaryOnly(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
writeTarget := filepath.Join(deps.repoRoot, ".astro")
|
||
|
|
execTarget := "/usr/bin/sh"
|
||
|
|
report := &pmgsandbox.ViolationReport{
|
||
|
|
SandboxName: pmgsandbox.DriverSeatbelt,
|
||
|
|
Violations: []pmgsandbox.Violation{
|
||
|
|
{Kind: pmgsandbox.ViolationKindFSWrite, Target: writeTarget},
|
||
|
|
{Kind: pmgsandbox.ViolationKindExec, Target: execTarget},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
_, err := deps.cache.Write(report)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
_, _, err = runAllowCmd(t, deps, "--last")
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.NotNil(t, overlay)
|
||
|
|
require.Len(t, overlay.Allow, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_LastNoCacheErrors(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
_, _, err := runAllowCmd(t, deps, "--last")
|
||
|
|
require.Error(t, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_RefusesSensitiveTargetWithoutForce(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
secret := filepath.Join(deps.repoRoot, ".env")
|
||
|
|
_, _, err := runAllowCmd(t, deps, "read="+secret)
|
||
|
|
require.Error(t, err)
|
||
|
|
assert.Contains(t, err.Error(), "sensitive target")
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Nil(t, overlay)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_SensitiveTargetWithForceSaves(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
secret := filepath.Join(deps.repoRoot, ".env")
|
||
|
|
_, _, err := runAllowCmd(t, deps, "--force", "read="+secret)
|
||
|
|
require.NoError(t, err)
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.NotNil(t, overlay)
|
||
|
|
assert.Len(t, overlay.Allow, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_Dedupes(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
_, _, err := runAllowCmd(t, deps, "exec=/usr/bin/sh")
|
||
|
|
require.NoError(t, err)
|
||
|
|
stdout, _, err := runAllowCmd(t, deps, "exec=/usr/bin/sh")
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Contains(t, stdout, "No new allowances")
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.NotNil(t, overlay)
|
||
|
|
assert.Len(t, overlay.Allow, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_NothingToSaveErrors(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
_, _, err := runAllowCmd(t, deps)
|
||
|
|
require.Error(t, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_AllWithoutLastErrors(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
_, _, err := runAllowCmd(t, deps, "--all", "exec=/usr/bin/sh")
|
||
|
|
require.Error(t, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_InvalidPositionalRejected(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
_, _, err := runAllowCmd(t, deps, "garbage")
|
||
|
|
require.Error(t, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_RefusedUnderLockdown(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
cmd := newAllowCommand(allowFactory{
|
||
|
|
overlayDir: func() string { return deps.overlayDir },
|
||
|
|
repoRoot: func() (string, error) { return deps.repoRoot, nil },
|
||
|
|
cache: func() *pmgsandbox.ViolationCache { return deps.cache },
|
||
|
|
locked: func() bool { return true },
|
||
|
|
})
|
||
|
|
var stdout, stderr bytes.Buffer
|
||
|
|
cmd.SetOut(&stdout)
|
||
|
|
cmd.SetErr(&stderr)
|
||
|
|
cmd.SetArgs([]string{"exec=/usr/bin/sh"})
|
||
|
|
err := cmd.Execute()
|
||
|
|
require.Error(t, err)
|
||
|
|
assert.Contains(t, err.Error(), "global_lockdown")
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Nil(t, overlay)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllow_LastNormalizesRelativeTargets(t *testing.T) {
|
||
|
|
deps := newAllowDeps(t)
|
||
|
|
cwd, err := os.Getwd()
|
||
|
|
require.NoError(t, err)
|
||
|
|
relTarget := "./.astro"
|
||
|
|
wantAbs := filepath.Join(cwd, ".astro")
|
||
|
|
|
||
|
|
report := &pmgsandbox.ViolationReport{
|
||
|
|
SandboxName: pmgsandbox.DriverSeatbelt,
|
||
|
|
Violations: []pmgsandbox.Violation{
|
||
|
|
{Kind: pmgsandbox.ViolationKindFSWrite, Target: relTarget},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
_, err = deps.cache.Write(report)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
_, _, err = runAllowCmd(t, deps, "--last")
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
overlay, _, err := pmgsandbox.LoadOverlayForRepo(deps.overlayDir, deps.repoRoot)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.NotNil(t, overlay)
|
||
|
|
require.Len(t, overlay.Allow, 1)
|
||
|
|
assert.Equal(t, wantAbs, overlay.Allow[0].Value, "cache-derived target should be normalized to absolute")
|
||
|
|
}
|