package assets import ( "bytes" "context" "errors" "io" "os" "path/filepath" "runtime" "testing" ) func TestLocalFSPutReadDelete(t *testing.T) { store, err := NewLocalFS(t.TempDir(), "https://app.test") if err != nil { t.Fatal(err) } stored, err := store.Put(context.Background(), "uploads/day/a file.png", bytes.NewReader([]byte("asset")), 5, "image/png") if err != nil { t.Fatal(err) } if stored.URL != "https://app.test/uploads/day/a%20file.png" { t.Fatalf("URL = %q", stored.URL) } blob, err := store.Read(context.Background(), stored.Key) if err != nil { t.Fatal(err) } b, _ := io.ReadAll(blob.Body) blob.Body.Close() if string(b) != "asset" || blob.ContentType != "image/png" { t.Fatalf("blob = %q %#v", b, blob) } if err := store.Delete(context.Background(), stored.Key); err != nil { t.Fatal(err) } if _, err := store.Read(context.Background(), stored.Key); !errors.Is(err, ErrBlobNotFound) { t.Fatalf("Read deleted error = %v", err) } } func TestLocalFSRejectsTraversalAbsoluteAndSymlinks(t *testing.T) { root := t.TempDir() outside := t.TempDir() store, err := NewLocalFS(root, "http://local.test") if err != nil { t.Fatal(err) } for _, key := range []string{"../escape", "uploads/../../escape", filepath.Join(outside, "escape"), "uploads\\..\\escape"} { if _, err := store.Put(context.Background(), key, bytes.NewReader(nil), 0, "application/octet-stream"); !errors.Is(err, ErrUnsafeBlobKey) { t.Errorf("Put(%q) error = %v", key, err) } } if runtime.GOOS == "windows" { return } if err := os.Symlink(outside, filepath.Join(root, "linked")); err != nil { t.Fatal(err) } if _, err := store.Put(context.Background(), "linked/escape", bytes.NewReader([]byte("x")), 1, "text/plain"); !errors.Is(err, ErrUnsafeBlobKey) { t.Fatalf("symlink directory error = %v", err) } if err := os.WriteFile(filepath.Join(outside, "target"), []byte("secret"), 0600); err != nil { t.Fatal(err) } if err := os.Symlink(filepath.Join(outside, "target"), filepath.Join(root, "final")); err != nil { t.Fatal(err) } if _, err := store.Read(context.Background(), "final"); !errors.Is(err, ErrUnsafeBlobKey) { t.Fatalf("symlink file error = %v", err) } }