From 7acbfd72330b835e1ffdad3daf78c222d1256142 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 15 Nov 2017 22:12:54 +0000 Subject: [PATCH] Use a third-party URL parser The `url.parse` from the standard lib provides the username and password as one field, and decodes them. This means any percent-encoded characters are decoded, and it's impossible to tell whether a colon is the delimiter between the username and password, or was encoded and part of one or other. Newer versions of the Node standard library come with an updated URL parser. However, I would like to work with older versions of Node, so I've used a third party parser. --- lib/connect.js | 22 ++++++---------------- package.json | 3 ++- test/connect.js | 16 +++++++++------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/lib/connect.js b/lib/connect.js index c55e0e87..603f1981 100644 --- a/lib/connect.js +++ b/lib/connect.js @@ -6,7 +6,7 @@ 'use strict'; -var URL = require('url'); +var URL = require('url-parse'); var QS = require('querystring'); var Connection = require('./connection').Connection; var fmt = require('util').format; @@ -76,22 +76,12 @@ function openFrames(vhost, query, credentials, extraClientProperties) { }; } -// Decide on credentials based on what we're supplied. Note that in a -// parsed URL, the auth part is already URL-decoded, so e.g., '%3a' in -// the URL is already decoded to ':'. This is a bit unhelpful, as it -// means we can't tell whether a colon is a separator, or part of the -// username. Assume no colons in usernames. +// Decide on credentials based on what we're supplied. function credentialsFromUrl(parts) { var user = 'guest', passwd = 'guest'; - if (parts.auth) { - var colon = parts.auth.indexOf(':') - if (colon == -1) { - user = parts.auth; - passwd = ''; - } else { - user = parts.auth.substring(0, colon); - passwd = parts.auth.substring(colon+1); - } + if (parts.username != '' || parts.password != '') { + user = (parts.username) ? unescape(parts.username) : ''; + passwd = (parts.password) ? unescape(parts.password) : ''; } return credentials.plain(user, passwd); } @@ -137,7 +127,7 @@ function connect(url, socketOptions, openCallback) { fields = openFrames(url.vhost, config, sockopts.credentials || credentials.plain(user, pass), extraClientProperties); } else { - var parts = URL.parse(url, true); // yes, parse the query string + var parts = URL(url, true); // yes, parse the query string protocol = parts.protocol; sockopts.host = parts.hostname; sockopts.port = parseInt(parts.port) || ((protocol === 'amqp:') ? 5672 : 5671); diff --git a/package.json b/package.json index e774f161..ce37690e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "bluebird": "^3.4.6", "buffer-more-ints": "0.0.2", "readable-stream": "1.x >=1.1.9", - "safe-buffer": "^5.0.1" + "safe-buffer": "^5.0.1", + "url-parse": "^1.2.0" }, "devDependencies": { "mocha": "~1", diff --git a/test/connect.js b/test/connect.js index 277c3644..e44c38b6 100644 --- a/test/connect.js +++ b/test/connect.js @@ -13,6 +13,8 @@ var format = require('util').format; var URL = process.env.URL || 'amqp://localhost'; +var urlparse = require('url-parse'); + suite("Credentials", function() { function checkCreds(creds, user, pass, done) { @@ -27,29 +29,29 @@ suite("Credentials", function() { } test("no creds", function(done) { - var parts = {auth: ''}; + var parts = urlparse('amqp://localhost'); var creds = credentialsFromUrl(parts); checkCreds(creds, 'guest', 'guest', done); }); test("usual user:pass", function(done) { - var parts = {auth: 'user:pass'}; + var parts = urlparse('amqp://user:pass@localhost') var creds = credentialsFromUrl(parts); checkCreds(creds, 'user', 'pass', done); }); test("missing user", function(done) { - var parts = {auth: ':password'}; + var parts = urlparse('amqps://:password@localhost'); var creds = credentialsFromUrl(parts); checkCreds(creds, '', 'password', done); }); test("missing password", function(done) { - var parts = {auth: 'username'}; + var parts = urlparse('amqps://username:@localhost'); var creds = credentialsFromUrl(parts); checkCreds(creds, 'username', '', done); }); - test("colon in password", function(done) { - var parts = {auth: 'username:pass:word'}; + test("escaped colons", function(done) { + var parts = urlparse('amqp://user%3Aname:pass%3Aword@localhost') var creds = credentialsFromUrl(parts); - checkCreds(creds, 'username', 'pass:word', done); + checkCreds(creds, 'user:name', 'pass:word', done); }); });