package browser import ( "errors" "io" "os" "path/filepath" "testing" "webfilebrowser/internal/config" ) func TestOpenFileReturnsOnlyARegularFileWithinTheConfiguredRoot(t *testing.T) { rootPath := t.TempDir() contents := []byte("byte-identical download") if err := os.WriteFile(filepath.Join(rootPath, "report.txt"), contents, 0o600); err != nil { t.Fatal(err) } browser, err := New([]config.Root{{Name: "Media", Path: rootPath}}) if err != nil { t.Fatal(err) } file, metadata, err := browser.OpenFile("Media", "report.txt") if err != nil { t.Fatalf("OpenFile() error = %v", err) } t.Cleanup(func() { _ = file.Close() }) actual, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if string(actual) != string(contents) { t.Errorf("OpenFile() bytes = %q, want %q", actual, contents) } if metadata.Name != "report.txt" || metadata.Size != int64(len(contents)) { t.Errorf("OpenFile() metadata = %#v, want report.txt with its byte size", metadata) } } func TestOpenFileRejectsTraversalAndSymlinkEscapes(t *testing.T) { rootPath := t.TempDir() outsidePath := t.TempDir() if err := os.WriteFile(filepath.Join(outsidePath, "secret.txt"), []byte("outside-root"), 0o600); err != nil { t.Fatal(err) } if err := os.Symlink(filepath.Join(outsidePath, "secret.txt"), filepath.Join(rootPath, "escape.txt")); err != nil { t.Fatal(err) } browser, err := New([]config.Root{{Name: "Media", Path: rootPath}}) if err != nil { t.Fatal(err) } for _, relativePath := range []string{"../" + filepath.Base(outsidePath) + "/secret.txt", "escape.txt"} { t.Run(relativePath, func(t *testing.T) { if _, _, err := browser.OpenFile("Media", relativePath); !errors.Is(err, ErrPathOutsideRoot) { t.Errorf("OpenFile(%q) error = %v, want ErrPathOutsideRoot", relativePath, err) } }) } }