Files
pmg/sandbox/platform/probe_canary_test.go
5131c3f641 feat(sandbox): ExecutionContext plumbing and fail-closed lockdown contract (#371)
* feat(sandbox): ExecutionContext plumbing and fail-closed lockdown contract

Network lockdown needs the PMG proxy's address, which is only known at
spawn time. Thread an ExecutionContext from the proxy flow through the
runner and executor into every sandbox driver, and enforce the
network_via_proxy_only fail-closed contract: lockdown without a running
loopback proxy, or on a driver that cannot enforce it, is a hard error —
never a silent fallback to unrestricted network.

- sandbox.ExecutionContext{ProxyAddr} + 4-arg Sandbox.Execute
- sandbox.ValidateLockdown validates the proxy address (loopback only)
  with usefulerror code SandboxRequiresProxy
- Seatbelt validates lockdown before translation (translation itself
  lands next); bubblewrap and landlock reject lockdown as unsupported
  until Linux enforcement is implemented
- executor.WithExecutionContext, runner.ExecuteOptions.SandboxProxyAddr,
  proxy flow passes the live proxy address
- ApplySandbox also validates centrally before invoking the driver

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* fix(sandbox): require numeric in-range proxy port in ValidateLockdown

The validated port string is embedded into generated sandbox profiles,
so service names, zero, and out-of-range ports are refused.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* fix(sandbox): fail closed on Seatbelt lockdown until translation lands

A lockdown policy that passed proxy validation would silently receive
the pre-lockdown network rules from the translator. Reject it until the
lockdown profile translation is implemented, keeping the window between
plumbing and enforcement fail-closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* chore: review feedback — drop redundant comment, simplify stub help text

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 20:12:33 +05:30

119 lines
3.4 KiB
Go

package platform
import (
"context"
"errors"
"os/exec"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/safedep/pmg/sandbox"
)
type fakeSandbox struct {
name sandbox.DriverName
available bool
executeErr error
executedRun bool
closed bool
}
func (f *fakeSandbox) Execute(ctx context.Context, cmd *exec.Cmd, policy *sandbox.SandboxPolicy, rt *sandbox.ExecutionContext) (*sandbox.ExecutionResult, error) {
if f.executeErr != nil {
return nil, f.executeErr
}
return sandbox.NewExecutionResult(
sandbox.WithExecutionResultSandbox(f),
sandbox.WithExecutionResultExecuted(f.executedRun),
), nil
}
func (f *fakeSandbox) Name() sandbox.DriverName { return f.name }
func (f *fakeSandbox) IsAvailable() bool { return f.available }
func (f *fakeSandbox) Close() error { f.closed = true; return nil }
func trueCmd(ctx context.Context) *exec.Cmd { return exec.CommandContext(ctx, "true") }
func falseCmd(ctx context.Context) *exec.Cmd { return exec.CommandContext(ctx, "false") }
func maskedCanaryCmd(ctx context.Context) *exec.Cmd {
cmd := exec.CommandContext(ctx, "printf", "")
cmd.Args = []string{"cat", "", canaryTargetPath}
return cmd
}
func TestRunCanary(t *testing.T) {
tests := []struct {
name string
factory canarySandboxFactory
cmd canaryCommandFactory
want sandbox.ProbeStatus
}{
{
name: "ok blocks read",
factory: func() (sandbox.Sandbox, error) { return &fakeSandbox{name: "fake", available: true}, nil },
cmd: falseCmd,
want: sandbox.ProbeStatusOK,
},
{
name: "ok masks read",
factory: func() (sandbox.Sandbox, error) { return &fakeSandbox{name: "fake", available: true}, nil },
cmd: maskedCanaryCmd,
want: sandbox.ProbeStatusOK,
},
{
name: "fail did not block",
factory: func() (sandbox.Sandbox, error) { return &fakeSandbox{name: "fake", available: true}, nil },
cmd: trueCmd,
want: sandbox.ProbeStatusFail,
},
{
name: "skip not available",
factory: func() (sandbox.Sandbox, error) { return &fakeSandbox{name: "fake", available: false}, nil },
cmd: falseCmd,
want: sandbox.ProbeStatusSkipped,
},
{
name: "fail constructor error",
factory: func() (sandbox.Sandbox, error) { return nil, errors.New("boom") },
cmd: falseCmd,
want: sandbox.ProbeStatusFail,
},
{
name: "fail execute error",
factory: func() (sandbox.Sandbox, error) {
return &fakeSandbox{name: "fake", available: true, executeErr: errors.New("setup failed")}, nil
},
cmd: falseCmd,
want: sandbox.ProbeStatusFail,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
res := runCanary(context.Background(), "canary.fake", "fake", tc.factory, tc.cmd)
assert.Equal(t, "canary.fake", res.Name)
assert.Equal(t, tc.want, res.Status)
})
}
}
func TestDenyAllCanaryPolicy(t *testing.T) {
p := denyAllCanaryPolicy()
require.NoError(t, p.ValidateResolved())
assert.Contains(t, p.Filesystem.DenyRead, canaryTargetPath)
}
func TestDriverInstallFix_BubblewrapIsDistroNeutral(t *testing.T) {
fix := driverInstallFix(sandbox.DriverBubblewrap)
assert.Contains(t, fix.Description, "distribution package manager")
assert.Empty(t, fix.Command)
assert.Equal(t, "https://github.com/containers/bubblewrap", fix.Docs)
}
func TestDefaultProbes_NotEmpty(t *testing.T) {
probes := DefaultProbes()
assert.NotEmpty(t, probes)
}