package builder import ( "context" "os" "path/filepath" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/catalogue" ) // A link as a parent of an archive's `from` must not lead the build out of the module: the repository // is somebody else's, and a build that followed it would pack, register and serve any directory the // build account can read (issue 1609). func TestAnArchiveWhoseFromGoesThroughALinkOutOfTheModuleIsRefused(t *testing.T) { outside := t.TempDir() if err := os.MkdirAll(filepath.Join(outside, "x"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(outside, "x", "secret"), []byte("s"), 0o644); err != nil { t.Fatal(err) } tree := t.TempDir() if err := os.Symlink(outside, filepath.Join(tree, "link")); err != nil { t.Fatal(err) } r := &recorded{} a := catalogue.Artifact{Name: "all", Kind: catalogue.ArtifactArchive, From: "link/x"} _, err := one(context.Background(), nil, r, "m", tree, t.TempDir(), "c", "", a, nil, nil, "", Npmrc{}, nil, nil, func(string, string, ...any) {}) if err == nil { t.Fatal("an archive was packed through a link out of the module") } realOutside, _ := filepath.EvalSymlinks(filepath.Join(outside, "x")) realTree, _ := filepath.EvalSymlinks(tree) for _, want := range []string{realOutside, realTree, "link/x"} { if !strings.Contains(err.Error(), want) { t.Errorf("the refusal %q does not name %q", err, want) } } if len(r.archives) != 0 { t.Errorf("a refused archive left %d published", len(r.archives)) } } // A link that stays inside the module is still followed, and its archive keeps its digest. func TestAnArchiveWhoseFromGoesThroughALinkInsideTheModuleIsPacked(t *testing.T) { tree := t.TempDir() if err := os.MkdirAll(filepath.Join(tree, "real", "x"), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(tree, "real", "x", "f"), []byte("f"), 0o644); err != nil { t.Fatal(err) } if err := os.Symlink("real", filepath.Join(tree, "link")); err != nil { t.Fatal(err) } r := &recorded{} a := catalogue.Artifact{Name: "all", Kind: catalogue.ArtifactArchive, From: "link/x"} if _, err := one(context.Background(), nil, r, "m", tree, t.TempDir(), "c", "", a, nil, nil, "", Npmrc{}, nil, nil, func(string, string, ...any) {}); err != nil { t.Fatal(err) } } // A module directory that is itself a link out of the fetched tree is refused, naming both places. func TestAModuleDirectoryThatIsALinkOutOfTheRepositoryIsRefused(t *testing.T) { outside := t.TempDir() if err := os.WriteFile(filepath.Join(outside, ManifestName), []byte("{}"), 0o644); err != nil { t.Fatal(err) } tree := t.TempDir() if err := os.MkdirAll(filepath.Join(tree, "modules"), 0o755); err != nil { t.Fatal(err) } if err := os.Symlink(outside, filepath.Join(tree, "modules", "evil")); err != nil { t.Fatal(err) } _, err := inside(tree, "modules/evil") if err == nil { t.Fatal("a module directory behind a link out of the repository was accepted") } realOutside, _ := filepath.EvalSymlinks(outside) realTree, _ := filepath.EvalSymlinks(tree) for _, want := range []string{realOutside, realTree, "modules/evil"} { if !strings.Contains(err.Error(), want) { t.Errorf("the refusal %q does not name %q", err, want) } } // A link that stays inside the repository, and a module directory not there, are left as they were. if err := os.MkdirAll(filepath.Join(tree, "real"), 0o755); err != nil { t.Fatal(err) } if err := os.Symlink("real", filepath.Join(tree, "modules", "fine")); err != nil { t.Fatal(err) } if _, err := inside(tree, "modules/fine"); err != nil { t.Errorf("a link inside the repository was refused: %v", err) } if _, err := inside(tree, "modules/none"); err != nil { t.Errorf("an absent module directory was refused here: %v", err) } } // A repository that commits `.mesh-build` as a link must not make a bundle's build write into, or pack, // a directory outside the module (issue 1609): the compile, the bundling and the pack each refuse it. func TestABundleWhoseOutputDirectoryIsALinkOutOfTheModuleIsRefused(t *testing.T) { outside := t.TempDir() if err := os.WriteFile(filepath.Join(outside, "secret"), []byte("s"), 0o644); err != nil { t.Fatal(err) } tree := t.TempDir() if err := os.Symlink(outside, filepath.Join(tree, ".mesh-build")); err != nil { t.Fatal(err) } chain, err := ToolchainFor("go") if err != nil { t.Fatal(err) } a := catalogue.Artifact{Name: "b", Kind: catalogue.ArtifactBundle, Language: "go", From: "cmd/b"} ran := false run := func(context.Context, string, string, ...string) (string, error) { ran = true; return "", nil } realOutside, _ := filepath.EvalSymlinks(outside) realTree, _ := filepath.EvalSymlinks(tree) check := func(what string, err error) { t.Helper() if err == nil { t.Fatalf("%s went through a link out of the module", what) } for _, want := range []string{realOutside, realTree} { if !strings.Contains(err.Error(), want) { t.Errorf("%s: the refusal %q does not name %q", what, err, want) } } } _, err = compile(context.Background(), run, tree, chain, "base", a) check("compile", err) _, err = bundled(context.Background(), run, tree, chain, "base", a, nil) check("bundled", err) check("the pack", outsideTheModule(tree, filepath.Join(tree, Out(a.Name)))) if ran { t.Error("a command ran for a refused bundle") } if entries, _ := os.ReadDir(outside); len(entries) != 1 { t.Errorf("a refused bundle wrote into the directory outside: %d entries", len(entries)) } }