80 lines
2.7 KiB
Go
80 lines
2.7 KiB
Go
package fileops
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestRemoveAllUnderRootRemovesNestedTreeIdempotently(t *testing.T) {
|
|
root := t.TempDir()
|
|
target := filepath.Join(root, "runs", "run-1")
|
|
mustWriteFile(t, filepath.Join(target, "nested", "result.txt"), []byte("result"), 0o640)
|
|
|
|
if err := RemoveAllUnderRoot(root, target); err != nil {
|
|
t.Fatalf("RemoveAllUnderRoot() error = %v", err)
|
|
}
|
|
if _, err := os.Lstat(target); !os.IsNotExist(err) {
|
|
t.Fatalf("Lstat(%q) error = %v, want not exist", target, err)
|
|
}
|
|
if err := RemoveAllUnderRoot(root, target); err != nil {
|
|
t.Fatalf("second RemoveAllUnderRoot() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRemoveAllUnderRootRejectsUnsafeTargets(t *testing.T) {
|
|
root := t.TempDir()
|
|
outside := t.TempDir()
|
|
target := filepath.Join(root, "runs", "run-1")
|
|
mustWriteFile(t, filepath.Join(target, "result.txt"), []byte("result"), 0o640)
|
|
sentinel := filepath.Join(outside, "sentinel.txt")
|
|
mustWriteFile(t, sentinel, []byte("outside"), 0o640)
|
|
|
|
if err := RemoveAllUnderRoot(root, root); err == nil {
|
|
t.Fatal("RemoveAllUnderRoot(root, root) error = nil, want root rejection")
|
|
}
|
|
if err := RemoveAllUnderRoot(root, filepath.Join(outside, "target")); err == nil {
|
|
t.Fatal("RemoveAllUnderRoot(outside) error = nil, want outside-root rejection")
|
|
}
|
|
if err := os.Symlink(outside, filepath.Join(root, "runs")); err == nil {
|
|
t.Fatal("Symlink() error = nil, want collision because runs already exists")
|
|
}
|
|
if err := os.RemoveAll(filepath.Join(root, "runs")); err != nil {
|
|
t.Fatalf("RemoveAll(runs) error = %v", err)
|
|
}
|
|
if err := os.Symlink(outside, filepath.Join(root, "runs")); err != nil {
|
|
t.Fatalf("Symlink() error = %v", err)
|
|
}
|
|
if err := RemoveAllUnderRoot(root, target); err == nil {
|
|
t.Fatal("RemoveAllUnderRoot(symlinked ancestor) error = nil, want rejection")
|
|
}
|
|
data, err := os.ReadFile(sentinel)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile(sentinel) error = %v", err)
|
|
}
|
|
if string(data) != "outside" {
|
|
t.Fatalf("sentinel content = %q, want outside", data)
|
|
}
|
|
}
|
|
|
|
func TestRemoveAllUnderRootRejectsSymlinkInTree(t *testing.T) {
|
|
root := t.TempDir()
|
|
outside := t.TempDir()
|
|
target := filepath.Join(root, "runs", "run-1")
|
|
if err := os.MkdirAll(target, 0o750); err != nil {
|
|
t.Fatalf("MkdirAll(target) error = %v", err)
|
|
}
|
|
sentinel := filepath.Join(outside, "sentinel.txt")
|
|
mustWriteFile(t, sentinel, []byte("outside"), 0o640)
|
|
if err := os.Symlink(outside, filepath.Join(target, "link")); err != nil {
|
|
t.Fatalf("Symlink() error = %v", err)
|
|
}
|
|
|
|
if err := RemoveAllUnderRoot(root, target); err == nil {
|
|
t.Fatal("RemoveAllUnderRoot() error = nil, want symlink rejection")
|
|
}
|
|
if _, err := os.Stat(sentinel); err != nil {
|
|
t.Fatalf("outside sentinel was changed: %v", err)
|
|
}
|
|
}
|