Files
mesh-controller/internal/builder/archive_symlink_test.go
T

105 lines
3.7 KiB
Go

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