mirror of
				https://github.com/jtesta/ssh-audit.git
				synced 2025-10-30 21:15:27 +01:00 
			
		
		
		
	Flake8 fixes (#35)
* Apply Flake8 also on `setup.py` modified: tox.ini * Fix W605 - invalid escape syntax modified: packages/setup.py modified: tox.ini * Update comment about Flake8: W504 W503 and W504 are mutual exclusive - so we have to keep one of them. modified: tox.ini * Fix F841 - variable assigned but never used modified: ssh-audit.py modified: tox.ini * Fix E741 - ambiguous variable name 'l' modified: ssh-audit.py modified: tox.ini * Fix E712 - comparison to False should be 'if cond is False' ... and not 'if conf == False'. modified: ssh-audit.py modified: tox.ini * Fix E711 - comparison to None should be 'if cond is not None' ... and not 'if cond != None'. modified: ssh-audit.py modified: tox.ini * Fix E305 - expected 2 blank lines ... after class or function definition, found 1. modified: ssh-audit.py modified: tox.ini * Fix E303 - too many blank lines modified: ssh-audit.py modified: tox.ini * Fix E303 - too many blank lines modified: ssh-audit.py modified: tox.ini * Fix E301 - expected 1 blank line, found 0 No code change necessary, probably fixed by another commit. modified: tox.ini * Fix E265 - block comment should start with '# ' There is lots of commented out code, which usually should be just deleted. I will keep it for now, as I am not yet very familiar with the code base. modified: ssh-audit.py modified: tox.ini * Fix E261 - at least two spaces before inline comment modified: ssh-audit.py modified: tox.ini * Fix E251 - unexpected spaces around keyword / parameter equals modified: packages/setup.py modified: tox.ini * Fix E231 - missing whitespace after ',' No code change necessary, probably fixed by previous commit. modified: tox.ini * Fix E226 - missing whitespace around arithmetic operator modified: ssh-audit.py modified: tox.ini * Fix W293 - blank line contains whitespace modified: ssh-audit.py modified: tox.ini * Fix E221 - multiple spaces before operator modified: ssh-audit.py modified: tox.ini * Update comment about Flake 8 E241 Lots of data is formatted as tables, so this warning is disabled for a good reason. modified: tox.ini * Fix E401 - multiple imports on one line modified: ssh-audit.py modified: tox.ini * Do not ignore Flake8 warning F401 ... as there were no errors in source code anyway. modified: tox.ini * Fix F821 - undefined name modified: ssh-audit.py modified: tox.ini * Reformat ignore section for Flake8 modified: tox.ini * Flake8 test suite modified: test/conftest.py modified: test/test_auditconf.py modified: test/test_banner.py modified: test/test_buffer.py modified: test/test_errors.py modified: test/test_output.py modified: test/test_resolve.py modified: test/test_socket.py modified: test/test_software.py modified: test/test_ssh1.py modified: test/test_ssh2.py modified: test/test_ssh_algorithm.py modified: test/test_utils.py modified: test/test_version_compare.py modified: tox.ini
This commit is contained in:
		| @@ -5,7 +5,7 @@ import re | |||||||
| from setuptools import setup | from setuptools import setup | ||||||
|  |  | ||||||
|  |  | ||||||
| version = re.search('^VERSION\s*=\s*\'v(\d\.\d\.\d)\'', open('sshaudit/sshaudit.py').read(), re.M).group(1) | version = re.search(r'^VERSION\s*=\s*\'v(\d\.\d\.\d)\'', open('sshaudit/sshaudit.py').read(), re.M).group(1) | ||||||
| print("\n\nPackaging ssh-audit v%s...\n\n" % version) | print("\n\nPackaging ssh-audit v%s...\n\n" % version) | ||||||
|  |  | ||||||
| with open("sshaudit/README.md", "rb") as f: | with open("sshaudit/README.md", "rb") as f: | ||||||
| @@ -13,20 +13,20 @@ with open("sshaudit/README.md", "rb") as f: | |||||||
|  |  | ||||||
|  |  | ||||||
| setup( | setup( | ||||||
|     name = "ssh-audit", |     name="ssh-audit", | ||||||
|     packages = ["sshaudit"], |     packages=["sshaudit"], | ||||||
|     license = 'MIT', |     license='MIT', | ||||||
|     entry_points = { |     entry_points={ | ||||||
|         "console_scripts": ['ssh-audit = sshaudit.sshaudit:main'] |         "console_scripts": ['ssh-audit = sshaudit.sshaudit:main'] | ||||||
|     }, |     }, | ||||||
|     version = version, |     version=version, | ||||||
|     description = "An SSH server & client configuration security auditing tool", |     description="An SSH server & client configuration security auditing tool", | ||||||
|     long_description = long_descr, |     long_description=long_descr, | ||||||
|     long_description_content_type = "text/markdown", |     long_description_content_type="text/markdown", | ||||||
|     author = "Joe Testa", |     author="Joe Testa", | ||||||
|     author_email = "jtesta@positronsecurity.com", |     author_email="jtesta@positronsecurity.com", | ||||||
|     url = "https://github.com/jtesta/ssh-audit", |     url="https://github.com/jtesta/ssh-audit", | ||||||
|     classifiers = [ |     classifiers=[ | ||||||
|         "Development Status :: 5 - Production/Stable", |         "Development Status :: 5 - Production/Stable", | ||||||
|         "Intended Audience :: Information Technology", |         "Intended Audience :: Information Technology", | ||||||
|         "Intended Audience :: System Administrators", |         "Intended Audience :: System Administrators", | ||||||
|   | |||||||
							
								
								
									
										104
									
								
								ssh-audit.py
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								ssh-audit.py
									
									
									
									
									
								
							| @@ -25,7 +25,20 @@ | |||||||
|    THE SOFTWARE. |    THE SOFTWARE. | ||||||
| """ | """ | ||||||
| from __future__ import print_function | from __future__ import print_function | ||||||
| import base64, binascii, errno, hashlib, getopt, io, os, random, re, select, socket, struct, sys, json | import base64 | ||||||
|  | import binascii | ||||||
|  | import errno | ||||||
|  | import getopt | ||||||
|  | import hashlib | ||||||
|  | import io | ||||||
|  | import json | ||||||
|  | import os | ||||||
|  | import random | ||||||
|  | import re | ||||||
|  | import select | ||||||
|  | import socket | ||||||
|  | import struct | ||||||
|  | import sys | ||||||
|  |  | ||||||
|  |  | ||||||
| VERSION = 'v2.2.1-dev' | VERSION = 'v2.2.1-dev' | ||||||
| @@ -41,7 +54,7 @@ if sys.version_info >= (3,):  # pragma: nocover | |||||||
| else:  # pragma: nocover | else:  # pragma: nocover | ||||||
| 	import StringIO as _StringIO  # pylint: disable=import-error | 	import StringIO as _StringIO  # pylint: disable=import-error | ||||||
| 	StringIO = BytesIO = _StringIO.StringIO | 	StringIO = BytesIO = _StringIO.StringIO | ||||||
| 	text_type = unicode  # pylint: disable=undefined-variable | 	text_type = unicode  # pylint: disable=undefined-variable  # noqa: F821 | ||||||
| 	binary_type = str | 	binary_type = str | ||||||
| try:  # pragma: nocover | try:  # pragma: nocover | ||||||
| 	# pylint: disable=unused-import | 	# pylint: disable=unused-import | ||||||
| @@ -190,9 +203,9 @@ class AuditConf(object): | |||||||
| 			elif o in ('-t', '--timeout'): | 			elif o in ('-t', '--timeout'): | ||||||
| 				aconf.timeout = float(a) | 				aconf.timeout = float(a) | ||||||
| 				aconf.timeout_set = True | 				aconf.timeout_set = True | ||||||
| 		if len(args) == 0 and aconf.client_audit == False: | 		if len(args) == 0 and aconf.client_audit is False: | ||||||
| 			usage_cb() | 			usage_cb() | ||||||
| 		if aconf.client_audit == False: | 		if aconf.client_audit is False: | ||||||
| 			if oport is not None: | 			if oport is not None: | ||||||
| 				host = args[0] | 				host = args[0] | ||||||
| 			else: | 			else: | ||||||
| @@ -700,7 +713,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | |||||||
| 				'ecdh-sha2-nistp256': KexNISTP256, | 				'ecdh-sha2-nistp256': KexNISTP256, | ||||||
| 				'ecdh-sha2-nistp384': KexNISTP384, | 				'ecdh-sha2-nistp384': KexNISTP384, | ||||||
| 				'ecdh-sha2-nistp521': KexNISTP521, | 				'ecdh-sha2-nistp521': KexNISTP521, | ||||||
| 				#'kexguess2@matt.ucc.asn.au': ??? | 				# 'kexguess2@matt.ucc.asn.au': ??? | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			# Pick the first kex algorithm that the server supports, which we | 			# Pick the first kex algorithm that the server supports, which we | ||||||
| @@ -798,7 +811,6 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | |||||||
| 					else: | 					else: | ||||||
| 						host_key_types[host_key_type]['parsed'] = True | 						host_key_types[host_key_type]['parsed'] = True | ||||||
|  |  | ||||||
|  |  | ||||||
| 	# Performs DH group exchanges to find what moduli are supported, and checks | 	# Performs DH group exchanges to find what moduli are supported, and checks | ||||||
| 	# their size. | 	# their size. | ||||||
| 	class GEXTest(object): | 	class GEXTest(object): | ||||||
| @@ -865,7 +877,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | |||||||
| 						# got here, doesn't mean the server is vulnerable... | 						# got here, doesn't mean the server is vulnerable... | ||||||
| 						smallest_modulus = kex_group.get_dh_modulus_size() | 						smallest_modulus = kex_group.get_dh_modulus_size() | ||||||
|  |  | ||||||
| 					except Exception as e: # pylint: disable=bare-except | 					except Exception:  # pylint: disable=bare-except | ||||||
| 						pass | 						pass | ||||||
| 					finally: | 					finally: | ||||||
| 						s.close() | 						s.close() | ||||||
| @@ -887,9 +899,9 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | |||||||
| 							kex_group.send_init_gex(s, bits, bits, bits) | 							kex_group.send_init_gex(s, bits, bits, bits) | ||||||
| 							kex_group.recv_reply(s, False) | 							kex_group.recv_reply(s, False) | ||||||
| 							smallest_modulus = kex_group.get_dh_modulus_size() | 							smallest_modulus = kex_group.get_dh_modulus_size() | ||||||
| 						except Exception as e: # pylint: disable=bare-except | 						except Exception:  # pylint: disable=bare-except | ||||||
| 							#import traceback | 							# import traceback | ||||||
| 							#print(traceback.format_exc()) | 							# print(traceback.format_exc()) | ||||||
| 							pass | 							pass | ||||||
| 						finally: | 						finally: | ||||||
| 							# The server is in a state that is not re-testable, | 							# The server is in a state that is not re-testable, | ||||||
| @@ -897,7 +909,6 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | |||||||
| 							# connection. | 							# connection. | ||||||
| 							s.close() | 							s.close() | ||||||
|  |  | ||||||
|  |  | ||||||
| 					if smallest_modulus > 0: | 					if smallest_modulus > 0: | ||||||
| 						kex.set_dh_modulus_size(gex_alg, smallest_modulus) | 						kex.set_dh_modulus_size(gex_alg, smallest_modulus) | ||||||
|  |  | ||||||
| @@ -919,6 +930,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | |||||||
| 					if reconnect_failed: | 					if reconnect_failed: | ||||||
| 						break | 						break | ||||||
|  |  | ||||||
|  |  | ||||||
| class SSH1(object): | class SSH1(object): | ||||||
| 	class CRC32(object): | 	class CRC32(object): | ||||||
| 		def __init__(self): | 		def __init__(self): | ||||||
| @@ -935,8 +947,8 @@ class SSH1(object): | |||||||
|  |  | ||||||
| 		def calc(self, v): | 		def calc(self, v): | ||||||
| 			# type: (binary_type) -> int | 			# type: (binary_type) -> int | ||||||
| 			crc, l = 0, len(v) | 			crc, length = 0, len(v) | ||||||
| 			for i in range(l): | 			for i in range(length): | ||||||
| 				n = ord(v[i:i + 1]) | 				n = ord(v[i:i + 1]) | ||||||
| 				n = n ^ (crc & 0xff) | 				n = n ^ (crc & 0xff) | ||||||
| 				crc = (crc >> 8) ^ self._table[n] | 				crc = (crc >> 8) ^ self._table[n] | ||||||
| @@ -1189,6 +1201,7 @@ class ReadBuf(object): | |||||||
| 		self._len = 0 | 		self._len = 0 | ||||||
| 		super(ReadBuf, self).reset() | 		super(ReadBuf, self).reset() | ||||||
|  |  | ||||||
|  |  | ||||||
| class WriteBuf(object): | class WriteBuf(object): | ||||||
| 	def __init__(self, data=None): | 	def __init__(self, data=None): | ||||||
| 		# type: (Optional[binary_type]) -> None | 		# type: (Optional[binary_type]) -> None | ||||||
| @@ -1609,7 +1622,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
|  |  | ||||||
| 			def __getitem__(self, product): | 			def __getitem__(self, product): | ||||||
| 				# type: (str) -> Sequence[Optional[str]] | 				# type: (str) -> Sequence[Optional[str]] | ||||||
| 				return tuple(self.__storage.get(product, [None]*4)) | 				return tuple(self.__storage.get(product, [None] * 4)) | ||||||
|  |  | ||||||
| 			def __str__(self): | 			def __str__(self): | ||||||
| 				# type: () -> str | 				# type: () -> str | ||||||
| @@ -1640,7 +1653,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 					ssh_versions[ssh_prod] = ssh_ver | 					ssh_versions[ssh_prod] = ssh_ver | ||||||
| 				for ssh_product, ssh_version in ssh_versions.items(): | 				for ssh_product, ssh_version in ssh_versions.items(): | ||||||
| 					if ssh_product not in self.__storage: | 					if ssh_product not in self.__storage: | ||||||
| 						self.__storage[ssh_product] = [None]*4 | 						self.__storage[ssh_product] = [None] * 4 | ||||||
| 					prev = self[ssh_product][pos] | 					prev = self[ssh_product][pos] | ||||||
| 					if (prev is None or | 					if (prev is None or | ||||||
| 					   (prev < ssh_version and pos % 2 == 0) or | 					   (prev < ssh_version and pos % 2 == 0) or | ||||||
| @@ -1783,19 +1796,18 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 			if software is not None: | 			if software is not None: | ||||||
| 				if software.product not in vproducts: | 				if software.product not in vproducts: | ||||||
| 					unknown_software = True | 					unknown_software = True | ||||||
| # |  | ||||||
| # The code below is commented out because it would try to guess what the server is, | 			# The code below is commented out because it would try to guess what the server is, | ||||||
| # usually resulting in wild & incorrect recommendations. | 			# usually resulting in wild & incorrect recommendations. | ||||||
| # | 			# if software is None: | ||||||
| #			if software is None: | 			# 	ssh_timeframe = self.get_ssh_timeframe(for_server) | ||||||
| #				ssh_timeframe = self.get_ssh_timeframe(for_server) | 			# 	for product in vproducts: | ||||||
| #				for product in vproducts: | 			# 		if product not in ssh_timeframe: | ||||||
| #					if product not in ssh_timeframe: | 			# 			continue | ||||||
| #						continue | 			# 		version = ssh_timeframe.get_from(product, for_server) | ||||||
| #					version = ssh_timeframe.get_from(product, for_server) | 			# 		if version is not None: | ||||||
| #					if version is not None: | 			# 			software = SSH.Software(None, product, version, None, None) | ||||||
| #						software = SSH.Software(None, product, version, None, None) | 			# 			break | ||||||
| #						break |  | ||||||
| 			rec = {}  # type: Dict[int, Dict[str, Dict[str, Dict[str, int]]]] | 			rec = {}  # type: Dict[int, Dict[str, Dict[str, Dict[str, int]]]] | ||||||
| 			if software is None: | 			if software is None: | ||||||
| 				unknown_software = True | 				unknown_software = True | ||||||
| @@ -2057,7 +2069,6 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 			self.client_host = None | 			self.client_host = None | ||||||
| 			self.client_port = None | 			self.client_port = None | ||||||
|  |  | ||||||
| 		 |  | ||||||
| 		def _resolve(self, ipvo): | 		def _resolve(self, ipvo): | ||||||
| 			# type: (Sequence[int]) -> Iterable[Tuple[int, Tuple[Any, ...]]] | 			# type: (Sequence[int]) -> Iterable[Tuple[int, Tuple[Any, ...]]] | ||||||
| 			ipvo = tuple([x for x in utils.unique_seq(ipvo) if x in (4, 6)]) | 			ipvo = tuple([x for x in utils.unique_seq(ipvo) if x in (4, 6)]) | ||||||
| @@ -2081,7 +2092,6 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 				out.fail('[exception] {0}'.format(e)) | 				out.fail('[exception] {0}'.format(e)) | ||||||
| 				sys.exit(1) | 				sys.exit(1) | ||||||
|  |  | ||||||
|  |  | ||||||
| 		# Listens on a server socket and accepts one connection (used for | 		# Listens on a server socket and accepts one connection (used for | ||||||
| 		# auditing client connections). | 		# auditing client connections). | ||||||
| 		def listen_and_accept(self): | 		def listen_and_accept(self): | ||||||
| @@ -2093,7 +2103,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 				s.bind(('0.0.0.0', self.__port)) | 				s.bind(('0.0.0.0', self.__port)) | ||||||
| 				s.listen() | 				s.listen() | ||||||
| 				self.__sock_map[s.fileno()] = s | 				self.__sock_map[s.fileno()] = s | ||||||
| 			except Exception as e: | 			except Exception: | ||||||
| 				print("Warning: failed to listen on any IPv4 interfaces.") | 				print("Warning: failed to listen on any IPv4 interfaces.") | ||||||
| 				pass | 				pass | ||||||
|  |  | ||||||
| @@ -2105,7 +2115,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 				s.bind(('::', self.__port)) | 				s.bind(('::', self.__port)) | ||||||
| 				s.listen() | 				s.listen() | ||||||
| 				self.__sock_map[s.fileno()] = s | 				self.__sock_map[s.fileno()] = s | ||||||
| 			except Exception as e: | 			except Exception: | ||||||
| 				print("Warning: failed to listen on any IPv6 interfaces.") | 				print("Warning: failed to listen on any IPv6 interfaces.") | ||||||
| 				pass | 				pass | ||||||
|  |  | ||||||
| @@ -2139,7 +2149,6 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 			c.settimeout(self.__timeout) | 			c.settimeout(self.__timeout) | ||||||
| 			self.__sock = c | 			self.__sock = c | ||||||
|  |  | ||||||
|  |  | ||||||
| 		def connect(self): | 		def connect(self): | ||||||
| 			# type: () -> None | 			# type: () -> None | ||||||
| 			err = None | 			err = None | ||||||
| @@ -2169,10 +2178,10 @@ class SSH(object):  # pylint: disable=too-few-public-methods | |||||||
| 			banner = SSH_HEADER.format('1.5' if sshv == 1 else '2.0') | 			banner = SSH_HEADER.format('1.5' if sshv == 1 else '2.0') | ||||||
| 			if self.__state < self.SM_BANNER_SENT: | 			if self.__state < self.SM_BANNER_SENT: | ||||||
| 				self.send_banner(banner) | 				self.send_banner(banner) | ||||||
| #			rto = self.__sock.gettimeout() | 			# rto = self.__sock.gettimeout() | ||||||
| #			self.__sock.settimeout(0.7) | 			# self.__sock.settimeout(0.7) | ||||||
| 			s, e = self.recv() | 			s, e = self.recv() | ||||||
| #			self.__sock.settimeout(rto) | 			# self.__sock.settimeout(rto) | ||||||
| 			if s < 0: | 			if s < 0: | ||||||
| 				return self.__banner, self.__header, e | 				return self.__banner, self.__header, e | ||||||
| 			e = None | 			e = None | ||||||
| @@ -2357,7 +2366,6 @@ class KexDH(object):  # pragma: nocover | |||||||
| 		self.__f = 0 | 		self.__f = 0 | ||||||
| 		self.__h_sig = 0 | 		self.__h_sig = 0 | ||||||
|  |  | ||||||
|  |  | ||||||
| 	def set_params(self, g, p): | 	def set_params(self, g, p): | ||||||
| 		self.__g = g | 		self.__g = g | ||||||
| 		self.__p = p | 		self.__p = p | ||||||
| @@ -2365,7 +2373,6 @@ class KexDH(object):  # pragma: nocover | |||||||
| 		self.__x = 0 | 		self.__x = 0 | ||||||
| 		self.__e = 0 | 		self.__e = 0 | ||||||
|  |  | ||||||
|  |  | ||||||
| 	def send_init(self, s, init_msg=SSH.Protocol.MSG_KEXDH_INIT): | 	def send_init(self, s, init_msg=SSH.Protocol.MSG_KEXDH_INIT): | ||||||
| 		# type: (SSH.Socket) -> None | 		# type: (SSH.Socket) -> None | ||||||
| 		r = random.SystemRandom() | 		r = random.SystemRandom() | ||||||
| @@ -2403,7 +2410,6 @@ class KexDH(object):  # pragma: nocover | |||||||
|  |  | ||||||
| 		key_id = principles = None  # pylint: disable=unused-variable | 		key_id = principles = None  # pylint: disable=unused-variable | ||||||
| 		critical_options = extensions = None  # pylint: disable=unused-variable | 		critical_options = extensions = None  # pylint: disable=unused-variable | ||||||
| 		valid_after = valid_before = None    # pylint: disable=unused-variable |  | ||||||
| 		nonce = ca_key = ca_key_type = None  # pylint: disable=unused-variable | 		nonce = ca_key = ca_key_type = None  # pylint: disable=unused-variable | ||||||
| 		ca_key_e = ca_key_n = None  # pylint: disable=unused-variable | 		ca_key_e = ca_key_n = None  # pylint: disable=unused-variable | ||||||
|  |  | ||||||
| @@ -2455,12 +2461,10 @@ class KexDH(object):  # pragma: nocover | |||||||
| 				# The principles, which are... I don't know what. | 				# The principles, which are... I don't know what. | ||||||
| 				principles, principles_len, ptr = KexDH.__get_bytes(hostkey, ptr) | 				principles, principles_len, ptr = KexDH.__get_bytes(hostkey, ptr) | ||||||
|  |  | ||||||
| 				# The timestamp that this certificate is valid after. | 				# Skip over the timestamp that this certificate is valid after. | ||||||
| 				valid_after = hostkey[ptr:ptr + 8] |  | ||||||
| 				ptr += 8 | 				ptr += 8 | ||||||
|  |  | ||||||
| 				# The timestamp that this certificate is valid before. | 				# Skip over the timestamp that this certificate is valid before. | ||||||
| 				valid_before = hostkey[ptr:ptr + 8] |  | ||||||
| 				ptr += 8 | 				ptr += 8 | ||||||
|  |  | ||||||
| 				# TODO: validate the principles, and time range. | 				# TODO: validate the principles, and time range. | ||||||
| @@ -2895,7 +2899,7 @@ def output_fingerprints(algs, sha256=True): | |||||||
| 		if algs.ssh1kex is not None: | 		if algs.ssh1kex is not None: | ||||||
| 			name = 'ssh-rsa1' | 			name = 'ssh-rsa1' | ||||||
| 			fp = SSH.Fingerprint(algs.ssh1kex.host_key_fingerprint_data) | 			fp = SSH.Fingerprint(algs.ssh1kex.host_key_fingerprint_data) | ||||||
| 			#bits = algs.ssh1kex.host_key_bits | 			# bits = algs.ssh1kex.host_key_bits | ||||||
| 			fps.append((name, fp)) | 			fps.append((name, fp)) | ||||||
| 		if algs.ssh2kex is not None: | 		if algs.ssh2kex is not None: | ||||||
| 			host_keys = algs.ssh2kex.host_keys() | 			host_keys = algs.ssh2kex.host_keys() | ||||||
| @@ -2917,8 +2921,8 @@ def output_fingerprints(algs, sha256=True): | |||||||
| 		for fpp in fps: | 		for fpp in fps: | ||||||
| 			name, fp = fpp | 			name, fp = fpp | ||||||
| 			fpo = fp.sha256 if sha256 else fp.md5 | 			fpo = fp.sha256 if sha256 else fp.md5 | ||||||
| 			#p = '' if out.batch else ' ' * (padlen - len(name)) | 			# p = '' if out.batch else ' ' * (padlen - len(name)) | ||||||
| 			#out.good('(fin) {0}{1} -- {2} {3}'.format(name, p, bits, fpo)) | 			# out.good('(fin) {0}{1} -- {2} {3}'.format(name, p, bits, fpo)) | ||||||
| 			out.good('(fin) {0}: {1}'.format(name, fpo)) | 			out.good('(fin) {0}: {1}'.format(name, fpo)) | ||||||
| 	if len(obuf) > 0: | 	if len(obuf) > 0: | ||||||
| 		out.head('# fingerprints') | 		out.head('# fingerprints') | ||||||
| @@ -3018,7 +3022,7 @@ def output_info(algs, software, client_audit, any_problems, padlen=0): | |||||||
|  |  | ||||||
| def output(banner, header, client_host=None, kex=None, pkm=None): | def output(banner, header, client_host=None, kex=None, pkm=None): | ||||||
| 	# type: (Optional[SSH.Banner], List[text_type], Optional[SSH2.Kex], Optional[SSH1.PublicKeyMessage]) -> None | 	# type: (Optional[SSH.Banner], List[text_type], Optional[SSH2.Kex], Optional[SSH1.PublicKeyMessage]) -> None | ||||||
| 	client_audit = (client_host != None) # If set, this is a client audit. | 	client_audit = client_host is not None  # If set, this is a client audit. | ||||||
| 	sshv = 1 if pkm is not None else 2 | 	sshv = 1 if pkm is not None else 2 | ||||||
| 	algs = SSH.Algorithms(pkm, kex) | 	algs = SSH.Algorithms(pkm, kex) | ||||||
| 	with OutputBuffer() as obuf: | 	with OutputBuffer() as obuf: | ||||||
| @@ -3077,11 +3081,11 @@ def output(banner, header, client_host=None, kex=None, pkm=None): | |||||||
| 	perfect_config = output_recommendations(algs, software, maxlen) | 	perfect_config = output_recommendations(algs, software, maxlen) | ||||||
| 	output_info(algs, software, client_audit, not perfect_config) | 	output_info(algs, software, client_audit, not perfect_config) | ||||||
|  |  | ||||||
|  |  | ||||||
| 	# If we encountered any unknown algorithms, ask the user to report them. | 	# If we encountered any unknown algorithms, ask the user to report them. | ||||||
| 	if len(unknown_algorithms) > 0: | 	if len(unknown_algorithms) > 0: | ||||||
| 		out.warn("\n\n!!! WARNING: unknown algorithm(s) found!: %s.  Please email the full output above to the maintainer (jtesta@positronsecurity.com), or create a Github issue at <https://github.com/jtesta/ssh-audit/issues>.\n" % ','.join(unknown_algorithms)) | 		out.warn("\n\n!!! WARNING: unknown algorithm(s) found!: %s.  Please email the full output above to the maintainer (jtesta@positronsecurity.com), or create a Github issue at <https://github.com/jtesta/ssh-audit/issues>.\n" % ','.join(unknown_algorithms)) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Utils(object): | class Utils(object): | ||||||
| 	@classmethod | 	@classmethod | ||||||
| 	def _type_err(cls, v, target): | 	def _type_err(cls, v, target): | ||||||
| @@ -3204,6 +3208,7 @@ class Utils(object): | |||||||
| 		except:  # pylint: disable=bare-except | 		except:  # pylint: disable=bare-except | ||||||
| 			return -1.0 | 			return -1.0 | ||||||
|  |  | ||||||
|  |  | ||||||
| def build_struct(banner, kex=None, pkm=None, client_host=None): | def build_struct(banner, kex=None, pkm=None, client_host=None): | ||||||
| 	res = { | 	res = { | ||||||
| 		"banner": { | 		"banner": { | ||||||
| @@ -3281,6 +3286,7 @@ def build_struct(banner, kex=None, pkm=None, client_host=None): | |||||||
|  |  | ||||||
| 	return res | 	return res | ||||||
|  |  | ||||||
|  |  | ||||||
| def audit(aconf, sshv=None): | def audit(aconf, sshv=None): | ||||||
| 	# type: (AuditConf, Optional[int]) -> None | 	# type: (AuditConf, Optional[int]) -> None | ||||||
| 	out.batch = aconf.batch | 	out.batch = aconf.batch | ||||||
| @@ -3350,9 +3356,11 @@ def audit(aconf, sshv=None): | |||||||
| utils = Utils() | utils = Utils() | ||||||
| out = Output() | out = Output() | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
| 	conf = AuditConf.from_cmdline(sys.argv[1:], usage) | 	conf = AuditConf.from_cmdline(sys.argv[1:], usage) | ||||||
| 	audit(conf) | 	audit(conf) | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__':  # pragma: nocover | if __name__ == '__main__':  # pragma: nocover | ||||||
| 	main() | 	main() | ||||||
|   | |||||||
| @@ -52,7 +52,6 @@ class TestErrors(object): | |||||||
| 		assert 'timed out' in lines[-1] | 		assert 'timed out' in lines[-1] | ||||||
|  |  | ||||||
| 	def test_recv_empty(self, output_spy, virtual_socket): | 	def test_recv_empty(self, output_spy, virtual_socket): | ||||||
| 		vsocket = virtual_socket |  | ||||||
| 		lines = self._audit(output_spy) | 		lines = self._audit(output_spy) | ||||||
| 		assert len(lines) == 1 | 		assert len(lines) == 1 | ||||||
| 		assert 'did not receive banner' in lines[-1] | 		assert 'did not receive banner' in lines[-1] | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ class TestResolve(object): | |||||||
| 		conf = self._conf() | 		conf = self._conf() | ||||||
| 		output_spy.begin() | 		output_spy.begin() | ||||||
| 		with pytest.raises(SystemExit): | 		with pytest.raises(SystemExit): | ||||||
| 			r = list(s._resolve(conf.ipvo)) | 			list(s._resolve(conf.ipvo)) | ||||||
| 		lines = output_spy.flush() | 		lines = output_spy.flush() | ||||||
| 		assert len(lines) == 1 | 		assert len(lines) == 1 | ||||||
| 		assert 'hostname nor servname provided' in lines[-1] | 		assert 'hostname nor servname provided' in lines[-1] | ||||||
| @@ -40,7 +40,6 @@ class TestResolve(object): | |||||||
| 		assert len(r) == 0 | 		assert len(r) == 0 | ||||||
|  |  | ||||||
| 	def test_resolve_ipv4(self, virtual_socket): | 	def test_resolve_ipv4(self, virtual_socket): | ||||||
| 		vsocket = virtual_socket |  | ||||||
| 		conf = self._conf() | 		conf = self._conf() | ||||||
| 		conf.ipv4 = True | 		conf.ipv4 = True | ||||||
| 		s = self.ssh.Socket('localhost', 22) | 		s = self.ssh.Socket('localhost', 22) | ||||||
| @@ -49,7 +48,6 @@ class TestResolve(object): | |||||||
| 		assert r[0] == (socket.AF_INET, ('127.0.0.1', 22)) | 		assert r[0] == (socket.AF_INET, ('127.0.0.1', 22)) | ||||||
|  |  | ||||||
| 	def test_resolve_ipv6(self, virtual_socket): | 	def test_resolve_ipv6(self, virtual_socket): | ||||||
| 		vsocket = virtual_socket |  | ||||||
| 		s = self.ssh.Socket('localhost', 22) | 		s = self.ssh.Socket('localhost', 22) | ||||||
| 		conf = self._conf() | 		conf = self._conf() | ||||||
| 		conf.ipv6 = True | 		conf.ipv6 = True | ||||||
| @@ -58,7 +56,6 @@ class TestResolve(object): | |||||||
| 		assert r[0] == (socket.AF_INET6, ('::1', 22)) | 		assert r[0] == (socket.AF_INET6, ('::1', 22)) | ||||||
|  |  | ||||||
| 	def test_resolve_ipv46_both(self, virtual_socket): | 	def test_resolve_ipv46_both(self, virtual_socket): | ||||||
| 		vsocket = virtual_socket |  | ||||||
| 		s = self.ssh.Socket('localhost', 22) | 		s = self.ssh.Socket('localhost', 22) | ||||||
| 		conf = self._conf() | 		conf = self._conf() | ||||||
| 		r = list(s._resolve(conf.ipvo)) | 		r = list(s._resolve(conf.ipvo)) | ||||||
| @@ -67,7 +64,6 @@ class TestResolve(object): | |||||||
| 		assert r[1] == (socket.AF_INET6, ('::1', 22)) | 		assert r[1] == (socket.AF_INET6, ('::1', 22)) | ||||||
|  |  | ||||||
| 	def test_resolve_ipv46_order(self, virtual_socket): | 	def test_resolve_ipv46_order(self, virtual_socket): | ||||||
| 		vsocket = virtual_socket |  | ||||||
| 		s = self.ssh.Socket('localhost', 22) | 		s = self.ssh.Socket('localhost', 22) | ||||||
| 		conf = self._conf() | 		conf = self._conf() | ||||||
| 		conf.ipv4 = True | 		conf.ipv4 = True | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| #!/usr/bin/env python | #!/usr/bin/env python | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| import socket |  | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -12,17 +11,17 @@ class TestSocket(object): | |||||||
|  |  | ||||||
| 	def test_invalid_host(self, virtual_socket): | 	def test_invalid_host(self, virtual_socket): | ||||||
| 		with pytest.raises(ValueError): | 		with pytest.raises(ValueError): | ||||||
| 			s = self.ssh.Socket(None, 22) | 			self.ssh.Socket(None, 22) | ||||||
|  |  | ||||||
| 	def test_invalid_port(self, virtual_socket): | 	def test_invalid_port(self, virtual_socket): | ||||||
| 		with pytest.raises(ValueError): | 		with pytest.raises(ValueError): | ||||||
| 			s = self.ssh.Socket('localhost', 'abc') | 			self.ssh.Socket('localhost', 'abc') | ||||||
| 		with pytest.raises(ValueError): | 		with pytest.raises(ValueError): | ||||||
| 			s = self.ssh.Socket('localhost', -1) | 			self.ssh.Socket('localhost', -1) | ||||||
| 		with pytest.raises(ValueError): | 		with pytest.raises(ValueError): | ||||||
| 			s = self.ssh.Socket('localhost', 0) | 			self.ssh.Socket('localhost', 0) | ||||||
| 		with pytest.raises(ValueError): | 		with pytest.raises(ValueError): | ||||||
| 			s = self.ssh.Socket('localhost', 65536) | 			self.ssh.Socket('localhost', 65536) | ||||||
|  |  | ||||||
| 	def test_not_connected_socket(self, virtual_socket): | 	def test_not_connected_socket(self, virtual_socket): | ||||||
| 		sock = self.ssh.Socket('localhost', 22) | 		sock = self.ssh.Socket('localhost', 22) | ||||||
|   | |||||||
| @@ -95,10 +95,10 @@ class TestSSH1(object): | |||||||
| 		skey, hkey = self._server_key(), self._host_key() | 		skey, hkey = self._server_key(), self._host_key() | ||||||
| 		pkm = self.ssh1.PublicKeyMessage(cookie, skey, hkey, pflags, cmask, amask) | 		pkm = self.ssh1.PublicKeyMessage(cookie, skey, hkey, pflags, cmask, amask) | ||||||
| 		self._assert_pkm_fields(pkm, skey, hkey) | 		self._assert_pkm_fields(pkm, skey, hkey) | ||||||
| 		for skey2 in ([], [0], [0,1], [0,1,2,3]): | 		for skey2 in ([], [0], [0, 1], [0, 1, 2, 3]): | ||||||
| 			with pytest.raises(ValueError): | 			with pytest.raises(ValueError): | ||||||
| 				pkm = self.ssh1.PublicKeyMessage(cookie, skey2, hkey, pflags, cmask, amask) | 				pkm = self.ssh1.PublicKeyMessage(cookie, skey2, hkey, pflags, cmask, amask) | ||||||
| 		for hkey2 in ([], [0], [0,1], [0,1,2,3]): | 		for hkey2 in ([], [0], [0, 1], [0, 1, 2, 3]): | ||||||
| 			with pytest.raises(ValueError): | 			with pytest.raises(ValueError): | ||||||
| 				print(hkey2) | 				print(hkey2) | ||||||
| 				pkm = self.ssh1.PublicKeyMessage(cookie, skey, hkey2, pflags, cmask, amask) | 				pkm = self.ssh1.PublicKeyMessage(cookie, skey, hkey2, pflags, cmask, amask) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #!/usr/bin/env python | #!/usr/bin/env python | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| import struct, os | import os | ||||||
|  | import struct | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -105,15 +105,15 @@ class TestVersionCompare(object): | |||||||
| 			versions.append('2015.{0}'.format(i)) | 			versions.append('2015.{0}'.format(i)) | ||||||
| 		for i in range(72, 75): | 		for i in range(72, 75): | ||||||
| 			versions.append('2016.{0}'.format(i)) | 			versions.append('2016.{0}'.format(i)) | ||||||
| 		l = len(versions) | 		length = len(versions) | ||||||
| 		for i in range(l): | 		for i in range(length): | ||||||
| 			v = versions[i] | 			v = versions[i] | ||||||
| 			s = self.get_dropbear_software(v) | 			s = self.get_dropbear_software(v) | ||||||
| 			assert s.compare_version(v) == 0 | 			assert s.compare_version(v) == 0 | ||||||
| 			if i - 1 >= 0: | 			if i - 1 >= 0: | ||||||
| 				vbefore = versions[i - 1] | 				vbefore = versions[i - 1] | ||||||
| 				assert s.compare_version(vbefore) > 0 | 				assert s.compare_version(vbefore) > 0 | ||||||
| 			if i + 1 < l: | 			if i + 1 < length: | ||||||
| 				vnext = versions[i + 1] | 				vnext = versions[i + 1] | ||||||
| 				assert s.compare_version(vnext) < 0 | 				assert s.compare_version(vnext) < 0 | ||||||
|  |  | ||||||
| @@ -164,15 +164,15 @@ class TestVersionCompare(object): | |||||||
| 			versions.append('6.{0}'.format(i)) | 			versions.append('6.{0}'.format(i)) | ||||||
| 		for i in range(0, 4): | 		for i in range(0, 4): | ||||||
| 			versions.append('7.{0}'.format(i)) | 			versions.append('7.{0}'.format(i)) | ||||||
| 		l = len(versions) | 		length = len(versions) | ||||||
| 		for i in range(l): | 		for i in range(length): | ||||||
| 			v = versions[i] | 			v = versions[i] | ||||||
| 			s = self.get_openssh_software(v) | 			s = self.get_openssh_software(v) | ||||||
| 			assert s.compare_version(v) == 0 | 			assert s.compare_version(v) == 0 | ||||||
| 			if i - 1 >= 0: | 			if i - 1 >= 0: | ||||||
| 				vbefore = versions[i - 1] | 				vbefore = versions[i - 1] | ||||||
| 				assert s.compare_version(vbefore) > 0 | 				assert s.compare_version(vbefore) > 0 | ||||||
| 			if i + 1 < l: | 			if i + 1 < length: | ||||||
| 				vnext = versions[i + 1] | 				vnext = versions[i + 1] | ||||||
| 				assert s.compare_version(vnext) < 0 | 				assert s.compare_version(vnext) < 0 | ||||||
|  |  | ||||||
| @@ -202,14 +202,14 @@ class TestVersionCompare(object): | |||||||
| 			versions.append('0.6.{0}'.format(i)) | 			versions.append('0.6.{0}'.format(i)) | ||||||
| 		for i in range(0, 5): | 		for i in range(0, 5): | ||||||
| 			versions.append('0.7.{0}'.format(i)) | 			versions.append('0.7.{0}'.format(i)) | ||||||
| 		l = len(versions) | 		length = len(versions) | ||||||
| 		for i in range(l): | 		for i in range(length): | ||||||
| 			v = versions[i] | 			v = versions[i] | ||||||
| 			s = self.get_libssh_software(v) | 			s = self.get_libssh_software(v) | ||||||
| 			assert s.compare_version(v) == 0 | 			assert s.compare_version(v) == 0 | ||||||
| 			if i - 1 >= 0: | 			if i - 1 >= 0: | ||||||
| 				vbefore = versions[i - 1] | 				vbefore = versions[i - 1] | ||||||
| 				assert s.compare_version(vbefore) > 0 | 				assert s.compare_version(vbefore) > 0 | ||||||
| 			if i + 1 < l: | 			if i + 1 < length: | ||||||
| 				vnext = versions[i + 1] | 				vnext = versions[i + 1] | ||||||
| 				assert s.compare_version(vnext) < 0 | 				assert s.compare_version(vnext) < 0 | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								tox.ini
									
									
									
									
									
								
							| @@ -80,7 +80,7 @@ commands = | |||||||
| deps = | deps = | ||||||
| 	flake8 | 	flake8 | ||||||
| commands = | commands = | ||||||
| 	flake8 {posargs:{env:SSHAUDIT}} | 	flake8 {posargs:{env:SSHAUDIT} {toxinidir}/packages/setup.py {toxinidir}/test}  --statistics | ||||||
|  |  | ||||||
| [testenv:vulture] | [testenv:vulture] | ||||||
| deps = | deps = | ||||||
| @@ -137,42 +137,13 @@ max-module-lines = 2500 | |||||||
|  |  | ||||||
| [flake8] | [flake8] | ||||||
| ignore = | ignore = | ||||||
| 	# indentation contains tabs | 	W191,  # indentation contains tabs | ||||||
| 	W191, | 	E101,  # indentation contains mixed spaces and tabs | ||||||
| 	# blank line contains whitespace | 	E241,  # multiple spaces after operator; should be kept for tabular data | ||||||
| 	W293, | 	E501,  # line too long | ||||||
| 	# indentation contains mixed spaces and tabs |  | ||||||
| 	E101, |  | ||||||
| 	# multiple spaces before operator |  | ||||||
| 	E221, |  | ||||||
| 	# multiple spaces after operator |  | ||||||
| 	E241, |  | ||||||
| 	# multiple imports on one line |  | ||||||
| 	E401, |  | ||||||
| 	# line too long |  | ||||||
| 	E501, |  | ||||||
| 	# module imported but unused |  | ||||||
| 	F401, |  | ||||||
| 	# undefined name |  | ||||||
| 	F821, |  | ||||||
| 	# these exceptions should be handled one by one |  | ||||||
| 	E117,  # over-indented | 	E117,  # over-indented | ||||||
| 	E126,  # continuation line over-indented for hanging indent | 	E126,  # continuation line over-indented for hanging indent | ||||||
| 	E128,  # continuation line under-indented for visual indent | 	E128,  # continuation line under-indented for visual indent | ||||||
| 	E226, # missing whitespace around arithmetic operator |  | ||||||
| 	E231, # missing whitespace after ',' |  | ||||||
| 	E251, # unexpected spaces around keyword / parameter equals |  | ||||||
| 	E261, # at least two spaces before inline comment |  | ||||||
| 	E265, # block comment should start with '# ' |  | ||||||
| 	E301, # expected 1 blank line, found 0 |  | ||||||
| 	E302, # expected 2 blank lines, found 1 |  | ||||||
| 	E303, # too many blank lines (2) |  | ||||||
| 	E305, # expected 2 blank lines after class or function definition, found 1 |  | ||||||
| 	E711, # comparison to None should be 'if cond is not None:' |  | ||||||
| 	E712, # comparison to False should be 'if cond is False:' or 'if not cond:' |  | ||||||
| 	E722,  # do not use bare 'except' | 	E722,  # do not use bare 'except' | ||||||
| 	E741, # ambiguous variable name 'l' |  | ||||||
| 	F601,  # dictionary key 'ecdsa-sha2-1.3.132.0.10' repeated with different values | 	F601,  # dictionary key 'ecdsa-sha2-1.3.132.0.10' repeated with different values | ||||||
| 	F841, # local variable 'e' is assigned to but never used | 	W504,  # line break after binary operator; this (or W503) has to stay | ||||||
| 	W504, # line break after binary operator |  | ||||||
| 	W605, # invalid escape sequence '\s' |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jürgen Gmach
					Jürgen Gmach