package main import ( "archive/zip" "bytes" "encoding/json" "errors" "os" "os/exec" "path/filepath" "testing" ) func TestBundleRejectsStaleMissingModifiedAndUnexpectedArtifacts(t *testing.T) { t.Chdir(t.TempDir()) mustFixture(t, os.WriteFile("go.mod", []byte("module example.invalid/test\n"), 0600)) mustFixture(t, os.WriteFile("go.sum", []byte("recorded sum\n"), 0600)) mod, _ := inputDigest("go.mod") sum, _ := inputDigest("go.sum") m := manifest{Version: 1, GoModSHA256: mod, GoSumSHA256: sum, Modules: []module{{Path: "code.fritzlab.net/agenthub/example", Version: "v0.1.0"}}} for _, ext := range []string{".info", ".mod", ".zip"} { name := "code.fritzlab.net/agenthub/example/@v/v0.1.0" + ext b := []byte("canonical artifact " + ext) mustFixture(t, os.MkdirAll(filepath.Dir(filepath.Join(bundleRoot, name)), 0700)) mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, name), b, 0600)) m.Modules[0].Files = append(m.Modules[0].Files, artifact{Path: name, Size: int64(len(b)), SHA256: digest(b)}) } b, _ := json.Marshal(m) mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, "manifest.json"), b, 0600)) mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, "go.mod"), []byte(archiveBoundary), 0600)) if err := check(); err != nil { t.Fatal(err) } for _, boundary := range []string{"", "module unexpected.invalid/nested\n"} { mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, "go.mod"), []byte(boundary), 0600)) if check() == nil { t.Fatal("missing or changed archive boundary accepted") } } mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, "go.mod"), []byte(archiveBoundary), 0600)) originalPath, originalVersion := m.Modules[0].Path, m.Modules[0].Version for _, change := range []string{"path", "version"} { if change == "path" { m.Modules[0].Path = "code.fritzlab.net/agenthub/other" } else { m.Modules[0].Version = "v0.2.0" } changed, _ := json.Marshal(m) mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, "manifest.json"), changed, 0600)) if check() == nil { t.Fatal("artifact accepted under different module identity") } m.Modules[0].Path, m.Modules[0].Version = originalPath, originalVersion } mustFixture(t, os.WriteFile(filepath.Join(bundleRoot, "manifest.json"), b, 0600)) mustFixture(t, os.WriteFile("go.mod", []byte("changed graph\n"), 0600)) if check() == nil { t.Fatal("stale dependency closure accepted") } mustFixture(t, os.WriteFile("go.mod", []byte("module example.invalid/test\n"), 0600)) path := filepath.Join(bundleRoot, m.Modules[0].Files[2].Path) original, _ := os.ReadFile(path) mustFixture(t, os.WriteFile(path, []byte("corrupted zip"), 0600)) if check() == nil { t.Fatal("modified artifact accepted") } mustFixture(t, os.Remove(path)) if check() == nil { t.Fatal("missing artifact accepted") } mustFixture(t, os.WriteFile(path, original, 0600)) extra := filepath.Join(bundleRoot, "extra") mustFixture(t, os.WriteFile(extra, []byte("unlisted"), 0600)) if check() == nil { t.Fatal("unlisted artifact accepted") } mustFixture(t, os.Remove(extra)) mustFixture(t, os.Symlink("manifest.json", extra)) if check() == nil { t.Fatal("symlink accepted") } } func TestNativeGoSumRejectsChangedModuleArtifact(t *testing.T) { root := t.TempDir() proxy := filepath.Join(root, "proxy") modulePath, version := "code.fritzlab.net/fixture/module", "v0.1.0" base := filepath.Join(proxy, modulePath, "@v", version) if err := os.MkdirAll(filepath.Dir(base), 0700); err != nil { t.Fatal(err) } mod := []byte("module " + modulePath + "\n\ngo 1.27.0\n") mustFixture(t, os.WriteFile(base+".mod", mod, 0600)) mustFixture(t, os.WriteFile(base+".info", []byte(`{"Version":"v0.1.0","Time":"2026-01-01T00:00:00Z"}`), 0600)) var archive bytes.Buffer zw := zip.NewWriter(&archive) entry, err := zw.Create(modulePath + "@" + version + "/go.mod") if err != nil { t.Fatal(err) } if _, err := entry.Write(mod); err != nil { t.Fatal(err) } if err = zw.Close(); err != nil { t.Fatal(err) } mustFixture(t, os.WriteFile(base+".zip", archive.Bytes(), 0600)) mustFixture(t, os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example.invalid/checksum\n\ngo 1.27.0\n\nrequire "+modulePath+" "+version+"\n"), 0600)) run := func(cache string) ([]byte, error) { cmd := exec.Command("go", "mod", "download", modulePath+"@"+version) cmd.Dir = root cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOMODCACHE="+filepath.Join(root, cache), "GOPROXY=file://"+proxy, "GONOPROXY=none", "GONOSUMDB="+privatePrefix, "GOSUMDB=off", "GOTOOLCHAIN=local", "GOFLAGS=", "GOWORK=off") return cmd.CombinedOutput() } if output, err := run("original-cache"); err != nil { t.Fatalf("native fixture admission: %v: %s", err, output) } mustFixture(t, os.WriteFile(base+".mod", append(mod, '\n'), 0600)) output, err := run("fresh-cache") if err == nil || !bytes.Contains(output, []byte("checksum mismatch")) { t.Fatalf("native module checksum guard failed: %v: %s", err, output) } } func TestGeneratorMissingPrivateMetadataNeverInvokesGit(t *testing.T) { root := t.TempDir() t.Chdir(root) mustFixture(t, os.WriteFile("go.mod", []byte("module example.invalid/offline\n\ngo 1.27.0\n\nrequire code.fritzlab.net/agenthub/missing v0.0.1\n"), 0600)) mustFixture(t, os.WriteFile("go.sum", nil, 0600)) bin := filepath.Join(root, "bin") mustFixture(t, os.Mkdir(bin, 0700)) marker := filepath.Join(root, "git-invoked") mustFixture(t, os.WriteFile(filepath.Join(bin, "git"), []byte("#!/bin/sh\nprintf invoked > \"$PRIVATE_GIT_PROBE\"\nexit 99\n"), 0700)) t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH")) t.Setenv("PRIVATE_GIT_PROBE", marker) t.Setenv("GOMODCACHE", filepath.Join(root, "empty-cache")) t.Setenv("GOPRIVATE", "code.fritzlab.net") t.Setenv("GONOPROXY", "code.fritzlab.net") t.Setenv("GOTOOLCHAIN", "auto") err := generate() if err == nil { t.Fatal("missing cached module accepted") } var failure *exec.ExitError if !errors.As(err, &failure) || !bytes.Contains(failure.Stderr, []byte("module lookup disabled by GOPROXY=off")) { t.Fatalf("not a proven offline refusal: %v", err) } if _, err = os.Stat(marker); !os.IsNotExist(err) { t.Fatal("Git invoked during offline generation") } if _, err = os.Stat(bundleRoot); !os.IsNotExist(err) { t.Fatal("failed generation published output") } mustFixture(t, os.WriteFile("go.mod", []byte("module example.invalid/offline\n\ngo 1.999.0\n"), 0600)) _, err = nativeGo("list", "-m", "-json", "all") if !errors.As(err, &failure) || !bytes.Contains(failure.Stderr, []byte("GOTOOLCHAIN=local")) { t.Fatalf("toolchain download not disabled: %v", err) } } func TestCanonicalModuleArtifactPathBinding(t *testing.T) { for _, test := range []struct{ path, version, want string }{ {"code.fritzlab.net/agenthub/Provider", "v1.2.3-RC.1", "code.fritzlab.net/agenthub/!provider/@v/v1.2.3-!r!c.1"}, {"code.fritzlab.net/agenthub/provider", "v2.0.0+incompatible", "code.fritzlab.net/agenthub/provider/@v/v2.0.0+incompatible"}, } { got, err := moduleStem(test.path, test.version) if err != nil || got != test.want { t.Fatalf("stem %q %v", got, err) } } for _, path := range []string{"code.fritzlab.net/agenthub/../provider", "code.fritzlab.net/agenthub/provider!", "other.invalid/agenthub/provider"} { if _, err := moduleStem(path, "v1.0.0"); err == nil { t.Fatal("invalid path accepted") } } } func TestLazyGraphModuleCacheIdentity(t *testing.T) { cache := t.TempDir() path, version := "code.fritzlab.net/fixture/module", "v0.1.0" stem, err := cacheStem(cache, path, version, "") if err != nil || stem != path+"/@v/"+version { t.Fatalf("selected lazy module rejected: %q %v", stem, err) } for _, metadata := range []string{filepath.Join(cache, "foreign/@v/v0.1.0.mod"), filepath.Join(cache, "../escaped.mod"), filepath.Join(cache, path, "@v/v0.2.0.mod")} { if _, err := cacheStem(cache, path, version, metadata); err == nil { t.Fatal("contradictory cache identity accepted") } } } func mustFixture(t *testing.T, err error) { t.Helper() if err != nil { t.Fatal(err) } }