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) } } }