From 17ef5bf57b0c8eeea1d002b51cc6f961a51ccbbf Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 21 Jan 2020 23:35:48 +0100 Subject: [PATCH 1/4] http: decode username and password before encoding Fixes: https://github.com/nodejs/node/issues/31439 --- lib/internal/url.js | 3 ++- test/parallel/test-http-url-username.js | 33 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-http-url-username.js diff --git a/lib/internal/url.js b/lib/internal/url.js index 09fa9f2cf47d50..9c7fadaece6b32 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1284,7 +1284,8 @@ function urlToOptions(url) { options.port = Number(url.port); } if (url.username || url.password) { - options.auth = `${url.username}:${url.password}`; + options.auth = + `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`; } return options; } diff --git a/test/parallel/test-http-url-username.js b/test/parallel/test-http-url-username.js new file mode 100644 index 00000000000000..27820f713a0e53 --- /dev/null +++ b/test/parallel/test-http-url-username.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const MakeDuplexPair = require('../common/duplexpair'); + +// Test that usernames from URLs are URL-decoded, as they should be. + +{ + const url = new URL('http://localhost'); + url.username = 'test@test'; + url.password = '123456'; + + const server = http.createServer( + common.mustCall((req, res) => { + assert.strictEqual( + req.headers.authorization, + 'Basic ' + Buffer.from('test@test:123456').toString('base64')); + res.statusCode = 200; + res.end(); + })); + + const { clientSide, serverSide } = MakeDuplexPair(); + server.emit('connection', serverSide); + + const req = http.request(url, { + createConnection: common.mustCall(() => clientSide) + }, common.mustCall((res) => { + res.resume(); // We don’t actually care about contents. + res.on('end', common.mustCall()); + })); + req.end(); +} From 76ca7362bc7866c49c7a97010e141b4dac2f1f68 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 21 Jan 2020 23:54:29 +0100 Subject: [PATCH 2/4] fixup! http: decode username and password before encoding --- doc/api/url.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/api/url.md b/doc/api/url.md index a198b699cd31d8..179a6e5885e28c 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -470,6 +470,12 @@ the URL, use the [`url.search`][] setter. See [`URLSearchParams`][] documentation for details. #### `url.username` + * {string} diff --git a/lib/internal/url.js b/lib/internal/url.js index 9c7fadaece6b32..3bd9de1a883bb6 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1265,6 +1265,10 @@ function domainToUnicode(domain) { return _domainToUnicode(`${domain}`); } +function decodeAuth(str) { + return decodeURIComponent(str).replace(':', '%3A').replace('/', '%2F'); +} + // Utility function that converts a URL object into an ordinary // options object as expected by the http.request and https.request // APIs. @@ -1284,8 +1288,7 @@ function urlToOptions(url) { options.port = Number(url.port); } if (url.username || url.password) { - options.auth = - `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`; + options.auth = `${decodeAuth(url.username)}:${decodeAuth(url.password)}`; } return options; } From 5ebbb79a3bd56b99821081c23e1f5db9c879fce8 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 23 Jan 2020 22:26:23 +0100 Subject: [PATCH 4/4] fixup! http: decode username and password before encoding --- test/parallel/test-http-url-username.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-http-url-username.js b/test/parallel/test-http-url-username.js index 27820f713a0e53..85956c916bd1a6 100644 --- a/test/parallel/test-http-url-username.js +++ b/test/parallel/test-http-url-username.js @@ -8,14 +8,14 @@ const MakeDuplexPair = require('../common/duplexpair'); { const url = new URL('http://localhost'); - url.username = 'test@test'; - url.password = '123456'; + url.username = 'test@test"'; + url.password = '123456^'; const server = http.createServer( common.mustCall((req, res) => { assert.strictEqual( req.headers.authorization, - 'Basic ' + Buffer.from('test@test:123456').toString('base64')); + 'Basic ' + Buffer.from('test@test":123456^').toString('base64')); res.statusCode = 200; res.end(); }));