1
0
mirror of https://github.com/rclone/rclone.git synced 2026-02-21 03:43:32 +00:00

webdav: escape reserved characters in URL path segments

Use URLPathEscapeAll instead of URLPathEscape for path encoding.

URLPathEscape relies on Go's url.URL.String() which only minimally
escapes paths - reserved sub-delimiter characters like semicolons and
equals signs pass through unescaped. Per RFC 3986 section 3.3, these
characters must be percent-encoded when used as literal values in
path segments.

Some WebDAV servers (notably dCache/Jetty) interpret unescaped
semicolons as path parameter delimiters, which truncates filenames
at the semicolon position. URLPathEscapeAll encodes everything
except [A-Za-z0-9/], which is safe for all servers.

Fixes #9082
This commit is contained in:
Varun Chawla
2026-02-20 08:30:15 -08:00
committed by GitHub
parent c63ecace41
commit fd8b28d36d
2 changed files with 33 additions and 1 deletions

View File

@@ -423,7 +423,7 @@ func (f *Fs) filePath(file string) string {
if f.opt.Enc != encoder.EncodeZero {
subPath = f.opt.Enc.FromStandardPath(subPath)
}
return rest.URLPathEscape(subPath)
return rest.URLPathEscapeAll(subPath)
}
// dirPath returns a directory path (f.root, dir)

View File

@@ -80,3 +80,35 @@ func TestHeaders(t *testing.T) {
_, err := f.Features().About(context.Background())
require.NoError(t, err)
}
// TestReservedCharactersInPathAreEscaped verifies that reserved characters
// like semicolons and equals signs in file paths are percent-encoded in
// HTTP requests to the WebDAV server (RFC 3986 compliance).
func TestReservedCharactersInPathAreEscaped(t *testing.T) {
var capturedPath string
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
capturedPath = r.RequestURI
// Return a 404 so the NewObject call fails cleanly
w.WriteHeader(http.StatusNotFound)
})
ts := httptest.NewServer(handler)
defer ts.Close()
configfile.Install()
m := configmap.Simple{
"type": "webdav",
"url": ts.URL,
}
f, err := webdav.NewFs(context.Background(), remoteName, "", m)
require.NoError(t, err)
// Try to access a file with a semicolon in the name.
// We expect the request to fail (404), but the path should be escaped.
_, _ = f.NewObject(context.Background(), "my;test")
// The semicolon must be percent-encoded as %3B
assert.Contains(t, capturedPath, "my%3Btest", "semicolons in path should be percent-encoded")
assert.NotContains(t, capturedPath, "my;test", "raw semicolons should not appear in path")
}