Refuse templates read through a symlink anywhere under files/
A symlinked files/ or a symlinked directory under it let module check, the merge gate and module add read a template from outside the repository tree, and a symlink no template names passed them while the build's packing refuses it (review of mesh-controller#313). Every entry under files/ must now be a plain directory or file, and templates are read through an os.Root of the module's directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -256,3 +256,68 @@ func TestAManifestStillNamingATemplateIsNotHeld(t *testing.T) {
|
||||
t.Fatalf("an inline manifest is refused: %s", problem)
|
||||
}
|
||||
}
|
||||
|
||||
// A symlinked files/, or a symlinked directory under it, would read a file outside the tree: refused by name,
|
||||
// as is any symlink or special file under files/ that no template names, which the build's packing refuses.
|
||||
func TestASymlinkAnywhereUnderFilesIsRefusedByName(t *testing.T) {
|
||||
m, err := ParseManifest([]byte(templatedTwin))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outside := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(outside, "deeper"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range []string{"tidy.conf", "deeper/extra.conf", "secret"} {
|
||||
if err := os.WriteFile(filepath.Join(outside, filepath.FromSlash(name)), []byte("outside"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
write := func(t *testing.T, dir, name string) {
|
||||
t.Helper()
|
||||
full := filepath.Join(dir, filepath.FromSlash(name))
|
||||
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(full, []byte("inside"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
link := func(t *testing.T, target, at string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(filepath.Dir(at), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(target, at); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
make func(t *testing.T, dir string)
|
||||
names string
|
||||
}{
|
||||
{"files/ itself", func(t *testing.T, dir string) {
|
||||
link(t, outside, filepath.Join(dir, "files"))
|
||||
}, "files/ is a symlink"},
|
||||
{"a directory under files/", func(t *testing.T, dir string) {
|
||||
write(t, dir, "files/tidy.conf")
|
||||
link(t, filepath.Join(outside, "deeper"), filepath.Join(dir, "files", "deeper"))
|
||||
}, "files/deeper is a symlink"},
|
||||
{"a file no template names", func(t *testing.T, dir string) {
|
||||
write(t, dir, "files/tidy.conf")
|
||||
write(t, dir, "files/deeper/extra.conf")
|
||||
link(t, filepath.Join(outside, "secret"), filepath.Join(dir, "files", "unnamed"))
|
||||
}, "files/unnamed is a symlink"},
|
||||
} {
|
||||
dir := t.TempDir()
|
||||
c.make(t, dir)
|
||||
_, err := WithTemplatesFrom(m, dir)
|
||||
if err == nil || !strings.Contains(err.Error(), c.names) {
|
||||
t.Errorf("a symlinked %s was not refused by name: %v", c.name, err)
|
||||
}
|
||||
if err != nil && strings.Contains(err.Error(), "outside") {
|
||||
t.Errorf("a symlinked %s was read: %v", c.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user