mirror of
https://github.com/jtesta/ssh-audit.git
synced 2024-11-22 10:31:41 +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:
parent
29d874b450
commit
246a41d46f
@ -5,7 +5,7 @@ import re
|
||||
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)
|
||||
|
||||
with open("sshaudit/README.md", "rb") as f:
|
||||
@ -13,20 +13,20 @@ with open("sshaudit/README.md", "rb") as f:
|
||||
|
||||
|
||||
setup(
|
||||
name = "ssh-audit",
|
||||
packages = ["sshaudit"],
|
||||
license = 'MIT',
|
||||
entry_points = {
|
||||
name="ssh-audit",
|
||||
packages=["sshaudit"],
|
||||
license='MIT',
|
||||
entry_points={
|
||||
"console_scripts": ['ssh-audit = sshaudit.sshaudit:main']
|
||||
},
|
||||
version = version,
|
||||
description = "An SSH server & client configuration security auditing tool",
|
||||
long_description = long_descr,
|
||||
long_description_content_type = "text/markdown",
|
||||
author = "Joe Testa",
|
||||
author_email = "jtesta@positronsecurity.com",
|
||||
url = "https://github.com/jtesta/ssh-audit",
|
||||
classifiers = [
|
||||
version=version,
|
||||
description="An SSH server & client configuration security auditing tool",
|
||||
long_description=long_descr,
|
||||
long_description_content_type="text/markdown",
|
||||
author="Joe Testa",
|
||||
author_email="jtesta@positronsecurity.com",
|
||||
url="https://github.com/jtesta/ssh-audit",
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Information Technology",
|
||||
"Intended Audience :: System Administrators",
|
||||
|
520
ssh-audit.py
520
ssh-audit.py
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@ class _OutputSpy(list):
|
||||
self.__out = StringIO()
|
||||
self.__old_stdout = sys.stdout
|
||||
sys.stdout = self.__out
|
||||
|
||||
|
||||
def flush(self):
|
||||
lines = self.__out.getvalue().splitlines()
|
||||
sys.stdout = self.__old_stdout
|
||||
@ -44,12 +44,12 @@ class _VirtualGlobalSocket(object):
|
||||
def __init__(self, vsocket):
|
||||
self.vsocket = vsocket
|
||||
self.addrinfodata = {}
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def create_connection(self, address, timeout=0, source_address=None):
|
||||
# pylint: disable=protected-access
|
||||
return self.vsocket._connect(address, True)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def socket(self,
|
||||
family=socket.AF_INET,
|
||||
@ -57,7 +57,7 @@ class _VirtualGlobalSocket(object):
|
||||
proto=0,
|
||||
fileno=None):
|
||||
return self.vsocket
|
||||
|
||||
|
||||
def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, flags=0):
|
||||
key = '{0}#{1}'.format(host, port)
|
||||
if key in self.addrinfodata:
|
||||
@ -85,41 +85,41 @@ class _VirtualSocket(object):
|
||||
self.sdata = []
|
||||
self.errors = {}
|
||||
self.gsock = _VirtualGlobalSocket(self)
|
||||
|
||||
|
||||
def _check_err(self, method):
|
||||
method_error = self.errors.get(method)
|
||||
if method_error:
|
||||
raise method_error
|
||||
|
||||
|
||||
def connect(self, address):
|
||||
return self._connect(address, False)
|
||||
|
||||
|
||||
def _connect(self, address, ret=True):
|
||||
self.peer_address = address
|
||||
self._connected = True
|
||||
self._check_err('connect')
|
||||
return self if ret else None
|
||||
|
||||
|
||||
def settimeout(self, timeout):
|
||||
self.timeout = timeout
|
||||
|
||||
|
||||
def gettimeout(self):
|
||||
return self.timeout
|
||||
|
||||
|
||||
def getpeername(self):
|
||||
if self.peer_address is None or not self._connected:
|
||||
raise socket.error(57, 'Socket is not connected')
|
||||
return self.peer_address
|
||||
|
||||
|
||||
def getsockname(self):
|
||||
return self.sock_address
|
||||
|
||||
|
||||
def bind(self, address):
|
||||
self.sock_address = address
|
||||
|
||||
|
||||
def listen(self, backlog):
|
||||
pass
|
||||
|
||||
|
||||
def accept(self):
|
||||
# pylint: disable=protected-access
|
||||
conn = _VirtualSocket()
|
||||
@ -127,7 +127,7 @@ class _VirtualSocket(object):
|
||||
conn.peer_address = ('127.0.0.1', 0)
|
||||
conn._connected = True
|
||||
return conn, conn.peer_address
|
||||
|
||||
|
||||
def recv(self, bufsize, flags=0):
|
||||
# pylint: disable=unused-argument
|
||||
if not self._connected:
|
||||
@ -138,7 +138,7 @@ class _VirtualSocket(object):
|
||||
if isinstance(data, Exception):
|
||||
raise data
|
||||
return data
|
||||
|
||||
|
||||
def send(self, data):
|
||||
if self.peer_address is None or not self._connected:
|
||||
raise socket.error(32, 'Broken pipe')
|
||||
|
@ -9,7 +9,7 @@ class TestAuditConf(object):
|
||||
def init(self, ssh_audit):
|
||||
self.AuditConf = ssh_audit.AuditConf
|
||||
self.usage = ssh_audit.usage
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _test_conf(conf, **kwargs):
|
||||
options = {
|
||||
@ -38,11 +38,11 @@ class TestAuditConf(object):
|
||||
assert conf.ipv4 == options['ipv4']
|
||||
assert conf.ipv6 == options['ipv6']
|
||||
assert conf.ipvo == options['ipvo']
|
||||
|
||||
|
||||
def test_audit_conf_defaults(self):
|
||||
conf = self.AuditConf()
|
||||
self._test_conf(conf)
|
||||
|
||||
|
||||
def test_audit_conf_booleans(self):
|
||||
conf = self.AuditConf()
|
||||
for p in ['ssh1', 'ssh2', 'batch', 'colors', 'verbose']:
|
||||
@ -52,7 +52,7 @@ class TestAuditConf(object):
|
||||
for v in [False, 0]:
|
||||
setattr(conf, p, v)
|
||||
assert getattr(conf, p) is False
|
||||
|
||||
|
||||
def test_audit_conf_port(self):
|
||||
conf = self.AuditConf()
|
||||
for port in [22, 2222]:
|
||||
@ -62,7 +62,7 @@ class TestAuditConf(object):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
conf.port = port
|
||||
excinfo.match(r'.*invalid port.*')
|
||||
|
||||
|
||||
def test_audit_conf_ipvo(self):
|
||||
# ipv4-only
|
||||
conf = self.AuditConf()
|
||||
@ -114,7 +114,7 @@ class TestAuditConf(object):
|
||||
assert conf.ipvo == (4, 6)
|
||||
conf.ipvo = (4, 4, 4, 6, 6)
|
||||
assert conf.ipvo == (4, 6)
|
||||
|
||||
|
||||
def test_audit_conf_level(self):
|
||||
conf = self.AuditConf()
|
||||
for level in ['info', 'warn', 'fail']:
|
||||
@ -124,7 +124,7 @@ class TestAuditConf(object):
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
conf.level = level
|
||||
excinfo.match(r'.*invalid level.*')
|
||||
|
||||
|
||||
def test_audit_conf_cmdline(self):
|
||||
# pylint: disable=too-many-statements
|
||||
c = lambda x: self.AuditConf.from_cmdline(x.split(), self.usage) # noqa
|
||||
|
@ -8,7 +8,7 @@ class TestBanner(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
self.ssh = ssh_audit.SSH
|
||||
|
||||
|
||||
def test_simple_banners(self):
|
||||
banner = lambda x: self.ssh.Banner.parse(x) # noqa
|
||||
b = banner('SSH-2.0-OpenSSH_7.3')
|
||||
@ -26,12 +26,12 @@ class TestBanner(object):
|
||||
assert b.software == 'Cisco-1.25'
|
||||
assert b.comments is None
|
||||
assert str(b) == 'SSH-1.5-Cisco-1.25'
|
||||
|
||||
|
||||
def test_invalid_banners(self):
|
||||
b = lambda x: self.ssh.Banner.parse(x) # noqa
|
||||
assert b('Something') is None
|
||||
assert b('SSH-XXX-OpenSSH_7.3') is None
|
||||
|
||||
|
||||
def test_banners_with_spaces(self):
|
||||
b = lambda x: self.ssh.Banner.parse(x) # noqa
|
||||
s = 'SSH-2.0-OpenSSH_4.3p2'
|
||||
@ -42,7 +42,7 @@ class TestBanner(object):
|
||||
assert str(b('SSH-2.0- OpenSSH_4.3p2 Debian-9etch3 on i686-pc-linux-gnu')) == s
|
||||
assert str(b('SSH-2.0-OpenSSH_4.3p2 Debian-9etch3 on i686-pc-linux-gnu ')) == s
|
||||
assert str(b('SSH-2.0- OpenSSH_4.3p2 Debian-9etch3 on i686-pc-linux-gnu ')) == s
|
||||
|
||||
|
||||
def test_banners_without_software(self):
|
||||
b = lambda x: self.ssh.Banner.parse(x) # noqa
|
||||
assert b('SSH-2.0').protocol == (2, 0)
|
||||
@ -53,13 +53,13 @@ class TestBanner(object):
|
||||
assert b('SSH-2.0-').software == ''
|
||||
assert b('SSH-2.0-').comments is None
|
||||
assert str(b('SSH-2.0-')) == 'SSH-2.0-'
|
||||
|
||||
|
||||
def test_banners_with_comments(self):
|
||||
b = lambda x: self.ssh.Banner.parse(x) # noqa
|
||||
assert repr(b('SSH-2.0-OpenSSH_7.2p2 Ubuntu-1')) == '<Banner(protocol=2.0, software=OpenSSH_7.2p2, comments=Ubuntu-1)>'
|
||||
assert repr(b('SSH-1.99-OpenSSH_3.4p1 Debian 1:3.4p1-1.woody.3')) == '<Banner(protocol=1.99, software=OpenSSH_3.4p1, comments=Debian 1:3.4p1-1.woody.3)>'
|
||||
assert repr(b('SSH-1.5-1.3.7 F-SECURE SSH')) == '<Banner(protocol=1.5, software=1.3.7, comments=F-SECURE SSH)>'
|
||||
|
||||
|
||||
def test_banners_with_multiple_protocols(self):
|
||||
b = lambda x: self.ssh.Banner.parse(x) # noqa
|
||||
assert str(b('SSH-1.99-SSH-1.99-OpenSSH_3.6.1p2')) == 'SSH-1.99-OpenSSH_3.6.1p2'
|
||||
|
@ -11,13 +11,13 @@ class TestBuffer(object):
|
||||
self.rbuf = ssh_audit.ReadBuf
|
||||
self.wbuf = ssh_audit.WriteBuf
|
||||
self.utf8rchar = b'\xef\xbf\xbd'
|
||||
|
||||
|
||||
@classmethod
|
||||
def _b(cls, v):
|
||||
v = re.sub(r'\s', '', v)
|
||||
data = [int(v[i * 2:i * 2 + 2], 16) for i in range(len(v) // 2)]
|
||||
return bytes(bytearray(data))
|
||||
|
||||
|
||||
def test_unread(self):
|
||||
w = self.wbuf().write_byte(1).write_int(2).write_flush()
|
||||
r = self.rbuf(w)
|
||||
@ -26,7 +26,7 @@ class TestBuffer(object):
|
||||
assert r.unread_len == 4
|
||||
r.read_int()
|
||||
assert r.unread_len == 0
|
||||
|
||||
|
||||
def test_byte(self):
|
||||
w = lambda x: self.wbuf().write_byte(x).write_flush() # noqa
|
||||
r = lambda x: self.rbuf(x).read_byte() # noqa
|
||||
@ -37,7 +37,7 @@ class TestBuffer(object):
|
||||
for p in tc:
|
||||
assert w(p[0]) == self._b(p[1])
|
||||
assert r(self._b(p[1])) == p[0]
|
||||
|
||||
|
||||
def test_bool(self):
|
||||
w = lambda x: self.wbuf().write_bool(x).write_flush() # noqa
|
||||
r = lambda x: self.rbuf(x).read_bool() # noqa
|
||||
@ -46,7 +46,7 @@ class TestBuffer(object):
|
||||
for p in tc:
|
||||
assert w(p[0]) == self._b(p[1])
|
||||
assert r(self._b(p[1])) == p[0]
|
||||
|
||||
|
||||
def test_int(self):
|
||||
w = lambda x: self.wbuf().write_int(x).write_flush() # noqa
|
||||
r = lambda x: self.rbuf(x).read_int() # noqa
|
||||
@ -57,7 +57,7 @@ class TestBuffer(object):
|
||||
for p in tc:
|
||||
assert w(p[0]) == self._b(p[1])
|
||||
assert r(self._b(p[1])) == p[0]
|
||||
|
||||
|
||||
def test_string(self):
|
||||
w = lambda x: self.wbuf().write_string(x).write_flush() # noqa
|
||||
r = lambda x: self.rbuf(x).read_string() # noqa
|
||||
@ -69,7 +69,7 @@ class TestBuffer(object):
|
||||
if not isinstance(v, bytes):
|
||||
v = bytes(bytearray(v, 'utf-8'))
|
||||
assert r(self._b(p[1])) == v
|
||||
|
||||
|
||||
def test_list(self):
|
||||
w = lambda x: self.wbuf().write_list(x).write_flush() # noqa
|
||||
r = lambda x: self.rbuf(x).read_list() # noqa
|
||||
@ -77,13 +77,13 @@ class TestBuffer(object):
|
||||
for p in tc:
|
||||
assert w(p[0]) == self._b(p[1])
|
||||
assert r(self._b(p[1])) == p[0]
|
||||
|
||||
|
||||
def test_list_nonutf8(self):
|
||||
r = lambda x: self.rbuf(x).read_list() # noqa
|
||||
src = self._b('00 00 00 04 de ad be ef')
|
||||
dst = [(b'\xde\xad' + self.utf8rchar + self.utf8rchar).decode('utf-8')]
|
||||
assert r(src) == dst
|
||||
|
||||
|
||||
def test_line(self):
|
||||
w = lambda x: self.wbuf().write_line(x).write_flush() # noqa
|
||||
r = lambda x: self.rbuf(x).read_line() # noqa
|
||||
@ -91,13 +91,13 @@ class TestBuffer(object):
|
||||
for p in tc:
|
||||
assert w(p[0]) == self._b(p[1])
|
||||
assert r(self._b(p[1])) == p[0]
|
||||
|
||||
|
||||
def test_line_nonutf8(self):
|
||||
r = lambda x: self.rbuf(x).read_line() # noqa
|
||||
src = self._b('de ad be af')
|
||||
dst = (b'\xde\xad' + self.utf8rchar + self.utf8rchar).decode('utf-8')
|
||||
assert r(src) == dst
|
||||
|
||||
|
||||
def test_bitlen(self):
|
||||
# pylint: disable=protected-access
|
||||
class Py26Int(int):
|
||||
@ -105,7 +105,7 @@ class TestBuffer(object):
|
||||
raise AttributeError
|
||||
assert self.wbuf._bitlength(42) == 6
|
||||
assert self.wbuf._bitlength(Py26Int(42)) == 6
|
||||
|
||||
|
||||
def test_mpint1(self):
|
||||
mpint1w = lambda x: self.wbuf().write_mpint1(x).write_flush() # noqa
|
||||
mpint1r = lambda x: self.rbuf(x).read_mpint1() # noqa
|
||||
@ -116,7 +116,7 @@ class TestBuffer(object):
|
||||
for p in tc:
|
||||
assert mpint1w(p[0]) == self._b(p[1])
|
||||
assert mpint1r(self._b(p[1])) == p[0]
|
||||
|
||||
|
||||
def test_mpint2(self):
|
||||
mpint2w = lambda x: self.wbuf().write_mpint2(x).write_flush() # noqa
|
||||
mpint2r = lambda x: self.rbuf(x).read_mpint2() # noqa
|
||||
|
@ -11,13 +11,13 @@ class TestErrors(object):
|
||||
def init(self, ssh_audit):
|
||||
self.AuditConf = ssh_audit.AuditConf
|
||||
self.audit = ssh_audit.audit
|
||||
|
||||
|
||||
def _conf(self):
|
||||
conf = self.AuditConf('localhost', 22)
|
||||
conf.colors = False
|
||||
conf.batch = True
|
||||
return conf
|
||||
|
||||
|
||||
def _audit(self, spy, conf=None, sysexit=True):
|
||||
if conf is None:
|
||||
conf = self._conf()
|
||||
@ -29,34 +29,33 @@ class TestErrors(object):
|
||||
self.audit(conf)
|
||||
lines = spy.flush()
|
||||
return lines
|
||||
|
||||
|
||||
def test_connection_unresolved(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.gsock.addrinfodata['localhost#22'] = []
|
||||
lines = self._audit(output_spy)
|
||||
assert len(lines) == 1
|
||||
assert 'has no DNS records' in lines[-1]
|
||||
|
||||
|
||||
def test_connection_refused(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.errors['connect'] = socket.error(errno.ECONNREFUSED, 'Connection refused')
|
||||
lines = self._audit(output_spy)
|
||||
assert len(lines) == 1
|
||||
assert 'Connection refused' in lines[-1]
|
||||
|
||||
|
||||
def test_connection_timeout(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.errors['connect'] = socket.timeout('timed out')
|
||||
lines = self._audit(output_spy)
|
||||
assert len(lines) == 1
|
||||
assert 'timed out' in lines[-1]
|
||||
|
||||
|
||||
def test_recv_empty(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
lines = self._audit(output_spy)
|
||||
assert len(lines) == 1
|
||||
assert 'did not receive banner' in lines[-1]
|
||||
|
||||
|
||||
def test_recv_timeout(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(socket.timeout('timed out'))
|
||||
@ -64,7 +63,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 1
|
||||
assert 'did not receive banner' in lines[-1]
|
||||
assert 'timed out' in lines[-1]
|
||||
|
||||
|
||||
def test_recv_retry_till_timeout(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(socket.error(errno.EAGAIN, 'Resource temporarily unavailable'))
|
||||
@ -75,7 +74,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 1
|
||||
assert 'did not receive banner' in lines[-1]
|
||||
assert 'timed out' in lines[-1]
|
||||
|
||||
|
||||
def test_recv_retry_till_reset(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(socket.error(errno.EAGAIN, 'Resource temporarily unavailable'))
|
||||
@ -86,7 +85,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 1
|
||||
assert 'did not receive banner' in lines[-1]
|
||||
assert 'reset by peer' in lines[-1]
|
||||
|
||||
|
||||
def test_connection_closed_before_banner(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(socket.error(errno.ECONNRESET, 'Connection reset by peer'))
|
||||
@ -94,7 +93,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 1
|
||||
assert 'did not receive banner' in lines[-1]
|
||||
assert 'reset by peer' in lines[-1]
|
||||
|
||||
|
||||
def test_connection_closed_after_header(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'header line 1\n')
|
||||
@ -105,7 +104,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 3
|
||||
assert 'did not receive banner' in lines[-1]
|
||||
assert 'reset by peer' in lines[-1]
|
||||
|
||||
|
||||
def test_connection_closed_after_banner(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'SSH-2.0-ssh-audit-test\r\n')
|
||||
@ -114,7 +113,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 2
|
||||
assert 'error reading packet' in lines[-1]
|
||||
assert 'reset by peer' in lines[-1]
|
||||
|
||||
|
||||
def test_empty_data_after_banner(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'SSH-2.0-ssh-audit-test\r\n')
|
||||
@ -122,7 +121,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 2
|
||||
assert 'error reading packet' in lines[-1]
|
||||
assert 'empty' in lines[-1]
|
||||
|
||||
|
||||
def test_wrong_data_after_banner(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'SSH-2.0-ssh-audit-test\r\n')
|
||||
@ -131,7 +130,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 2
|
||||
assert 'error reading packet' in lines[-1]
|
||||
assert 'xxx' in lines[-1]
|
||||
|
||||
|
||||
def test_non_ascii_banner(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'SSH-2.0-ssh-audit-test\xc3\xbc\r\n')
|
||||
@ -140,7 +139,7 @@ class TestErrors(object):
|
||||
assert 'error reading packet' in lines[-1]
|
||||
assert 'ASCII' in lines[-2]
|
||||
assert lines[-3].endswith('SSH-2.0-ssh-audit-test?')
|
||||
|
||||
|
||||
def test_nonutf8_data_after_banner(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'SSH-2.0-ssh-audit-test\r\n')
|
||||
@ -149,7 +148,7 @@ class TestErrors(object):
|
||||
assert len(lines) == 2
|
||||
assert 'error reading packet' in lines[-1]
|
||||
assert '\\x81\\xff' in lines[-1]
|
||||
|
||||
|
||||
def test_protocol_mismatch_by_conf(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.rdata.append(b'SSH-1.3-ssh-audit-test\r\n')
|
||||
|
@ -10,7 +10,7 @@ class TestOutput(object):
|
||||
def init(self, ssh_audit):
|
||||
self.Output = ssh_audit.Output
|
||||
self.OutputBuffer = ssh_audit.OutputBuffer
|
||||
|
||||
|
||||
def test_output_buffer_no_lines(self, output_spy):
|
||||
output_spy.begin()
|
||||
with self.OutputBuffer() as obuf:
|
||||
@ -21,13 +21,13 @@ class TestOutput(object):
|
||||
pass
|
||||
obuf.flush()
|
||||
assert output_spy.flush() == []
|
||||
|
||||
|
||||
def test_output_buffer_no_flush(self, output_spy):
|
||||
output_spy.begin()
|
||||
with self.OutputBuffer():
|
||||
print(u'abc')
|
||||
assert output_spy.flush() == []
|
||||
|
||||
|
||||
def test_output_buffer_flush(self, output_spy):
|
||||
output_spy.begin()
|
||||
with self.OutputBuffer() as obuf:
|
||||
@ -36,14 +36,14 @@ class TestOutput(object):
|
||||
print(u'def')
|
||||
obuf.flush()
|
||||
assert output_spy.flush() == [u'abc', u'', u'def']
|
||||
|
||||
|
||||
def test_output_defaults(self):
|
||||
out = self.Output()
|
||||
# default: on
|
||||
assert out.batch is False
|
||||
assert out.use_colors is True
|
||||
assert out.level == 'info'
|
||||
|
||||
|
||||
def test_output_colors(self, output_spy):
|
||||
out = self.Output()
|
||||
# test without colors
|
||||
@ -82,7 +82,7 @@ class TestOutput(object):
|
||||
output_spy.begin()
|
||||
out.fail('fail color')
|
||||
assert output_spy.flush() == [u'\x1b[0;31mfail color\x1b[0m']
|
||||
|
||||
|
||||
def test_output_sep(self, output_spy):
|
||||
out = self.Output()
|
||||
output_spy.begin()
|
||||
@ -90,7 +90,7 @@ class TestOutput(object):
|
||||
out.sep()
|
||||
out.sep()
|
||||
assert output_spy.flush() == [u'', u'', u'']
|
||||
|
||||
|
||||
def test_output_levels(self):
|
||||
out = self.Output()
|
||||
assert out.get_level('info') == 0
|
||||
@ -98,7 +98,7 @@ class TestOutput(object):
|
||||
assert out.get_level('warn') == 1
|
||||
assert out.get_level('fail') == 2
|
||||
assert out.get_level('unknown') > 2
|
||||
|
||||
|
||||
def test_output_level_property(self):
|
||||
out = self.Output()
|
||||
out.level = 'info'
|
||||
@ -111,7 +111,7 @@ class TestOutput(object):
|
||||
assert out.level == 'fail'
|
||||
out.level = 'invalid level'
|
||||
assert out.level == 'unknown'
|
||||
|
||||
|
||||
def test_output_level(self, output_spy):
|
||||
out = self.Output()
|
||||
# visible: all
|
||||
@ -150,7 +150,7 @@ class TestOutput(object):
|
||||
out.warn('warn color')
|
||||
out.fail('fail color')
|
||||
assert len(output_spy.flush()) == 1
|
||||
|
||||
|
||||
def test_output_batch(self, output_spy):
|
||||
out = self.Output()
|
||||
# visible: all
|
||||
|
@ -11,13 +11,13 @@ class TestResolve(object):
|
||||
self.AuditConf = ssh_audit.AuditConf
|
||||
self.audit = ssh_audit.audit
|
||||
self.ssh = ssh_audit.SSH
|
||||
|
||||
|
||||
def _conf(self):
|
||||
conf = self.AuditConf('localhost', 22)
|
||||
conf.colors = False
|
||||
conf.batch = True
|
||||
return conf
|
||||
|
||||
|
||||
def test_resolve_error(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.gsock.addrinfodata['localhost#22'] = socket.gaierror(8, 'hostname nor servname provided, or not known')
|
||||
@ -25,11 +25,11 @@ class TestResolve(object):
|
||||
conf = self._conf()
|
||||
output_spy.begin()
|
||||
with pytest.raises(SystemExit):
|
||||
r = list(s._resolve(conf.ipvo))
|
||||
list(s._resolve(conf.ipvo))
|
||||
lines = output_spy.flush()
|
||||
assert len(lines) == 1
|
||||
assert 'hostname nor servname provided' in lines[-1]
|
||||
|
||||
|
||||
def test_resolve_hostname_without_records(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
vsocket.gsock.addrinfodata['localhost#22'] = []
|
||||
@ -38,36 +38,32 @@ class TestResolve(object):
|
||||
output_spy.begin()
|
||||
r = list(s._resolve(conf.ipvo))
|
||||
assert len(r) == 0
|
||||
|
||||
|
||||
def test_resolve_ipv4(self, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
conf = self._conf()
|
||||
conf.ipv4 = True
|
||||
s = self.ssh.Socket('localhost', 22)
|
||||
r = list(s._resolve(conf.ipvo))
|
||||
assert len(r) == 1
|
||||
assert r[0] == (socket.AF_INET, ('127.0.0.1', 22))
|
||||
|
||||
|
||||
def test_resolve_ipv6(self, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
s = self.ssh.Socket('localhost', 22)
|
||||
conf = self._conf()
|
||||
conf.ipv6 = True
|
||||
r = list(s._resolve(conf.ipvo))
|
||||
assert len(r) == 1
|
||||
assert r[0] == (socket.AF_INET6, ('::1', 22))
|
||||
|
||||
|
||||
def test_resolve_ipv46_both(self, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
s = self.ssh.Socket('localhost', 22)
|
||||
conf = self._conf()
|
||||
r = list(s._resolve(conf.ipvo))
|
||||
assert len(r) == 2
|
||||
assert r[0] == (socket.AF_INET, ('127.0.0.1', 22))
|
||||
assert r[1] == (socket.AF_INET6, ('::1', 22))
|
||||
|
||||
|
||||
def test_resolve_ipv46_order(self, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
s = self.ssh.Socket('localhost', 22)
|
||||
conf = self._conf()
|
||||
conf.ipv4 = True
|
||||
|
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import socket
|
||||
import pytest
|
||||
|
||||
|
||||
@ -9,21 +8,21 @@ class TestSocket(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
self.ssh = ssh_audit.SSH
|
||||
|
||||
|
||||
def test_invalid_host(self, virtual_socket):
|
||||
with pytest.raises(ValueError):
|
||||
s = self.ssh.Socket(None, 22)
|
||||
|
||||
self.ssh.Socket(None, 22)
|
||||
|
||||
def test_invalid_port(self, virtual_socket):
|
||||
with pytest.raises(ValueError):
|
||||
s = self.ssh.Socket('localhost', 'abc')
|
||||
self.ssh.Socket('localhost', 'abc')
|
||||
with pytest.raises(ValueError):
|
||||
s = self.ssh.Socket('localhost', -1)
|
||||
self.ssh.Socket('localhost', -1)
|
||||
with pytest.raises(ValueError):
|
||||
s = self.ssh.Socket('localhost', 0)
|
||||
self.ssh.Socket('localhost', 0)
|
||||
with pytest.raises(ValueError):
|
||||
s = self.ssh.Socket('localhost', 65536)
|
||||
|
||||
self.ssh.Socket('localhost', 65536)
|
||||
|
||||
def test_not_connected_socket(self, virtual_socket):
|
||||
sock = self.ssh.Socket('localhost', 22)
|
||||
banner, header, err = sock.get_banner()
|
||||
|
@ -8,13 +8,13 @@ class TestSoftware(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
self.ssh = ssh_audit.SSH
|
||||
|
||||
|
||||
def test_unknown_software(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
assert ps('SSH-1.5') is None
|
||||
assert ps('SSH-1.99-AlfaMegaServer') is None
|
||||
assert ps('SSH-2.0-BetaMegaServer 0.0.1') is None
|
||||
|
||||
|
||||
def test_openssh_software(self):
|
||||
# pylint: disable=too-many-statements
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
@ -102,7 +102,7 @@ class TestSoftware(object):
|
||||
assert s.display(True) == str(s)
|
||||
assert s.display(False) == 'OpenSSH 5.9'
|
||||
assert repr(s) == '<Software(product=OpenSSH, version=5.9, patch=CASPUR)>'
|
||||
|
||||
|
||||
def test_dropbear_software(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
# common
|
||||
@ -153,7 +153,7 @@ class TestSoftware(object):
|
||||
assert s.display(True) == str(s)
|
||||
assert s.display(False) == 'Dropbear SSH 2014.66'
|
||||
assert repr(s) == '<Software(product=Dropbear SSH, version=2014.66, patch=agbn_1)>'
|
||||
|
||||
|
||||
def test_libssh_software(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
# common
|
||||
@ -179,7 +179,7 @@ class TestSoftware(object):
|
||||
assert s.display(True) == str(s)
|
||||
assert s.display(False) == str(s)
|
||||
assert repr(s) == '<Software(product=libssh, version=0.7.4)>'
|
||||
|
||||
|
||||
def test_romsshell_software(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
# common
|
||||
@ -194,7 +194,7 @@ class TestSoftware(object):
|
||||
assert s.display(True) == str(s)
|
||||
assert s.display(False) == str(s)
|
||||
assert repr(s) == '<Software(vendor=Allegro Software, product=RomSShell, version=5.40)>'
|
||||
|
||||
|
||||
def test_hp_ilo_software(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
# common
|
||||
@ -209,7 +209,7 @@ class TestSoftware(object):
|
||||
assert s.display(True) == str(s)
|
||||
assert s.display(False) == str(s)
|
||||
assert repr(s) == '<Software(vendor=HP, product=iLO (Integrated Lights-Out) sshd, version=0.2.1)>'
|
||||
|
||||
|
||||
def test_cisco_software(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
# common
|
||||
@ -224,7 +224,7 @@ class TestSoftware(object):
|
||||
assert s.display(True) == str(s)
|
||||
assert s.display(False) == str(s)
|
||||
assert repr(s) == '<Software(vendor=Cisco, product=IOS/PIX sshd, version=1.25)>'
|
||||
|
||||
|
||||
def test_software_os(self):
|
||||
ps = lambda x: self.ssh.Software.parse(self.ssh.Banner.parse(x)) # noqa
|
||||
# unknown
|
||||
|
@ -14,7 +14,7 @@ class TestSSH1(object):
|
||||
self.wbuf = ssh_audit.WriteBuf
|
||||
self.audit = ssh_audit.audit
|
||||
self.AuditConf = ssh_audit.AuditConf
|
||||
|
||||
|
||||
def _conf(self):
|
||||
conf = self.AuditConf('localhost', 22)
|
||||
conf.colors = False
|
||||
@ -23,7 +23,7 @@ class TestSSH1(object):
|
||||
conf.ssh1 = True
|
||||
conf.ssh2 = False
|
||||
return conf
|
||||
|
||||
|
||||
def _create_ssh1_packet(self, payload, valid_crc=True):
|
||||
padding = -(len(payload) + 4) % 8
|
||||
plen = len(payload) + 4
|
||||
@ -31,15 +31,15 @@ class TestSSH1(object):
|
||||
cksum = self.ssh1.crc32(pad_bytes + payload) if valid_crc else 0
|
||||
data = struct.pack('>I', plen) + pad_bytes + payload + struct.pack('>I', cksum)
|
||||
return data
|
||||
|
||||
|
||||
@classmethod
|
||||
def _server_key(cls):
|
||||
return (1024, 0x10001, 0xee6552da432e0ac2c422df1a51287507748bfe3b5e3e4fa989a8f49fdc163a17754939ef18ef8a667ea3b71036a151fcd7f5e01ceef1e4439864baf3ac569047582c69d6c128212e0980dcb3168f00d371004039983f6033cd785b8b8f85096c7d9405cbfdc664e27c966356a6b4eb6ee20ad43414b50de18b22829c1880b551)
|
||||
|
||||
|
||||
@classmethod
|
||||
def _host_key(cls):
|
||||
return (2048, 0x10001, 0xdfa20cd2a530ccc8c870aa60d9feb3b35deeab81c3215a96557abbd683d21f4600f38e475d87100da9a4404220eeb3bb5584e5a2b5b48ffda58530ea19104a32577d7459d91e76aa711b241050f4cc6d5327ccce254f371acad3be56d46eb5919b73f20dbdb1177b700f00891c5bf4ed128bb90ed541b778288285bcfa28432ab5cbcb8321b6e24760e998e0daa519f093a631e44276d7dd252ce0c08c75e2ab28a7349ead779f97d0f20a6d413bf3623cd216dc35375f6366690bcc41e3b2d5465840ec7ee0dc7e3f1c101d674a0c7dbccbc3942788b111396add2f8153b46a0e4b50d66e57ee92958f1c860dd97cc0e40e32febff915343ed53573142bdf4b)
|
||||
|
||||
|
||||
def _pkm_payload(self):
|
||||
w = self.wbuf()
|
||||
w.write(b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff')
|
||||
@ -51,11 +51,11 @@ class TestSSH1(object):
|
||||
w.write_int(72)
|
||||
w.write_int(36)
|
||||
return w.write_flush()
|
||||
|
||||
|
||||
def test_crc32(self):
|
||||
assert self.ssh1.crc32(b'') == 0x00
|
||||
assert self.ssh1.crc32(b'The quick brown fox jumps over the lazy dog') == 0xb9c60808
|
||||
|
||||
|
||||
def test_fingerprint(self):
|
||||
# pylint: disable=protected-access
|
||||
b, e, m = self._host_key()
|
||||
@ -65,7 +65,7 @@ class TestSSH1(object):
|
||||
assert b == 2048
|
||||
assert fp.md5 == 'MD5:9d:26:f8:39:fc:20:9d:9b:ca:cc:4a:0f:e1:93:f5:96'
|
||||
assert fp.sha256 == 'SHA256:vZdx3mhzbvVJmn08t/ruv8WDhJ9jfKYsCTuSzot+QIs'
|
||||
|
||||
|
||||
def _assert_pkm_keys(self, pkm, skey, hkey):
|
||||
b, e, m = skey
|
||||
assert pkm.server_key_bits == b
|
||||
@ -75,7 +75,7 @@ class TestSSH1(object):
|
||||
assert pkm.host_key_bits == b
|
||||
assert pkm.host_key_public_exponent == e
|
||||
assert pkm.host_key_public_modulus == m
|
||||
|
||||
|
||||
def _assert_pkm_fields(self, pkm, skey, hkey):
|
||||
assert pkm is not None
|
||||
assert pkm.cookie == b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
||||
@ -88,25 +88,25 @@ class TestSSH1(object):
|
||||
fp = self.ssh.Fingerprint(pkm.host_key_fingerprint_data)
|
||||
assert fp.md5 == 'MD5:9d:26:f8:39:fc:20:9d:9b:ca:cc:4a:0f:e1:93:f5:96'
|
||||
assert fp.sha256 == 'SHA256:vZdx3mhzbvVJmn08t/ruv8WDhJ9jfKYsCTuSzot+QIs'
|
||||
|
||||
|
||||
def test_pkm_init(self):
|
||||
cookie = b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
||||
pflags, cmask, amask = 2, 72, 36
|
||||
skey, hkey = self._server_key(), self._host_key()
|
||||
pkm = self.ssh1.PublicKeyMessage(cookie, skey, hkey, pflags, cmask, amask)
|
||||
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):
|
||||
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):
|
||||
print(hkey2)
|
||||
pkm = self.ssh1.PublicKeyMessage(cookie, skey, hkey2, pflags, cmask, amask)
|
||||
|
||||
|
||||
def test_pkm_read(self):
|
||||
pkm = self.ssh1.PublicKeyMessage.parse(self._pkm_payload())
|
||||
self._assert_pkm_fields(pkm, self._server_key(), self._host_key())
|
||||
|
||||
|
||||
def test_pkm_payload(self):
|
||||
cookie = b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
||||
skey, hkey = self._server_key(), self._host_key()
|
||||
@ -114,7 +114,7 @@ class TestSSH1(object):
|
||||
pkm1 = self.ssh1.PublicKeyMessage(cookie, skey, hkey, pflags, cmask, amask)
|
||||
pkm2 = self.ssh1.PublicKeyMessage.parse(self._pkm_payload())
|
||||
assert pkm1.payload == pkm2.payload
|
||||
|
||||
|
||||
def test_ssh1_server_simple(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
w = self.wbuf()
|
||||
@ -126,7 +126,7 @@ class TestSSH1(object):
|
||||
self.audit(self._conf())
|
||||
lines = output_spy.flush()
|
||||
assert len(lines) == 13
|
||||
|
||||
|
||||
def test_ssh1_server_invalid_first_packet(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
w = self.wbuf()
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import struct, os
|
||||
import os
|
||||
import struct
|
||||
import pytest
|
||||
|
||||
|
||||
@ -14,7 +15,7 @@ class TestSSH2(object):
|
||||
self.wbuf = ssh_audit.WriteBuf
|
||||
self.audit = ssh_audit.audit
|
||||
self.AuditConf = ssh_audit.AuditConf
|
||||
|
||||
|
||||
def _conf(self):
|
||||
conf = self.AuditConf('localhost', 22)
|
||||
conf.colors = False
|
||||
@ -23,7 +24,7 @@ class TestSSH2(object):
|
||||
conf.ssh1 = False
|
||||
conf.ssh2 = True
|
||||
return conf
|
||||
|
||||
|
||||
@classmethod
|
||||
def _create_ssh2_packet(cls, payload):
|
||||
padding = -(len(payload) + 5) % 8
|
||||
@ -33,7 +34,7 @@ class TestSSH2(object):
|
||||
pad_bytes = b'\x00' * padding
|
||||
data = struct.pack('>Ib', plen, padding) + payload + pad_bytes
|
||||
return data
|
||||
|
||||
|
||||
def _kex_payload(self):
|
||||
w = self.wbuf()
|
||||
w.write(b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff')
|
||||
@ -50,7 +51,7 @@ class TestSSH2(object):
|
||||
w.write_byte(False)
|
||||
w.write_int(0)
|
||||
return w.write_flush()
|
||||
|
||||
|
||||
def test_kex_read(self):
|
||||
kex = self.ssh2.Kex.parse(self._kex_payload())
|
||||
assert kex is not None
|
||||
@ -69,7 +70,7 @@ class TestSSH2(object):
|
||||
assert kex.server.languages == [u'']
|
||||
assert kex.follows is False
|
||||
assert kex.unused == 0
|
||||
|
||||
|
||||
def _get_empty_kex(self, cookie=None):
|
||||
kex_algs, key_algs = [], []
|
||||
enc, mac, compression, languages = [], [], ['none'], []
|
||||
@ -80,7 +81,7 @@ class TestSSH2(object):
|
||||
cookie = os.urandom(16)
|
||||
kex = self.ssh2.Kex(cookie, kex_algs, key_algs, cli, srv, 0)
|
||||
return kex
|
||||
|
||||
|
||||
def _get_kex_variat1(self):
|
||||
cookie = b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
|
||||
kex = self._get_empty_kex(cookie)
|
||||
@ -123,12 +124,12 @@ class TestSSH2(object):
|
||||
continue
|
||||
kex.client.compression.append(a)
|
||||
return kex
|
||||
|
||||
|
||||
def test_key_payload(self):
|
||||
kex1 = self._get_kex_variat1()
|
||||
kex2 = self.ssh2.Kex.parse(self._kex_payload())
|
||||
assert kex1.payload == kex2.payload
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily skip this test to have a working test suite!")
|
||||
def test_ssh2_server_simple(self, output_spy, virtual_socket):
|
||||
vsocket = virtual_socket
|
||||
|
@ -8,24 +8,24 @@ class TestSSHAlgorithm(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
self.ssh = ssh_audit.SSH
|
||||
|
||||
|
||||
def _tf(self, v, s=None):
|
||||
return self.ssh.Algorithm.Timeframe().update(v, s)
|
||||
|
||||
|
||||
def test_get_ssh_version(self):
|
||||
def ver(v):
|
||||
return self.ssh.Algorithm.get_ssh_version(v)
|
||||
|
||||
|
||||
assert ver('7.5') == ('OpenSSH', '7.5', False)
|
||||
assert ver('7.5C') == ('OpenSSH', '7.5', True)
|
||||
assert ver('d2016.74') == ('Dropbear SSH', '2016.74', False)
|
||||
assert ver('l10.7.4') == ('libssh', '0.7.4', False)
|
||||
assert ver('')[1] == ''
|
||||
|
||||
|
||||
def test_get_since_text(self):
|
||||
def gst(v):
|
||||
return self.ssh.Algorithm.get_since_text(v)
|
||||
|
||||
|
||||
assert gst(['7.5']) == 'available since OpenSSH 7.5'
|
||||
assert gst(['7.5C']) == 'available since OpenSSH 7.5 (client only)'
|
||||
assert gst(['7.5,']) == 'available since OpenSSH 7.5'
|
||||
@ -33,12 +33,12 @@ class TestSSHAlgorithm(object):
|
||||
assert gst(['7.5,d2016.73']) == 'available since OpenSSH 7.5, Dropbear SSH 2016.73'
|
||||
assert gst(['l10.7.4']) is None
|
||||
assert gst([]) is None
|
||||
|
||||
|
||||
def test_timeframe_creation(self):
|
||||
# pylint: disable=line-too-long,too-many-statements
|
||||
def cmp_tf(v, s, r):
|
||||
assert str(self._tf(v, s)) == str(r)
|
||||
|
||||
|
||||
cmp_tf(['6.2'], None, {'OpenSSH': ['6.2', None, '6.2', None]})
|
||||
cmp_tf(['6.2'], True, {'OpenSSH': ['6.2', None, None, None]})
|
||||
cmp_tf(['6.2'], False, {'OpenSSH': [None, None, '6.2', None]})
|
||||
@ -57,7 +57,7 @@ class TestSSHAlgorithm(object):
|
||||
cmp_tf(['6.2C,6.3'], None, {'OpenSSH': ['6.3', None, '6.2', None]})
|
||||
cmp_tf(['6.2C,6.3'], True, {'OpenSSH': ['6.3', None, None, None]})
|
||||
cmp_tf(['6.2C,6.3'], False, {'OpenSSH': [None, None, '6.2', None]})
|
||||
|
||||
|
||||
cmp_tf(['6.2', '6.6'], None, {'OpenSSH': ['6.2', '6.6', '6.2', '6.6']})
|
||||
cmp_tf(['6.2', '6.6'], True, {'OpenSSH': ['6.2', '6.6', None, None]})
|
||||
cmp_tf(['6.2', '6.6'], False, {'OpenSSH': [None, None, '6.2', '6.6']})
|
||||
@ -76,7 +76,7 @@ class TestSSHAlgorithm(object):
|
||||
cmp_tf(['6.2C,6.3', '6.6'], None, {'OpenSSH': ['6.3', '6.6', '6.2', '6.6']})
|
||||
cmp_tf(['6.2C,6.3', '6.6'], True, {'OpenSSH': ['6.3', '6.6', None, None]})
|
||||
cmp_tf(['6.2C,6.3', '6.6'], False, {'OpenSSH': [None, None, '6.2', '6.6']})
|
||||
|
||||
|
||||
cmp_tf(['6.2', '6.6', None], None, {'OpenSSH': ['6.2', '6.6', '6.2', None]})
|
||||
cmp_tf(['6.2', '6.6', None], True, {'OpenSSH': ['6.2', '6.6', None, None]})
|
||||
cmp_tf(['6.2', '6.6', None], False, {'OpenSSH': [None, None, '6.2', None]})
|
||||
@ -95,7 +95,7 @@ class TestSSHAlgorithm(object):
|
||||
cmp_tf(['6.3C,6.2', '6.6', None], None, {'OpenSSH': ['6.2', '6.6', '6.3', None]})
|
||||
cmp_tf(['6.3C,6.2', '6.6', None], True, {'OpenSSH': ['6.2', '6.6', None, None]})
|
||||
cmp_tf(['6.3C,6.2', '6.6', None], False, {'OpenSSH': [None, None, '6.3', None]})
|
||||
|
||||
|
||||
cmp_tf(['6.2', '6.6', '7.1'], None, {'OpenSSH': ['6.2', '6.6', '6.2', '7.1']})
|
||||
cmp_tf(['6.2', '6.6', '7.1'], True, {'OpenSSH': ['6.2', '6.6', None, None]})
|
||||
cmp_tf(['6.2', '6.6', '7.1'], False, {'OpenSSH': [None, None, '6.2', '7.1']})
|
||||
@ -111,7 +111,7 @@ class TestSSHAlgorithm(object):
|
||||
cmp_tf(['6.3C,6.2', '6.6', '7.1'], None, {'OpenSSH': ['6.2', '6.6', '6.3', '7.1']})
|
||||
cmp_tf(['6.3C,6.2', '6.6', '7.1'], True, {'OpenSSH': ['6.2', '6.6', None, None]})
|
||||
cmp_tf(['6.3C,6.2', '6.6', '7.1'], False, {'OpenSSH': [None, None, '6.3', '7.1']})
|
||||
|
||||
|
||||
tf1 = self._tf(['6.1,d2016.72,6.2C', '6.6,d2016.73', '7.1,d2016.74'])
|
||||
tf2 = self._tf(['d2016.72,6.2C,6.1', 'd2016.73,6.6', 'd2016.74,7.1'])
|
||||
tf3 = self._tf(['d2016.72,6.2C,6.1', '6.6,d2016.73', '7.1,d2016.74'])
|
||||
@ -123,7 +123,7 @@ class TestSSHAlgorithm(object):
|
||||
assert dv in str(tf1) and dv in str(tf2) and dv in str(tf3)
|
||||
assert ov in repr(tf1) and ov in repr(tf2) and ov in repr(tf3)
|
||||
assert dv in repr(tf1) and dv in repr(tf2) and dv in repr(tf3)
|
||||
|
||||
|
||||
def test_timeframe_object(self):
|
||||
tf = self._tf(['6.1,6.2C', '6.6', '7.1'])
|
||||
assert 'OpenSSH' in tf
|
||||
@ -138,7 +138,7 @@ class TestSSHAlgorithm(object):
|
||||
assert tf.get_till('OpenSSH', True) == '6.6'
|
||||
assert tf.get_from('OpenSSH', False) == '6.2'
|
||||
assert tf.get_till('OpenSSH', False) == '7.1'
|
||||
|
||||
|
||||
tf = self._tf(['6.1,d2016.72,6.2C', '6.6,d2016.73', '7.1,d2016.74'])
|
||||
assert 'OpenSSH' in tf
|
||||
assert 'Dropbear SSH' in tf
|
||||
|
@ -10,7 +10,7 @@ class TestUtils(object):
|
||||
def init(self, ssh_audit):
|
||||
self.utils = ssh_audit.Utils
|
||||
self.PY3 = sys.version_info >= (3,)
|
||||
|
||||
|
||||
def test_to_bytes_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -22,7 +22,7 @@ class TestUtils(object):
|
||||
# other
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_bytes(123)
|
||||
|
||||
|
||||
def test_to_bytes_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -34,7 +34,7 @@ class TestUtils(object):
|
||||
# other
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_bytes(123)
|
||||
|
||||
|
||||
def test_to_utext_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -46,7 +46,7 @@ class TestUtils(object):
|
||||
# other
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_utext(123)
|
||||
|
||||
|
||||
def test_to_utext_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -58,7 +58,7 @@ class TestUtils(object):
|
||||
# other
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_utext(123)
|
||||
|
||||
|
||||
def test_to_ntext_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -70,7 +70,7 @@ class TestUtils(object):
|
||||
# other
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_ntext(123)
|
||||
|
||||
|
||||
def test_to_ntext_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -82,7 +82,7 @@ class TestUtils(object):
|
||||
# other
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_ntext(123)
|
||||
|
||||
|
||||
def test_is_ascii_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -94,7 +94,7 @@ class TestUtils(object):
|
||||
assert self.utils.is_ascii('fran\xc3\xa7ais') is False
|
||||
# other
|
||||
assert self.utils.is_ascii(123) is False
|
||||
|
||||
|
||||
def test_is_ascii_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -105,7 +105,7 @@ class TestUtils(object):
|
||||
assert self.utils.is_ascii(u'fran\xe7ais') is False
|
||||
# other
|
||||
assert self.utils.is_ascii(123) is False
|
||||
|
||||
|
||||
def test_to_ascii_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -119,7 +119,7 @@ class TestUtils(object):
|
||||
assert self.utils.to_ascii('fran\xc3\xa7ais', 'ignore') == 'franais'
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_ascii(123)
|
||||
|
||||
|
||||
def test_to_ascii_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -132,7 +132,7 @@ class TestUtils(object):
|
||||
assert self.utils.to_ascii(u'fran\xe7ais', 'ignore') == 'franais'
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_ascii(123)
|
||||
|
||||
|
||||
def test_is_print_ascii_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -147,7 +147,7 @@ class TestUtils(object):
|
||||
assert self.utils.is_print_ascii('fran\xc3\xa7ais') is False
|
||||
# other
|
||||
assert self.utils.is_print_ascii(123) is False
|
||||
|
||||
|
||||
def test_is_print_ascii_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -160,7 +160,7 @@ class TestUtils(object):
|
||||
assert self.utils.is_print_ascii(u'fran\xe7ais') is False
|
||||
# other
|
||||
assert self.utils.is_print_ascii(123) is False
|
||||
|
||||
|
||||
def test_to_print_ascii_py2(self):
|
||||
if self.PY3:
|
||||
return
|
||||
@ -180,7 +180,7 @@ class TestUtils(object):
|
||||
assert self.utils.to_print_ascii('fran\xc3\xa7ais\n', 'ignore') == 'franais'
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_print_ascii(123)
|
||||
|
||||
|
||||
def test_to_print_ascii_py3(self):
|
||||
if not self.PY3:
|
||||
return
|
||||
@ -199,18 +199,18 @@ class TestUtils(object):
|
||||
assert self.utils.to_print_ascii(u'fran\xe7ais\n', 'ignore') == 'franais'
|
||||
with pytest.raises(TypeError):
|
||||
self.utils.to_print_ascii(123)
|
||||
|
||||
|
||||
def test_ctoi(self):
|
||||
assert self.utils.ctoi(123) == 123
|
||||
assert self.utils.ctoi('ABC') == 65
|
||||
|
||||
|
||||
def test_parse_int(self):
|
||||
assert self.utils.parse_int(123) == 123
|
||||
assert self.utils.parse_int('123') == 123
|
||||
assert self.utils.parse_int(-123) == -123
|
||||
assert self.utils.parse_int('-123') == -123
|
||||
assert self.utils.parse_int('abc') == 0
|
||||
|
||||
|
||||
def test_unique_seq(self):
|
||||
assert self.utils.unique_seq((1, 2, 2, 3, 3, 3)) == (1, 2, 3)
|
||||
assert self.utils.unique_seq((3, 3, 3, 2, 2, 1)) == (3, 2, 1)
|
||||
|
@ -8,19 +8,19 @@ class TestVersionCompare(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
self.ssh = ssh_audit.SSH
|
||||
|
||||
|
||||
def get_dropbear_software(self, v):
|
||||
b = self.ssh.Banner.parse('SSH-2.0-dropbear_{0}'.format(v))
|
||||
return self.ssh.Software.parse(b)
|
||||
|
||||
|
||||
def get_openssh_software(self, v):
|
||||
b = self.ssh.Banner.parse('SSH-2.0-OpenSSH_{0}'.format(v))
|
||||
return self.ssh.Software.parse(b)
|
||||
|
||||
|
||||
def get_libssh_software(self, v):
|
||||
b = self.ssh.Banner.parse('SSH-2.0-libssh-{0}'.format(v))
|
||||
return self.ssh.Software.parse(b)
|
||||
|
||||
|
||||
def test_dropbear_compare_version_pre_years(self):
|
||||
s = self.get_dropbear_software('0.44')
|
||||
assert s.compare_version(None) == 1
|
||||
@ -32,7 +32,7 @@ class TestVersionCompare(object):
|
||||
assert s.between_versions('0.43', '0.45')
|
||||
assert s.between_versions('0.43', '0.43') is False
|
||||
assert s.between_versions('0.45', '0.43') is False
|
||||
|
||||
|
||||
def test_dropbear_compare_version_with_years(self):
|
||||
s = self.get_dropbear_software('2015.71')
|
||||
assert s.compare_version(None) == 1
|
||||
@ -44,7 +44,7 @@ class TestVersionCompare(object):
|
||||
assert s.between_versions('2014.66', '2016.74')
|
||||
assert s.between_versions('2014.66', '2015.69') is False
|
||||
assert s.between_versions('2016.74', '2014.66') is False
|
||||
|
||||
|
||||
def test_dropbear_compare_version_mixed(self):
|
||||
s = self.get_dropbear_software('0.53.1')
|
||||
assert s.compare_version(None) == 1
|
||||
@ -56,7 +56,7 @@ class TestVersionCompare(object):
|
||||
assert s.between_versions('0.53', '2011.54')
|
||||
assert s.between_versions('0.53', '0.53') is False
|
||||
assert s.between_versions('2011.54', '0.53') is False
|
||||
|
||||
|
||||
def test_dropbear_compare_version_patchlevel(self):
|
||||
s1 = self.get_dropbear_software('0.44')
|
||||
s2 = self.get_dropbear_software('0.44test3')
|
||||
@ -80,7 +80,7 @@ class TestVersionCompare(object):
|
||||
assert s2.between_versions('0.44', '0.43') is False
|
||||
assert s1.compare_version(s2) > 0
|
||||
assert s2.compare_version(s1) < 0
|
||||
|
||||
|
||||
def test_dropbear_compare_version_sequential(self):
|
||||
versions = []
|
||||
for i in range(28, 44):
|
||||
@ -105,18 +105,18 @@ class TestVersionCompare(object):
|
||||
versions.append('2015.{0}'.format(i))
|
||||
for i in range(72, 75):
|
||||
versions.append('2016.{0}'.format(i))
|
||||
l = len(versions)
|
||||
for i in range(l):
|
||||
length = len(versions)
|
||||
for i in range(length):
|
||||
v = versions[i]
|
||||
s = self.get_dropbear_software(v)
|
||||
assert s.compare_version(v) == 0
|
||||
if i - 1 >= 0:
|
||||
vbefore = versions[i - 1]
|
||||
assert s.compare_version(vbefore) > 0
|
||||
if i + 1 < l:
|
||||
if i + 1 < length:
|
||||
vnext = versions[i + 1]
|
||||
assert s.compare_version(vnext) < 0
|
||||
|
||||
|
||||
def test_openssh_compare_version_simple(self):
|
||||
s = self.get_openssh_software('3.7.1')
|
||||
assert s.compare_version(None) == 1
|
||||
@ -128,7 +128,7 @@ class TestVersionCompare(object):
|
||||
assert s.between_versions('3.7', '3.8')
|
||||
assert s.between_versions('3.6', '3.7') is False
|
||||
assert s.between_versions('3.8', '3.7') is False
|
||||
|
||||
|
||||
def test_openssh_compare_version_patchlevel(self):
|
||||
s1 = self.get_openssh_software('2.1.1')
|
||||
s2 = self.get_openssh_software('2.1.1p2')
|
||||
@ -141,7 +141,7 @@ class TestVersionCompare(object):
|
||||
assert s2.compare_version('2.1.1p3') < 0
|
||||
assert s1.compare_version(s2) == 0
|
||||
assert s2.compare_version(s1) == 0
|
||||
|
||||
|
||||
def test_openbsd_compare_version_sequential(self):
|
||||
versions = []
|
||||
for v in ['1.2.3', '2.1.0', '2.1.1', '2.2.0', '2.3.0']:
|
||||
@ -164,18 +164,18 @@ class TestVersionCompare(object):
|
||||
versions.append('6.{0}'.format(i))
|
||||
for i in range(0, 4):
|
||||
versions.append('7.{0}'.format(i))
|
||||
l = len(versions)
|
||||
for i in range(l):
|
||||
length = len(versions)
|
||||
for i in range(length):
|
||||
v = versions[i]
|
||||
s = self.get_openssh_software(v)
|
||||
assert s.compare_version(v) == 0
|
||||
if i - 1 >= 0:
|
||||
vbefore = versions[i - 1]
|
||||
assert s.compare_version(vbefore) > 0
|
||||
if i + 1 < l:
|
||||
if i + 1 < length:
|
||||
vnext = versions[i + 1]
|
||||
assert s.compare_version(vnext) < 0
|
||||
|
||||
|
||||
def test_libssh_compare_version_simple(self):
|
||||
s = self.get_libssh_software('0.3')
|
||||
assert s.compare_version(None) == 1
|
||||
@ -187,7 +187,7 @@ class TestVersionCompare(object):
|
||||
assert s.between_versions('0.2', '0.3.1')
|
||||
assert s.between_versions('0.1', '0.2') is False
|
||||
assert s.between_versions('0.3.1', '0.2') is False
|
||||
|
||||
|
||||
def test_libssh_compare_version_sequential(self):
|
||||
versions = []
|
||||
for v in ['0.2', '0.3']:
|
||||
@ -202,14 +202,14 @@ class TestVersionCompare(object):
|
||||
versions.append('0.6.{0}'.format(i))
|
||||
for i in range(0, 5):
|
||||
versions.append('0.7.{0}'.format(i))
|
||||
l = len(versions)
|
||||
for i in range(l):
|
||||
length = len(versions)
|
||||
for i in range(length):
|
||||
v = versions[i]
|
||||
s = self.get_libssh_software(v)
|
||||
assert s.compare_version(v) == 0
|
||||
if i - 1 >= 0:
|
||||
vbefore = versions[i - 1]
|
||||
assert s.compare_version(vbefore) > 0
|
||||
if i + 1 < l:
|
||||
if i + 1 < length:
|
||||
vnext = versions[i + 1]
|
||||
assert s.compare_version(vnext) < 0
|
||||
|
51
tox.ini
51
tox.ini
@ -80,7 +80,7 @@ commands =
|
||||
deps =
|
||||
flake8
|
||||
commands =
|
||||
flake8 {posargs:{env:SSHAUDIT}}
|
||||
flake8 {posargs:{env:SSHAUDIT} {toxinidir}/packages/setup.py {toxinidir}/test} --statistics
|
||||
|
||||
[testenv:vulture]
|
||||
deps =
|
||||
@ -137,42 +137,13 @@ max-module-lines = 2500
|
||||
|
||||
[flake8]
|
||||
ignore =
|
||||
# indentation contains tabs
|
||||
W191,
|
||||
# blank line contains whitespace
|
||||
W293,
|
||||
# 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
|
||||
E126, # continuation line over-indented for hanging 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'
|
||||
E741, # ambiguous variable name 'l'
|
||||
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
|
||||
W605, # invalid escape sequence '\s'
|
||||
W191, # indentation contains tabs
|
||||
E101, # indentation contains mixed spaces and tabs
|
||||
E241, # multiple spaces after operator; should be kept for tabular data
|
||||
E501, # line too long
|
||||
E117, # over-indented
|
||||
E126, # continuation line over-indented for hanging indent
|
||||
E128, # continuation line under-indented for visual indent
|
||||
E722, # do not use bare 'except'
|
||||
F601, # dictionary key 'ecdsa-sha2-1.3.132.0.10' repeated with different values
|
||||
W504, # line break after binary operator; this (or W503) has to stay
|
||||
|
Loading…
Reference in New Issue
Block a user