@@ -0,0 +1,103 @@
|
||||
package sourceagent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormalizeControllerPolicySecureDefaultsAndRoots(t *testing.T) {
|
||||
p, err := normalizeControllerPolicy(defaultControllerPolicy())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.Enabled || p.AutonomousEnabled || !p.DryRun || p.AllowDestructive {
|
||||
t.Fatalf("unsafe default controller policy: %+v", p)
|
||||
}
|
||||
if p.MaxConcurrentJobs != 1 || p.MaxJobDuration <= 0 {
|
||||
t.Fatalf("unexpected limits: %+v", p)
|
||||
}
|
||||
|
||||
root := t.TempDir()
|
||||
p.AllowedComposeRoots = []string{root, root}
|
||||
p, err = normalizeControllerPolicy(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(p.AllowedComposeRoots) != 1 || !filepath.IsAbs(p.AllowedComposeRoots[0]) {
|
||||
t.Fatalf("compose roots were not normalized: %#v", p.AllowedComposeRoots)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateControllerJobAutonomyAndDestructivePolicy(t *testing.T) {
|
||||
base := ControllerPolicy{Enabled: true, AutonomousEnabled: true, AllowDestructive: false}
|
||||
for _, kind := range []string{"evidence_http_probe", "health_recovery", "compute_capacity_compose", "compose_smoke_test"} {
|
||||
if err := validateControllerJob(ControllerJob{Kind: kind, Autonomous: true}, base); err != nil {
|
||||
t.Fatalf("%s: %v", kind, err)
|
||||
}
|
||||
}
|
||||
if err := validateControllerJob(ControllerJob{Kind: "container_create", Autonomous: true}, base); err == nil {
|
||||
t.Fatal("autonomous arbitrary container creation must be denied")
|
||||
}
|
||||
if err := validateControllerJob(ControllerJob{Kind: "container_remove"}, base); err == nil {
|
||||
t.Fatal("destructive removal must require policy")
|
||||
}
|
||||
base.AllowDestructive = true
|
||||
if err := validateControllerJob(ControllerJob{Kind: "container_remove"}, base); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
base.AutonomousEnabled = false
|
||||
if err := validateControllerJob(ControllerJob{Kind: "evidence_http_probe", Autonomous: true}, base); err == nil {
|
||||
t.Fatal("autonomous off must deny autonomous jobs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerImageAllowlistDoesNotUseAmbiguousPrefix(t *testing.T) {
|
||||
allowed := []string{"curlimages/curl:", "registry.example/team/", "exact/tool:1"}
|
||||
good := []string{"curlimages/curl:8.11.1", "registry.example/team/worker:2", "exact/tool:1"}
|
||||
bad := []string{"curlimages/curl-malicious:8", "registry.example/team-malicious/worker:2", "exact/tool:10"}
|
||||
for _, image := range good {
|
||||
if !imageAllowed(image, allowed) {
|
||||
t.Fatalf("expected allowed: %s", image)
|
||||
}
|
||||
}
|
||||
for _, image := range bad {
|
||||
if imageAllowed(image, allowed) {
|
||||
t.Fatalf("unexpected allow: %s", image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitDockerImageReferenceHandlesRegistryPortsAndDigests(t *testing.T) {
|
||||
cases := []struct{ in, repo, tag string }{
|
||||
{"curlimages/curl:8.11.1", "curlimages/curl", "8.11.1"},
|
||||
{"registry.example:5000/team/tool:2", "registry.example:5000/team/tool", "2"},
|
||||
{"registry.example:5000/team/tool", "registry.example:5000/team/tool", ""},
|
||||
{"tool@sha256:012345", "tool@sha256:012345", ""},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
repo, tag := splitDockerImageReference(tc.in)
|
||||
if repo != tc.repo || tag != tc.tag {
|
||||
t.Fatalf("%s => %s/%s, want %s/%s", tc.in, repo, tag, tc.repo, tc.tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerJobHistoryRejectsInlineSecrets(t *testing.T) {
|
||||
if err := validateControllerParametersForStorage(ControllerJob{Kind: "container_create", Parameters: map[string]any{"env": []string{"MODE=test", "API_TOKEN=secret"}}}); err == nil {
|
||||
t.Fatal("expected inline secret to be rejected")
|
||||
}
|
||||
if err := validateControllerParametersForStorage(ControllerJob{Kind: "container_create", Parameters: map[string]any{"env": []string{"MODE=test", "LOG_LEVEL=debug"}}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvidenceProbeBlocksLocalTargets(t *testing.T) {
|
||||
for _, raw := range []string{"http://127.0.0.1/x", "http://10.1.2.3/x", "http://[::1]/x", "http://localhost/x"} {
|
||||
u, _ := url.Parse(raw)
|
||||
if err := validateEvidenceProbeTarget(context.Background(), u); err == nil {
|
||||
t.Fatalf("expected local target rejection: %s", raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user