diff --git a/changelog/unreleased/fix-propfind-escape-name.md b/changelog/unreleased/fix-propfind-escape-name.md
new file mode 100644
index 0000000000..91854255b0
--- /dev/null
+++ b/changelog/unreleased/fix-propfind-escape-name.md
@@ -0,0 +1,8 @@
+Bugfix: Properly escape oc:name in propfind response
+
+The oc:name property in the ocdav propfind response might contain
+XML special characters. We now apply the proper escaping on that
+property.
+
+https://github.com/cs3org/reva/pull/3255
+https://github.com/owncloud/ocis/issues/4474
diff --git a/internal/http/services/owncloud/ocdav/propfind/propfind.go b/internal/http/services/owncloud/ocdav/propfind/propfind.go
index bfe39b70c4..093f4746b3 100644
--- a/internal/http/services/owncloud/ocdav/propfind/propfind.go
+++ b/internal/http/services/owncloud/ocdav/propfind/propfind.go
@@ -1010,7 +1010,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
}
if md.Name != "" {
- appendToOK(prop.Raw("oc:name", md.Name))
+ appendToOK(prop.Escaped("oc:name", md.Name))
}
if md.Etag != "" {
@@ -1343,7 +1343,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
}
}
case "name":
- appendToOK(prop.Raw("oc:name", md.Name))
+ appendToOK(prop.Escaped("oc:name", md.Name))
case "shareid":
if ref, err := storagespace.ParseReference(strings.TrimPrefix(md.Path, "/")); err == nil && ref.GetResourceId().GetSpaceId() == utils.ShareStorageSpaceID {
appendToOK(prop.Raw("oc:shareid", ref.GetResourceId().GetOpaqueId()))
diff --git a/internal/http/services/owncloud/ocdav/propfind/propfind_test.go b/internal/http/services/owncloud/ocdav/propfind/propfind_test.go
index c7e80d1181..58513a90f3 100644
--- a/internal/http/services/owncloud/ocdav/propfind/propfind_test.go
+++ b/internal/http/services/owncloud/ocdav/propfind/propfind_test.go
@@ -233,6 +233,14 @@ var _ = Describe("Propfind", func() {
Path: "./dir",
Size: uint64(30),
})
+ mockStat(&sprovider.Reference{ResourceId: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "root"}, Path: "./dir&dir"},
+ &sprovider.ResourceInfo{
+ Id: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "dir"},
+ Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
+ Path: "./dir&dir",
+ Name: "dir&dir",
+ Size: uint64(30),
+ })
mockStat(&sprovider.Reference{ResourceId: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "dir"}, Path: "."},
&sprovider.ResourceInfo{
Id: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "dir"},
@@ -249,6 +257,15 @@ var _ = Describe("Propfind", func() {
Size: 30,
},
})
+ mockListContainer(&sprovider.Reference{ResourceId: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "root"}, Path: "./dir&dir"},
+ []*sprovider.ResourceInfo{
+ {
+ Id: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "direntry"},
+ Type: sprovider.ResourceType_RESOURCE_TYPE_FILE,
+ Path: "entry",
+ Size: 30,
+ },
+ })
mockListContainer(&sprovider.Reference{ResourceId: &sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "dir"}, Path: "."},
[]*sprovider.ResourceInfo{
{
@@ -793,6 +810,23 @@ var _ = Describe("Propfind", func() {
Expect(string(res.Responses[1].Propstat[0].Prop[0].InnerXML)).To(ContainSubstring("30"))
})
+ It("stats a directory with xml special characters", func() {
+ rr := httptest.NewRecorder()
+ req, err := http.NewRequest("GET", "/dir&dir", strings.NewReader(""))
+ Expect(err).ToNot(HaveOccurred())
+ req = req.WithContext(ctx)
+
+ spaceID := storagespace.FormatResourceID(sprovider.ResourceId{StorageId: "provider-1", SpaceId: "foospace", OpaqueId: "root"})
+ handler.HandleSpacesPropfind(rr, req, spaceID)
+ Expect(rr.Code).To(Equal(http.StatusMultiStatus))
+
+ res, _, err := readResponse(rr.Result().Body)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(len(res.Responses)).To(Equal(2))
+ Expect(string(res.Responses[0].Propstat[0].Prop[0].InnerXML)).To(ContainSubstring("dir&dir"))
+ Expect(string(res.Responses[1].Propstat[0].Prop[0].InnerXML)).To(ContainSubstring("30"))
+ })
+
It("includes all the thingsā¢ when depth is infinity", func() {
rr := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", strings.NewReader(""))