From 8a836942d545ffa44ed139099dfea4f96d336add Mon Sep 17 00:00:00 2001 From: Yan Kalchevskiy Date: Tue, 16 Jul 2013 14:02:24 +0700 Subject: Add support quoted values for SSHConfig (#157) --- tests/test_util.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'tests/test_util.py') diff --git a/tests/test_util.py b/tests/test_util.py index 69c75518..4e67e071 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -333,3 +333,71 @@ IdentityFile something_%l_using_fqdn """ config = paramiko.util.parse_ssh_config(StringIO(test_config)) assert config.lookup('meh') # will die during lookup() if bug regresses + + def test_quoted_host_names(self): + test_config_file = """\ +Host "param pam" param "pam" + Port 1111 + +Host "param2" + Port 2222 + +Host param3 parara + Port 3333 + +Host param4 "p a r" "p" "par" para + Port 4444 +""" + res = { + 'param pam': {'hostname': 'param pam', 'port': '1111'}, + 'param': {'hostname': 'param', 'port': '1111'}, + 'pam': {'hostname': 'pam', 'port': '1111'}, + + 'param2': {'hostname': 'param2', 'port': '2222'}, + + 'param3': {'hostname': 'param3', 'port': '3333'}, + 'parara': {'hostname': 'parara', 'port': '3333'}, + + 'param4': {'hostname': 'param4', 'port': '4444'}, + 'p a r': {'hostname': 'p a r', 'port': '4444'}, + 'p': {'hostname': 'p', 'port': '4444'}, + 'par': {'hostname': 'par', 'port': '4444'}, + 'para': {'hostname': 'para', 'port': '4444'}, + } + f = StringIO(test_config_file) + config = paramiko.util.parse_ssh_config(f) + for host, values in res.items(): + self.assertEquals( + paramiko.util.lookup_ssh_host_config(host, config), + values + ) + + def test_quoted_params_in_config(self): + test_config_file = """\ +Host "param pam" param "pam" + IdentityFile id_rsa + +Host "param2" + IdentityFile "test rsa key" + +Host param3 parara + IdentityFile id_rsa + IdentityFile "test rsa key" +""" + res = { + 'param pam': {'hostname': 'param pam', 'identityfile': ['id_rsa']}, + 'param': {'hostname': 'param', 'identityfile': ['id_rsa']}, + 'pam': {'hostname': 'pam', 'identityfile': ['id_rsa']}, + + 'param2': {'hostname': 'param2', 'identityfile': ['test rsa key']}, + + 'param3': {'hostname': 'param3', 'identityfile': ['id_rsa', 'test rsa key']}, + 'parara': {'hostname': 'parara', 'identityfile': ['id_rsa', 'test rsa key']}, + } + f = StringIO(test_config_file) + config = paramiko.util.parse_ssh_config(f) + for host, values in res.items(): + self.assertEquals( + paramiko.util.lookup_ssh_host_config(host, config), + values + ) -- cgit v1.2.3 From f258d1e207a21d4342dbc431fcc4a4dafe893e80 Mon Sep 17 00:00:00 2001 From: Yan Kalchevksiy Date: Sat, 15 Feb 2014 17:20:31 +0700 Subject: Moved get_hosts function into method. --- paramiko/config.py | 46 +++++++++++++++++++++++++--------------------- tests/test_util.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 21 deletions(-) (limited to 'tests/test_util.py') diff --git a/paramiko/config.py b/paramiko/config.py index dce472ee..5abf181f 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -54,26 +54,6 @@ class SSHConfig (object): :param file file_obj: a file-like object to read the config file from """ - def get_hosts(val): - i, length = 0, len(val) - hosts = [] - while i < length: - if val[i] == '"': - end = val.find('"', i + 1) - if end < 0: - raise Exception("Unparsable host %s" % val) - hosts.append(val[i + 1:end]) - i = end + 1 - elif not val[i].isspace(): - end = i + 1 - while end < length and not val[end].isspace(): - end += 1 - hosts.append(val[i:end]) - i = end + 1 - else: - i += 1 - - return hosts host = {"host": ['*'], "config": {}} for line in file_obj: @@ -90,7 +70,7 @@ class SSHConfig (object): if key == 'host': self._config.append(host) host = { - 'host': get_hosts(value), + 'host': self._get_hosts(value), 'config': {} } else: @@ -222,6 +202,30 @@ class SSHConfig (object): config[k] = config[k].replace(find, str(replace)) return config + def _get_hosts(self, host): + """ + Return a list of host_names from host value. + """ + i, length = 0, len(host) + hosts = [] + while i < length: + if host[i] == '"': + end = host.find('"', i + 1) + if end < 0: + raise Exception("Unparsable host %s" % host) + hosts.append(host[i + 1:end]) + i = end + 1 + elif not host[i].isspace(): + end = i + 1 + while end < length and not host[end].isspace() and host[end] != '"': + end += 1 + hosts.append(host[i:end]) + i = end + else: + i += 1 + + return hosts + class LazyFqdn(object): """ diff --git a/tests/test_util.py b/tests/test_util.py index 4e67e071..8142d416 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -401,3 +401,35 @@ Host param3 parara paramiko.util.lookup_ssh_host_config(host, config), values ) + + def test_quoted_host_in_config(self): + conf = SSHConfig() + correct_data = { + 'param': ['param'], + '"param"': ['param'], + + 'param pam': ['param', 'pam'], + '"param" "pam"': ['param', 'pam'], + '"param" pam': ['param', 'pam'], + 'param "pam"': ['param', 'pam'], + + 'param "pam" p': ['param', 'pam', 'p'], + '"param" pam "p"': ['param', 'pam', 'p'], + + '"pa ram"': ['pa ram'], + '"pa ram" pam': ['pa ram', 'pam'], + 'param "p a m"': ['param', 'p a m'], + } + incorrect_data = [ + 'param"', + '"param', + 'param "pam', + 'param "pam" "p a', + ] + for host, values in correct_data.items(): + self.assertEquals( + conf._get_hosts(host), + values + ) + for host in incorrect_data: + self.assertRaises(Exception, conf._get_hosts, host) -- cgit v1.2.3 From 2dec0a7671d83f21a290e1a2b3ea9159da45757a Mon Sep 17 00:00:00 2001 From: Olle Lundberg Date: Thu, 14 Aug 2014 15:13:58 +0200 Subject: Add a utility method for value clamping. --- paramiko/util.py | 3 +++ tests/test_util.py | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'tests/test_util.py') diff --git a/paramiko/util.py b/paramiko/util.py index f4ee3adc..d029f52e 100644 --- a/paramiko/util.py +++ b/paramiko/util.py @@ -320,3 +320,6 @@ def constant_time_bytes_eq(a, b): for i in (xrange if PY2 else range)(len(a)): res |= byte_ord(a[i]) ^ byte_ord(b[i]) return res == 0 + +def clamp_value(minimum, val, maximum): + return max(minimum, min(val, maximum)) diff --git a/tests/test_util.py b/tests/test_util.py index 69c75518..24f58076 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -333,3 +333,8 @@ IdentityFile something_%l_using_fqdn """ config = paramiko.util.parse_ssh_config(StringIO(test_config)) assert config.lookup('meh') # will die during lookup() if bug regresses + + def test_12_clamp_value(self): + self.assertEqual(32768, paramiko.util.clamp_value(32767, 32768, 32769)) + self.assertEqual(32767, paramiko.util.clamp_value(32767, 32765, 32769)) + self.assertEqual(32769, paramiko.util.clamp_value(32767, 32770, 32769)) -- cgit v1.2.3 From 163caabf5e4a2a63216c2192dc476d4184ab81b3 Mon Sep 17 00:00:00 2001 From: Olle Lundberg Date: Fri, 15 Aug 2014 15:33:25 +0200 Subject: Remove all occurences of ParamikoTest. Sorry paramiko, it's time to put on the big boy pants. You no longer support old as hell versions of python. --- tests/test_buffered_pipe.py | 5 ++--- tests/test_transport.py | 5 +++-- tests/test_util.py | 5 ++--- tests/util.py | 10 ---------- 4 files changed, 7 insertions(+), 18 deletions(-) (limited to 'tests/test_util.py') diff --git a/tests/test_buffered_pipe.py b/tests/test_buffered_pipe.py index 1045a925..3b8f97ad 100644 --- a/tests/test_buffered_pipe.py +++ b/tests/test_buffered_pipe.py @@ -22,11 +22,10 @@ Some unit tests for BufferedPipe. import threading import time +import unittest from paramiko.buffered_pipe import BufferedPipe, PipeTimeout from paramiko import pipe -from tests.util import ParamikoTest - def delay_thread(p): p.feed('a') @@ -40,7 +39,7 @@ def close_thread(p): p.close() -class BufferedPipeTest(ParamikoTest): +class BufferedPipeTest(unittest.TestCase): def test_1_buffered_pipe(self): p = BufferedPipe() self.assertTrue(not p.read_ready()) diff --git a/tests/test_transport.py b/tests/test_transport.py index ac744b26..5c77a321 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -26,6 +26,7 @@ import socket import time import threading import random +import unittest from paramiko import Transport, SecurityOptions, ServerInterface, RSAKey, DSSKey, \ SSHException, ChannelException @@ -35,7 +36,7 @@ from paramiko.common import MSG_KEXINIT, cMSG_CHANNEL_WINDOW_ADJUST from paramiko.py3compat import bytes from paramiko.message import Message from tests.loop import LoopSocket -from tests.util import ParamikoTest, test_path +from tests.util import test_path LONG_BANNER = """\ @@ -105,7 +106,7 @@ class NullServer (ServerInterface): return OPEN_SUCCEEDED -class TransportTest(ParamikoTest): +class TransportTest(unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() diff --git a/tests/test_util.py b/tests/test_util.py index 69c75518..643d9171 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -24,13 +24,12 @@ from binascii import hexlify import errno import os from hashlib import sha1 +import unittest import paramiko.util from paramiko.util import lookup_ssh_host_config as host_config from paramiko.py3compat import StringIO, byte_ord -from tests.util import ParamikoTest - test_config_file = """\ Host * User robey @@ -60,7 +59,7 @@ BGQ3GQ/Fc7SX6gkpXkwcZryoi4kNFhHu5LvHcZPdxXV1D+uTMfGS1eyd2Yz/DoNWXNAl8TI0cAsW\ from paramiko import * -class UtilTest(ParamikoTest): +class UtilTest(unittest.TestCase): def test_1_import(self): """ verify that all the classes can be imported from paramiko. diff --git a/tests/util.py b/tests/util.py index 66d2696c..b546a7e1 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,17 +1,7 @@ import os -import unittest root_path = os.path.dirname(os.path.realpath(__file__)) - -class ParamikoTest(unittest.TestCase): - # for Python 2.3 and below - if not hasattr(unittest.TestCase, 'assertTrue'): - assertTrue = unittest.TestCase.failUnless - if not hasattr(unittest.TestCase, 'assertFalse'): - assertFalse = unittest.TestCase.failIf - - def test_path(filename): return os.path.join(root_path, filename) -- cgit v1.2.3 From d05ef512bab1849e4fecafbc2a0c23465bf6b2c6 Mon Sep 17 00:00:00 2001 From: Olle Lundberg Date: Fri, 15 Aug 2014 15:41:49 +0200 Subject: Don't end a line with whitespace. This might be stripped by editors at will, which will make some tests brake. --- tests/test_util.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tests/test_util.py') diff --git a/tests/test_util.py b/tests/test_util.py index 69c75518..28c162e5 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -41,7 +41,12 @@ Host *.example.com \tUser bjork Port=3333 Host * - \t \t Crazy something dumb +""" + +dont_strip_whitespace_please = "\t \t Crazy something dumb " + +test_config_file += dont_strip_whitespace_please +test_config_file += """ Host spoo.example.com Crazy something else """ -- cgit v1.2.3 From 12e752d11aa22ee6ba959115725e386ca779c1cb Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Mon, 25 Aug 2014 22:44:54 -0700 Subject: Rework re #239 to work off post-1.13 codebase. Closes #239 --- paramiko/config.py | 2 +- sites/www/changelog.rst | 2 ++ tests/test_util.py | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'tests/test_util.py') diff --git a/paramiko/config.py b/paramiko/config.py index 77fa13d7..f650ee24 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -55,7 +55,7 @@ class SSHConfig (object): """ host = {"host": ['*'], "config": {}} for line in file_obj: - line = line.rstrip('\n').lstrip() + line = line.rstrip('\r\n').lstrip() if (line == '') or (line[0] == '#'): continue if '=' in line: diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index cdd513f4..903e6378 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,6 +2,8 @@ Changelog ========= +* :bug:`239` Add Windows-style CRLF support to SSH config file parsing. Props + to Christopher Swenson. * :support:`229` Fix a couple of incorrectly-copied docstrings' ``.. versionadded::`` RST directives. Thanks to Aarni Koskela for the catch. * :support:`169 backported` Minor refactor of diff --git a/tests/test_util.py b/tests/test_util.py index 6bde4045..ecf8db72 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -338,3 +338,9 @@ IdentityFile something_%l_using_fqdn """ config = paramiko.util.parse_ssh_config(StringIO(test_config)) assert config.lookup('meh') # will die during lookup() if bug regresses + + def test_13_config_dos_crlf_succeeds(self): + config_file = StringIO("host abcqwerty\r\nHostName 127.0.0.1\r\n") + config = paramiko.SSHConfig() + config.parse(config_file) + self.assertEqual(config.lookup("abcqwerty")["hostname"], "127.0.0.1") -- cgit v1.2.3 From 8e2d4321509cf942c3d499b643b1978b1addaebf Mon Sep 17 00:00:00 2001 From: Søren Løvborg Date: Fri, 19 Sep 2014 15:00:18 +0200 Subject: SSHConfig.get_hostnames: List literal hostnames from SSH config --- paramiko/config.py | 10 ++++++++++ tests/test_util.py | 5 +++++ 2 files changed, 15 insertions(+) (limited to 'tests/test_util.py') diff --git a/paramiko/config.py b/paramiko/config.py index 20ca4aa7..4972c27b 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -126,6 +126,16 @@ class SSHConfig (object): ret = self._expand_variables(ret, hostname) return ret + def get_hostnames(self): + """ + Return the set of literal hostnames defined in the SSH config (both + explicit hostnames and wildcard entries). + """ + hosts = set() + for entry in self._config: + hosts.update(entry['host']) + return hosts + def _allowed(self, hosts, hostname): match = False for host in hosts: diff --git a/tests/test_util.py b/tests/test_util.py index 35e15765..f961fbbc 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -349,6 +349,11 @@ IdentityFile something_%l_using_fqdn config.parse(config_file) self.assertEqual(config.lookup("abcqwerty")["hostname"], "127.0.0.1") + def test_14_get_hostnames(self): + f = StringIO(test_config_file) + config = paramiko.util.parse_ssh_config(f) + self.assertEqual(config.get_hostnames(), set(['*', '*.example.com', 'spoo.example.com'])) + def test_quoted_host_names(self): test_config_file = """\ Host "param pam" param "pam" -- cgit v1.2.3 From a08149d5f7aea1fcce55223a000ce9018df02961 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Wed, 12 Nov 2014 13:49:03 -0800 Subject: Failing test proving #429 --- tests/test_util.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'tests/test_util.py') diff --git a/tests/test_util.py b/tests/test_util.py index ecf8db72..0e7d0b2b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -25,8 +25,8 @@ import errno import os from Crypto.Hash import SHA import paramiko.util -from paramiko.util import lookup_ssh_host_config as host_config -from paramiko.py3compat import StringIO, byte_ord +from paramiko.util import lookup_ssh_host_config as host_config, safe_string +from paramiko.py3compat import StringIO, byte_ord, b from tests.util import ParamikoTest @@ -344,3 +344,14 @@ IdentityFile something_%l_using_fqdn config = paramiko.SSHConfig() config.parse(config_file) self.assertEqual(config.lookup("abcqwerty")["hostname"], "127.0.0.1") + + def test_safe_string(self): + vanilla = b("vanilla") + has_bytes = b("has \7\3 bytes") + safe_vanilla = safe_string(vanilla) + safe_has_bytes = safe_string(has_bytes) + expected_bytes = b("has %07%03 bytes") + err = "{0!r} != {1!r}" + assert safe_vanilla == vanilla, err.format(safe_vanilla, vanilla) + assert safe_has_bytes == expected_bytes, \ + err.format(safe_has_bytes, expected_bytes) -- cgit v1.2.3