mirror of
https://github.com/filebrowser/filebrowser.git
synced 2026-07-17 16:36:49 +00:00
121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
package files
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
// TestNewFs verifies that NewFs picks the right implementation and that the
|
|
// follow-external-symlinks toggle flips whether a symlink pointing outside the
|
|
// scope is honored.
|
|
func TestNewFs(t *testing.T) {
|
|
base := t.TempDir()
|
|
scope := filepath.Join(base, "srv")
|
|
outside := filepath.Join(base, "outside")
|
|
for _, d := range []string{scope, outside} {
|
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := os.WriteFile(filepath.Join(outside, "secret.txt"), []byte("secret"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// A symlink lexically inside the scope whose target resolves outside it.
|
|
if err := os.Symlink(outside, filepath.Join(scope, "escape")); err != nil {
|
|
t.Skipf("cannot create symlink: %v", err)
|
|
}
|
|
|
|
t.Run("disabled returns a ScopedFs that rejects the escaping symlink", func(t *testing.T) {
|
|
fs := NewFs(afero.NewOsFs(), scope, false)
|
|
if _, ok := fs.(*ScopedFs); !ok {
|
|
t.Fatalf("expected *ScopedFs, got %T", fs)
|
|
}
|
|
if _, err := fs.Stat("/escape"); !os.IsPermission(err) {
|
|
t.Fatalf("expected stat of escaping symlink to be rejected, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("enabled returns a BasePathFs that follows the escaping symlink", func(t *testing.T) {
|
|
fs := NewFs(afero.NewOsFs(), scope, true)
|
|
if _, ok := fs.(*afero.BasePathFs); !ok {
|
|
t.Fatalf("expected *afero.BasePathFs, got %T", fs)
|
|
}
|
|
if _, err := fs.Stat("/escape"); err != nil {
|
|
t.Fatalf("expected escaping symlink to be followed, got %v", err)
|
|
}
|
|
b, err := afero.ReadFile(fs, "/escape/secret.txt")
|
|
if err != nil {
|
|
t.Fatalf("expected to read through escaping symlink, got %v", err)
|
|
}
|
|
if string(b) != "secret" {
|
|
t.Fatalf("got %q, want %q", b, "secret")
|
|
}
|
|
|
|
// The link must also appear in a directory listing (the symptom in #5998).
|
|
entries, err := afero.ReadDir(fs, "/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var found bool
|
|
for _, e := range entries {
|
|
if e.Name() == "escape" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("expected escaping symlink to appear in the listing")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestBasePath verifies BasePath extracts the underlying *afero.BasePathFs from
|
|
// either filesystem NewFs may return, so User.FullPath keeps working.
|
|
func TestBasePath(t *testing.T) {
|
|
root := t.TempDir()
|
|
osFs := afero.NewOsFs()
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
followExternal bool
|
|
}{
|
|
{"ScopedFs", false},
|
|
{"BasePathFs", true},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
fs := NewFs(osFs, root, tc.followExternal)
|
|
base := BasePath(fs)
|
|
if base == nil {
|
|
t.Fatalf("expected non-nil base for %T", fs)
|
|
}
|
|
got := afero.FullBaseFsPath(base, "/x")
|
|
want := filepath.Join(root, "x")
|
|
if got != want {
|
|
t.Fatalf("FullBaseFsPath: got %q, want %q", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
if got := BasePath(osFs); got != nil {
|
|
t.Fatalf("expected nil base for a plain OsFs, got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestFileInfoRealPathUsesBasePathFsRealPath mirrors
|
|
// TestFileInfoRealPathUsesScopedFsRealPath for the follow-external-symlinks case,
|
|
// where the user filesystem is a bare BasePathFs.
|
|
func TestFileInfoRealPathUsesBasePathFsRealPath(t *testing.T) {
|
|
root := t.TempDir()
|
|
file := &FileInfo{
|
|
Fs: NewFs(afero.NewOsFs(), root, true),
|
|
Path: "/root/downloads",
|
|
}
|
|
|
|
got := file.RealPath()
|
|
want := filepath.Join(root, "root", "downloads")
|
|
if got != want {
|
|
t.Fatalf("got %q, want %q", got, want)
|
|
}
|
|
}
|