102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLocalRemoveSymlinkDoesNotDeleteTarget(t *testing.T) {
|
|
root := t.TempDir()
|
|
outside := t.TempDir()
|
|
target := filepath.Join(outside, "secret.txt")
|
|
if err := os.WriteFile(target, []byte("keep"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link := filepath.Join(root, "link")
|
|
if err := os.Symlink(target, link); err != nil {
|
|
t.Skipf("symlink unavailable: %v", err)
|
|
}
|
|
store := Local{Root: root}
|
|
if err := store.Remove("link"); err != nil {
|
|
t.Fatalf("remove symlink: %v", err)
|
|
}
|
|
if _, err := os.Stat(target); err != nil {
|
|
t.Fatalf("symlink removal must not delete target: %v", err)
|
|
}
|
|
if _, err := os.Lstat(link); !os.IsNotExist(err) {
|
|
t.Fatalf("symlink should be removed, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLocalRejectsSymlinkEscapeAndAllowsNestedCreate(t *testing.T) {
|
|
root := t.TempDir()
|
|
outside := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(outside, "secret.txt"), []byte("secret"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Symlink(outside, filepath.Join(root, "escape")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := Local{Root: root}
|
|
if _, err := s.Open("escape/secret.txt"); err == nil {
|
|
t.Fatal("expected symlink escape to be rejected")
|
|
}
|
|
f, err := s.Create("a/b/c.txt")
|
|
if err != nil {
|
|
t.Fatalf("nested create failed: %v", err)
|
|
}
|
|
_ = f.Close()
|
|
}
|
|
|
|
func TestMoveToTrashPreservesRelativePathAndCleansOldEntries(t *testing.T) {
|
|
root := t.TempDir()
|
|
trash := t.TempDir()
|
|
s := Local{Root: root}
|
|
if err := os.MkdirAll(filepath.Join(root, "one", "two"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(root, "one", "two", "same.txt"), []byte("x"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(trash, "old"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old := filepath.Join(trash, "old", "stale.txt")
|
|
if err := os.WriteFile(old, []byte("old"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
oldTime := time.Now().Add(-31 * 24 * time.Hour)
|
|
if err := os.Chtimes(old, oldTime, oldTime); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.MoveToTrash("one/two/same.txt", trash); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(trash, "one", "two", "same.txt")); err != nil {
|
|
t.Fatalf("trash path not preserved: %v", err)
|
|
}
|
|
if _, err := os.Stat(old); !os.IsNotExist(err) {
|
|
t.Fatalf("stale trash entry was not cleaned: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCopyPathPreservesSymlink(t *testing.T) {
|
|
src := filepath.Join(t.TempDir(), "link")
|
|
dst := filepath.Join(t.TempDir(), "link")
|
|
if err := os.Symlink("target", src); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := copyPath(src, dst); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := os.Readlink(dst)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != "target" {
|
|
t.Fatalf("got symlink target %q", got)
|
|
}
|
|
}
|