2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-17 19:21:29 +03:00

fix(http): decode URL basic auth credentials (#10825)

* fix(http): decode URL basic auth credentials

* fix(http): guard URL credential decode

* chore: apply small nits

---------

Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
Andrii Furmanets
2026-04-30 19:49:56 +03:00
committed by GitHub
parent 0e8b6bbb54
commit 2a51828213
2 changed files with 56 additions and 2 deletions
+18 -2
View File
@@ -57,6 +57,22 @@ const supportedProtocols = platform.protocols.map((protocol) => {
return protocol + ':';
});
// Node's WHATWG URL parser returns `username` and `password` percent-encoded.
// Decode before composing the `auth` option so credentials such as
// `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
// original value for malformed input so a bad encoding never throws.
const decodeURIComponentSafe = (value) => {
if (!utils.isString(value)) {
return value;
}
try {
return decodeURIComponent(value);
} catch (error) {
return value;
}
};
const flushOnFinish = (stream, [throttled, flush]) => {
stream.on('end', flush).on('error', flush);
@@ -650,8 +666,8 @@ export default isHttpAdapterSupported &&
}
if (!auth && parsed.username) {
const urlUsername = parsed.username;
const urlPassword = parsed.password;
const urlUsername = decodeURIComponentSafe(parsed.username);
const urlPassword = decodeURIComponentSafe(parsed.password);
auth = urlUsername + ':' + urlPassword;
}
+38
View File
@@ -890,6 +890,44 @@ describe('supports http with nodejs', () => {
}
});
it('should decode basic auth credentials from the request URL', async () => {
const server = await startHTTPServer(
(req, res) => {
res.end(req.headers.authorization);
},
{ port: SERVER_PORT }
);
try {
const response = await axios.get(
`http://my%40email.com:pa%24ss@localhost:${server.address().port}/`
);
const base64 = Buffer.from('my@email.com:pa$ss', 'utf8').toString('base64');
assert.strictEqual(response.data, `Basic ${base64}`);
} finally {
await stopHTTPServer(server);
}
});
it('keeps malformed URL credentials percent-encoding and does not throw', async () => {
const server = await startHTTPServer(
(req, res) => {
res.end(req.headers.authorization);
},
{ port: SERVER_PORT }
);
try {
const response = await axios.get(
`http://user%:foo%zz@localhost:${server.address().port}/`
);
const base64 = Buffer.from('user%:foo%zz', 'utf8').toString('base64');
assert.strictEqual(response.data, `Basic ${base64}`);
} finally {
await stopHTTPServer(server);
}
});
it('should support basic auth with a header', async () => {
const server = await startHTTPServer(
(req, res) => {