From 957546553048773b7ad2edd3eb199a95dee4a43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fred=20S=C3=B6derberg?= Date: Mon, 9 Mar 2026 10:55:38 +0100 Subject: [PATCH] fix: URL-encode special characters in credentials for XMLRPC URI in nipap-cli. Passwords (and usernames) containing special characters such as '/', '@' or ':' would break the XMLRPC URI used by nipap-cli because urllib.parse.quote() defaults to safe='/', leaving '/' unencoded. --- nipap-cli/nipap_cli/nipap_cli.py | 4 ++-- nipap-cli/tests/cli-test.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/nipap-cli/nipap_cli/nipap_cli.py b/nipap-cli/nipap_cli/nipap_cli.py index ab3cf31f9..71fb8300b 100755 --- a/nipap-cli/nipap_cli/nipap_cli.py +++ b/nipap-cli/nipap_cli/nipap_cli.py @@ -87,8 +87,8 @@ def setup_connection(): con_params['password'] = getpass.getpass() # Quote username & password - con_params['username'] = quote(con_params['username']) - con_params['password'] = quote(con_params['password']) + con_params['username'] = quote(con_params['username'], safe='') + con_params['password'] = quote(con_params['password'], safe='') # build XML-RPC URI pynipap.xmlrpc_uri = "%(protocol)s://%(username)s:%(password)s@%(hostname)s:%(port)s" % con_params diff --git a/nipap-cli/tests/cli-test.py b/nipap-cli/tests/cli-test.py index b968c1c4e..025358c22 100755 --- a/nipap-cli/tests/cli-test.py +++ b/nipap-cli/tests/cli-test.py @@ -4,6 +4,7 @@ # import unittest +from urllib.parse import quote, urlparse import sys sys.path.insert(0, '..') @@ -297,5 +298,35 @@ def test_rest_argument(self): ) +class TestPasswordEncoding(unittest.TestCase): + + def _build_uri(self, username, password): + return "http://%(username)s:%(password)s@localhost:1337" % { + 'username': quote(username, safe=''), + 'password': quote(password, safe=''), + } + + def test_slash_in_password(self): + uri = self._build_uri('admin', 'pass/word') + parsed = urlparse(uri) + self.assertEqual(parsed.username, 'admin') + self.assertEqual(parsed.password, 'pass/word') + + def test_at_in_password(self): + uri = self._build_uri('admin', 'p@ssword') + parsed = urlparse(uri) + self.assertEqual(parsed.password, 'p@ssword') + + def test_colon_in_password(self): + uri = self._build_uri('admin', 'pass:word') + parsed = urlparse(uri) + self.assertEqual(parsed.password, 'pass:word') + + def test_multiple_special_chars(self): + uri = self._build_uri('user/name', 'p@ss/w:rd!') + parsed = urlparse(uri) + self.assertEqual(parsed.username, 'user/name') + self.assertEqual(parsed.password, 'p@ss/w:rd!') + if __name__ == '__main__': unittest.main()