webdav: escape displayname
Displayname WebDAV property should be XML escaped. With current
implementation a file with name '<.txt' would make the XML
invalid.
Fixes golang/go#17158
Change-Id: Ib3b5376094edc957ed15adf511bd1050ea13d27e
Reviewed-on: https://go-review.googlesource.com/29297
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/webdav/prop.go b/webdav/prop.go
index 1459466..3446871 100644
--- a/webdav/prop.go
+++ b/webdav/prop.go
@@ -5,6 +5,7 @@
package webdav
import (
+ "bytes"
"encoding/xml"
"fmt"
"io"
@@ -333,6 +334,12 @@
return []Propstat{pstat}, nil
}
+func escapeXML(s string) string {
+ var buf bytes.Buffer
+ xml.EscapeText(&buf, []byte(s))
+ return buf.String()
+}
+
func findResourceType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
if fi.IsDir() {
return `<D:collection xmlns:D="DAV:"/>`, nil
@@ -345,7 +352,7 @@
// Hide the real name of a possibly prefixed root directory.
return "", nil
}
- return fi.Name(), nil
+ return escapeXML(fi.Name()), nil
}
func findContentLength(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
diff --git a/webdav/webdav_test.go b/webdav/webdav_test.go
index b068aab..82605cd 100644
--- a/webdav/webdav_test.go
+++ b/webdav/webdav_test.go
@@ -203,47 +203,61 @@
}
func TestFilenameEscape(t *testing.T) {
- re := regexp.MustCompile(`<D:href>([^<]*)</D:href>`)
- do := func(method, urlStr string) (string, error) {
+ hrefRe := regexp.MustCompile(`<D:href>([^<]*)</D:href>`)
+ displayNameRe := regexp.MustCompile(`<D:displayname>([^<]*)</D:displayname>`)
+ do := func(method, urlStr string) (string, string, error) {
req, err := http.NewRequest(method, urlStr, nil)
if err != nil {
- return "", err
+ return "", "", err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
- return "", err
+ return "", "", err
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
- return "", err
+ return "", "", err
}
- m := re.FindStringSubmatch(string(b))
- if len(m) != 2 {
- return "", errors.New("D:href not found")
+ hrefMatch := hrefRe.FindStringSubmatch(string(b))
+ if len(hrefMatch) != 2 {
+ return "", "", errors.New("D:href not found")
+ }
+ displayNameMatch := displayNameRe.FindStringSubmatch(string(b))
+ if len(displayNameMatch) != 2 {
+ return "", "", errors.New("D:displayname not found")
}
- return m[1], nil
+ return hrefMatch[1], displayNameMatch[1], nil
}
testCases := []struct {
- name, want string
+ name, wantHref, wantDisplayName string
}{{
- name: `/foo%bar`,
- want: `/foo%25bar`,
+ name: `/foo%bar`,
+ wantHref: `/foo%25bar`,
+ wantDisplayName: `foo%bar`,
}, {
- name: `/こんにちわ世界`,
- want: `/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%82%8F%E4%B8%96%E7%95%8C`,
+ name: `/こんにちわ世界`,
+ wantHref: `/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%82%8F%E4%B8%96%E7%95%8C`,
+ wantDisplayName: `こんにちわ世界`,
}, {
- name: `/Program Files/`,
- want: `/Program%20Files`,
+ name: `/Program Files/`,
+ wantHref: `/Program%20Files`,
+ wantDisplayName: `Program Files`,
}, {
- name: `/go+lang`,
- want: `/go+lang`,
+ name: `/go+lang`,
+ wantHref: `/go+lang`,
+ wantDisplayName: `go+lang`,
}, {
- name: `/go&lang`,
- want: `/go&lang`,
+ name: `/go&lang`,
+ wantHref: `/go&lang`,
+ wantDisplayName: `go&lang`,
+ }, {
+ name: `/go<lang`,
+ wantHref: `/go%3Clang`,
+ wantDisplayName: `go<lang`,
}}
fs := NewMemFS()
for _, tc := range testCases {
@@ -273,13 +287,16 @@
for _, tc := range testCases {
u.Path = tc.name
- got, err := do("PROPFIND", u.String())
+ gotHref, gotDisplayName, err := do("PROPFIND", u.String())
if err != nil {
t.Errorf("name=%q: PROPFIND: %v", tc.name, err)
continue
}
- if got != tc.want {
- t.Errorf("name=%q: got %q, want %q", tc.name, got, tc.want)
+ if gotHref != tc.wantHref {
+ t.Errorf("name=%q: got href %q, want %q", tc.name, gotHref, tc.wantHref)
+ }
+ if gotDisplayName != tc.wantDisplayName {
+ t.Errorf("name=%q: got dispayname %q, want %q", tc.name, gotDisplayName, tc.wantDisplayName)
}
}
}