diff --git a/backend/webdav/webdav.go b/backend/webdav/webdav.go index bc6a0241f..09746f2b6 100644 --- a/backend/webdav/webdav.go +++ b/backend/webdav/webdav.go @@ -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) diff --git a/backend/webdav/webdav_internal_test.go b/backend/webdav/webdav_internal_test.go index c2c2d1dfa..ee6cd7116 100644 --- a/backend/webdav/webdav_internal_test.go +++ b/backend/webdav/webdav_internal_test.go @@ -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") +}