// Copyright (C) 2026 Dcntrlized // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package registry import ( "context" "encoding/json" "os" "strings" "testing" "time" "git.dcntrlized.org/Dcntrlized/Maestro/lib/types" ) // TestSourceHrefAllowlist pins audit finding N7: the registry item's Source // comes verbatim from the image's OCI annotations, so the href the console // renders must be gated by the same http/https allowlist as the Web App chip. // An invalid source must never yield a usable href — it is served only as // plain display text. func TestSourceHrefAllowlist(t *testing.T) { valid := []string{ "https://github.com/Dcntrlized/Maestro", "https://git.dcntrlized.org/Dcntrlized/Calendar.git", "http://192.168.122.183:3000/Dcntrlized/KeepNote", "https://example.com/x?a=b#frag", "http://[::1]:8080/repo", } for _, raw := range valid { if got := sourceHrefFor(raw); got != raw { t.Errorf("valid source %q: got href %q, want it verbatim", raw, got) } } invalid := []string{ "javascript:alert(1)", "JAVASCRIPT:alert(1)", "data:text/html,", "vbscript:msgbox(1)", "file:///etc/passwd", "https://user:pass@example.com/repo", "https://user@example.com/repo", "github.com/Dcntrlized/Maestro", // relative: no scheme "//example.com/repo", // scheme-relative "/repo", // path only "", // absent " ", // whitespace only "https://example.com/re po", // space "https://example.com/", // angle brackets "https://example.com/repo\njavascript:1", // newline "https://example.com/repo\x00tail", // NUL "https://example.com/repo\x7f", // DEL "https:", // no host "ftp://example.com/repo", // wrong scheme } for _, raw := range invalid { if got := sourceHrefFor(raw); got != "" { t.Errorf("invalid source %q must not become an href, got %q", raw, got) } } } // TestSourceHrefWireKey locks the console contract: the validated href rides // as `source_href` next to the raw `source`, so the frontend can render plain // text for an invalid value instead of an anchor. func TestSourceHrefWireKey(t *testing.T) { b, err := json.Marshal(types.RegistryContainer{ Name: "keepnote", Source: "javascript:alert(1)", SourceHref: "", }) if err != nil { t.Fatalf("marshal: %v", err) } if !strings.Contains(string(b), `"source":"javascript:alert(1)"`) { t.Fatalf("raw source must stay on the wire for display: %s", b) } if strings.Contains(string(b), "source_href") { t.Fatalf("an invalid source must not emit source_href: %s", b) } b, err = json.Marshal(types.RegistryContainer{ Name: "keepnote", Source: "https://github.com/Dcntrlized/KeepNote", SourceHref: "https://github.com/Dcntrlized/KeepNote", }) if err != nil { t.Fatalf("marshal: %v", err) } if !strings.Contains(string(b), `"source_href":"https://github.com/Dcntrlized/KeepNote"`) { t.Fatalf("valid source must emit source_href: %s", b) } } // TestItemByNameLive resolves the infrastructure services against a live // registry. It is opt-in (MAESTRO_REGISTRY_TEST=1) so the default test run // stays offline; the dev VM and prod CI both set the Gitea env the function // reads. Infrastructure services must resolve so Maestro can auto-install them // on a fresh host (ensureInfraRunning). func TestItemByNameLive(t *testing.T) { if os.Getenv("MAESTRO_REGISTRY_TEST") != "1" { t.Skip("set MAESTRO_REGISTRY_TEST=1 with GITEA_* env to run against a live registry") } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() for _, name := range []string{"dcntrlized-db", "dcntrlized-auth"} { item, err := ItemByName(ctx, name) if err != nil { t.Fatalf("ItemByName(%q): %v", name, err) } if item.Image == "" { t.Errorf("ItemByName(%q): empty image", name) } if !strings.HasPrefix(item.URL, "http") { t.Errorf("ItemByName(%q): url %q not an http(s) compose source", name, item.URL) } t.Logf("resolved %s -> image=%s url=%s version=%s", name, item.Image, item.URL, item.Version) } } // TestServiceNameFromLabels locks the "is this package a Maestro service?" rule: // the org.dcntrlized.service label must be declared by the image (manifest // annotations or config labels). The runner job image dcntrlized/ci-tools // declares neither and must not surface in the console. func TestServiceNameFromLabels(t *testing.T) { if got := serviceNameFromLabels(map[string]string{"org.dcntrlized.service": "KeepNote"}, nil); got != "KeepNote" { t.Fatalf("annotation: got %q", got) } if got := serviceNameFromLabels(nil, map[string]string{"org.dcntrlized.service": "Calendar"}); got != "Calendar" { t.Fatalf("label: got %q", got) } // Annotations win when both are present. if got := serviceNameFromLabels( map[string]string{"org.dcntrlized.service": "KeepNote"}, map[string]string{"org.dcntrlized.service": "Calendar"}, ); got != "KeepNote" { t.Fatalf("annotation precedence: got %q", got) } if got := serviceNameFromLabels(nil, nil); got != "" { t.Fatalf("no label must yield empty, got %q", got) } if got := serviceNameFromLabels(map[string]string{"org.opencontainers.image.title": "ci-tools"}, nil); got != "" { t.Fatalf("unrelated labels must yield empty, got %q", got) } } // TestBaseURLNeverFallsBackToPublicInDev locks the dev registry seam: with // DCNTRLIZED_DEV=true Maestro must talk to the debug registry // (DCNTRLIZED_GITEA_URL) only — an unset dev URL yields "" (loud failure), // never the public git.dcntrlized.org. func TestBaseURLNeverFallsBackToPublicInDev(t *testing.T) { t.Setenv("DCNTRLIZED_DEV", "true") t.Setenv("DCNTRLIZED_GITEA_URL", "http://192.168.122.183:3000") if got := BaseURL(); got != "http://192.168.122.183:3000" { t.Fatalf("dev with DCNTRLIZED_GITEA_URL: got %q", got) } t.Setenv("DCNTRLIZED_GITEA_URL", "") if got := BaseURL(); got != "" { t.Fatalf("dev without DCNTRLIZED_GITEA_URL must not fall back to public, got %q", got) } t.Setenv("DCNTRLIZED_DEV", "") t.Setenv("DCNTRLIZED_GITEA_URL", "http://192.168.122.183:3000") if got := BaseURL(); got != "https://git.dcntrlized.org" { t.Fatalf("production must use the public registry, got %q", got) } }