utils/files_test.go
Rama main · réplica de lectura del repositorio original.
package utils
import (
"os"
"path/filepath"
"testing"
)
func TestURL2filename(t *testing.T) {
tests := []struct {
dirname string
urlpath string
want string
}{
{"/srv", "/index.html", "/srv" + Pathsep + "index.html"},
{"/srv/", "/index.html", "/srv/index.html"},
{"/srv", "/sub/page.md", "/srv" + Pathsep + "sub/page.md"},
{"/srv", "relative.txt", "/srv/relative.txt"},
{"/srv", "/../etc/passwd", "/srv" + Pathsep},
{"/srv", "//admin/secret.lua", "/srv" + Pathsep + "admin" + Pathsep + "secret.lua"},
{"/srv", "/a/./b//c", "/srv" + Pathsep + "a" + Pathsep + "b" + Pathsep + "c"},
{"/srv", "/sub/", "/srv" + Pathsep + "sub" + Pathsep},
{"/srv", "/", "/srv" + Pathsep},
}
for _, tt := range tests {
got := URL2filename(tt.dirname, tt.urlpath)
if got != tt.want {
t.Errorf("URL2filename(%q, %q) = %q, want %q", tt.dirname, tt.urlpath, got, tt.want)
}
}
}
// Containment helper: only descendants of root should pass.
func TestWithinDir(t *testing.T) {
root := t.TempDir()
cases := []struct {
name string
path string
want bool
}{
{"root itself", root, true},
{"direct child", filepath.Join(root, "a.js"), true},
{"nested child", filepath.Join(root, "sub", "a.js"), true},
{"sibling", filepath.Join(root, "..", "other"), false},
{"ancestor", filepath.Dir(root), false},
}
for _, c := range cases {
if got := WithinDir(root, c.path); got != c.want {
t.Errorf("%s: WithinDir(%q,%q) = %v, want %v", c.name, root, c.path, got, c.want)
}
}
}
func TestGetFilenames(t *testing.T) {
dir, err := os.MkdirTemp("", "algernon-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
// Create some files
for _, name := range []string{"a.txt", "b.md", "c.html"} {
if err := os.WriteFile(dir+"/"+name, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
}
filenames := GetFilenames(dir)
if len(filenames) != 3 {
t.Errorf("GetFilenames returned %d files, want 3", len(filenames))
}
// Non-existent directory returns empty slice
filenames = GetFilenames("/nonexistent_dir_xyz")
if len(filenames) != 0 {
t.Errorf("GetFilenames on nonexistent dir returned %d files, want 0", len(filenames))
}
}
func TestDescribeBytes(t *testing.T) {
tests := []struct {
size int64
want string
}{
{0, "0 KiB"},
{1024, "1 KiB"},
{512, "0 KiB"},
{1048576, "1 MiB"},
{2097152, "2 MiB"},
{10240, "10 KiB"},
}
for _, tt := range tests {
got := DescribeBytes(tt.size)
if got != tt.want {
t.Errorf("DescribeBytes(%d) = %q, want %q", tt.size, got, tt.want)
}
}
}
func TestResolvesWithinDir(t *testing.T) {
outside := t.TempDir()
secret := filepath.Join(outside, "secret.txt")
if err := os.WriteFile(secret, []byte("secret"), 0o600); err != nil {
t.Fatal(err)
}
root := t.TempDir()
inside := filepath.Join(root, "inside.txt")
if err := os.WriteFile(inside, []byte("ok"), 0o600); err != nil {
t.Fatal(err)
}
links := map[string]string{
"leak": secret,
"leakdir": outside,
"insidelink": inside,
}
for name, target := range links {
if err := os.Symlink(target, filepath.Join(root, name)); err != nil {
t.Skip("symlinks are not supported here:", err)
}
}
for p, want := range map[string]bool{
inside: true,
filepath.Join(root, "insidelink"): true,
filepath.Join(root, "missing.txt"): true,
filepath.Join(root, "leak"): false,
filepath.Join(root, "leakdir"): false,
filepath.Join(root, "leakdir", "secret.txt"): false,
} {
if got := ResolvesWithinDir(root, p); got != want {
t.Errorf("ResolvesWithinDir(root, %s) = %v, want %v", p, got, want)
}
}
// The root itself may be a symlink, like /srv/www -> /data/www
rootLink := filepath.Join(t.TempDir(), "rootlink")
if err := os.Symlink(root, rootLink); err != nil {
t.Fatal(err)
}
if !ResolvesWithinDir(rootLink, filepath.Join(rootLink, "inside.txt")) {
t.Error("a file in a symlinked root should be within the root")
}
if ResolvesWithinDir(rootLink, filepath.Join(rootLink, "leak")) {
t.Error("a symlink out of a symlinked root should not be within the root")
}
}