Fix WebDAV auth for passwords containing '%' followed by two hex digits

fd812070b6 made _parseURI() decode the URL credentials so that
download() could build its own Basic Auth header from the decoded
values, but request() passed the decoded credentials to xmlhttp.open(),
which percent-decodes them again, so a password like "example%41pass"
was sent as "exampleApass". Re-encode the credentials before passing
them to open().

Fixes #6048
This commit is contained in:
Dan Stillman 2026-09-16 09:53:07 -04:00
parent 3c959e1012
commit 1d4b8bc89a
2 changed files with 24 additions and 1 deletions

View file

@ -294,7 +294,15 @@ Zotero.HTTP = new function () {
if (!options.foreground) {
xmlhttp.mozBackgroundRequest = true;
}
xmlhttp.open(method, url, true, options.username, options.password);
// Necko percent-decodes credentials passed to open(), so encode them to preserve
// literal %HH sequences in the password
xmlhttp.open(
method,
url,
true,
options.username && encodeURIComponent(options.username),
options.password && encodeURIComponent(options.password)
);
// Isolate cookies into a separate jar via userContextId
if (options.userContextId && xmlhttp.setOriginAttributes) {

View file

@ -625,6 +625,21 @@ describe("Zotero.HTTP", function () {
xmlhttp.getResponseHeader("X-Echo-Auth")
);
});
it("should preserve literal %HH sequences in the password in request() and download()", async function () {
let username = "user";
let password = "example%41pass";
let expected = "Basic " + btoa(username + ":" + password);
let url = `http://${username}:${encodeURIComponent(password)}`
+ `@127.0.0.1:${port}/download/auth`;
let xmlhttp = await Zotero.HTTP.request("GET", url);
assert.equal(xmlhttp.getResponseHeader("X-Echo-Auth"), expected);
let dest = PathUtils.join(tmpDir, "auth-percent.bin");
let req = await Zotero.HTTP.download(Services.io.newURI(url), dest);
assert.equal(req.headers.get("X-Echo-Auth"), expected);
});
});