Skip to content

Commit f11d712

Browse files
banceknigeltao
authored andcommitted
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>
1 parent 41c5c5c commit f11d712

File tree

2 files changed

+48
-24
lines changed

2 files changed

+48
-24
lines changed

webdav/prop.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package webdav
66

77
import (
8+
"bytes"
89
"encoding/xml"
910
"fmt"
1011
"io"
@@ -333,6 +334,12 @@ loop:
333334
return []Propstat{pstat}, nil
334335
}
335336

337+
func escapeXML(s string) string {
338+
var buf bytes.Buffer
339+
xml.EscapeText(&buf, []byte(s))
340+
return buf.String()
341+
}
342+
336343
func findResourceType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
337344
if fi.IsDir() {
338345
return `<D:collection xmlns:D="DAV:"/>`, nil
@@ -345,7 +352,7 @@ func findDisplayName(fs FileSystem, ls LockSystem, name string, fi os.FileInfo)
345352
// Hide the real name of a possibly prefixed root directory.
346353
return "", nil
347354
}
348-
return fi.Name(), nil
355+
return escapeXML(fi.Name()), nil
349356
}
350357

351358
func findContentLength(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {

webdav/webdav_test.go

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -203,47 +203,61 @@ func TestPrefix(t *testing.T) {
203203
}
204204

205205
func TestFilenameEscape(t *testing.T) {
206-
re := regexp.MustCompile(`<D:href>([^<]*)</D:href>`)
207-
do := func(method, urlStr string) (string, error) {
206+
hrefRe := regexp.MustCompile(`<D:href>([^<]*)</D:href>`)
207+
displayNameRe := regexp.MustCompile(`<D:displayname>([^<]*)</D:displayname>`)
208+
do := func(method, urlStr string) (string, string, error) {
208209
req, err := http.NewRequest(method, urlStr, nil)
209210
if err != nil {
210-
return "", err
211+
return "", "", err
211212
}
212213
res, err := http.DefaultClient.Do(req)
213214
if err != nil {
214-
return "", err
215+
return "", "", err
215216
}
216217
defer res.Body.Close()
217218

218219
b, err := ioutil.ReadAll(res.Body)
219220
if err != nil {
220-
return "", err
221+
return "", "", err
221222
}
222-
m := re.FindStringSubmatch(string(b))
223-
if len(m) != 2 {
224-
return "", errors.New("D:href not found")
223+
hrefMatch := hrefRe.FindStringSubmatch(string(b))
224+
if len(hrefMatch) != 2 {
225+
return "", "", errors.New("D:href not found")
226+
}
227+
displayNameMatch := displayNameRe.FindStringSubmatch(string(b))
228+
if len(displayNameMatch) != 2 {
229+
return "", "", errors.New("D:displayname not found")
225230
}
226231

227-
return m[1], nil
232+
return hrefMatch[1], displayNameMatch[1], nil
228233
}
229234

230235
testCases := []struct {
231-
name, want string
236+
name, wantHref, wantDisplayName string
232237
}{{
233-
name: `/foo%bar`,
234-
want: `/foo%25bar`,
238+
name: `/foo%bar`,
239+
wantHref: `/foo%25bar`,
240+
wantDisplayName: `foo%bar`,
241+
}, {
242+
name: `/こんにちわ世界`,
243+
wantHref: `/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%82%8F%E4%B8%96%E7%95%8C`,
244+
wantDisplayName: `こんにちわ世界`,
235245
}, {
236-
name: `/こんにちわ世界`,
237-
want: `/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%82%8F%E4%B8%96%E7%95%8C`,
246+
name: `/Program Files/`,
247+
wantHref: `/Program%20Files`,
248+
wantDisplayName: `Program Files`,
238249
}, {
239-
name: `/Program Files/`,
240-
want: `/Program%20Files`,
250+
name: `/go+lang`,
251+
wantHref: `/go+lang`,
252+
wantDisplayName: `go+lang`,
241253
}, {
242-
name: `/go+lang`,
243-
want: `/go+lang`,
254+
name: `/go&lang`,
255+
wantHref: `/go&amp;lang`,
256+
wantDisplayName: `go&amp;lang`,
244257
}, {
245-
name: `/go&lang`,
246-
want: `/go&amp;lang`,
258+
name: `/go<lang`,
259+
wantHref: `/go%3Clang`,
260+
wantDisplayName: `go&lt;lang`,
247261
}}
248262
fs := NewMemFS()
249263
for _, tc := range testCases {
@@ -273,13 +287,16 @@ func TestFilenameEscape(t *testing.T) {
273287

274288
for _, tc := range testCases {
275289
u.Path = tc.name
276-
got, err := do("PROPFIND", u.String())
290+
gotHref, gotDisplayName, err := do("PROPFIND", u.String())
277291
if err != nil {
278292
t.Errorf("name=%q: PROPFIND: %v", tc.name, err)
279293
continue
280294
}
281-
if got != tc.want {
282-
t.Errorf("name=%q: got %q, want %q", tc.name, got, tc.want)
295+
if gotHref != tc.wantHref {
296+
t.Errorf("name=%q: got href %q, want %q", tc.name, gotHref, tc.wantHref)
297+
}
298+
if gotDisplayName != tc.wantDisplayName {
299+
t.Errorf("name=%q: got dispayname %q, want %q", tc.name, gotDisplayName, tc.wantDisplayName)
283300
}
284301
}
285302
}

0 commit comments

Comments
 (0)