mirror of
https://github.com/jtesta/ssh-audit.git
synced 2024-11-25 12:01:40 +01:00
Recognize libssh (software, history, compatibility, security, etc). Closes #8.
This commit is contained in:
parent
0c98bc1397
commit
5de7b913fd
102
ssh-audit.py
102
ssh-audit.py
@ -472,6 +472,7 @@ class SSH(object):
|
||||
class Product(object):
|
||||
OpenSSH = 'OpenSSH'
|
||||
DropbearSSH = 'Dropbear SSH'
|
||||
LibSSH = 'libssh'
|
||||
|
||||
class Software(object):
|
||||
def __init__(self, vendor, product, version, patch, os):
|
||||
@ -632,6 +633,12 @@ class SSH(object):
|
||||
v = None
|
||||
os = cls._extract_os(banner.comments)
|
||||
return cls(v, p, mx.group(1), patch, os)
|
||||
mx = re.match(r'^libssh-([\d\.]+\d+)(.*)', software)
|
||||
if mx:
|
||||
patch = cls._fix_patch(mx.group(2))
|
||||
v, p = None, SSH.Product.LibSSH
|
||||
os = cls._extract_os(banner.comments)
|
||||
return cls(v, p, mx.group(1), patch, os)
|
||||
mx = re.match(r'^RomSShell_([\d\.]+\d+)(.*)', software)
|
||||
if mx:
|
||||
patch = cls._fix_patch(mx.group(2))
|
||||
@ -718,21 +725,34 @@ class SSH(object):
|
||||
class Security(object):
|
||||
CVE = {
|
||||
'Dropbear SSH': [
|
||||
['0.44', '2015.71', 1, 'CVE-2016-3116', 5.5, 'bypass command restrictions via xauth command injection.'],
|
||||
['0.28', '2013.58', 1, 'CVE-2013-4434', 5.0, 'discover valid usernames through different time delays.'],
|
||||
['0.28', '2013.58', 1, 'CVE-2013-4421', 5.0, 'cause DoS (memory consumption) via a compressed packet.'],
|
||||
['0.52', '2011.54', 1, 'CVE-2012-0920', 7.1, 'execute arbitrary code or bypass command restrictions.'],
|
||||
['0.40', '0.48.1', 1, 'CVE-2007-1099', 7.5, 'conduct a MitM attack (no warning for hostkey mismatch).'],
|
||||
['0.28', '0.47', 1, 'CVE-2006-1206', 7.5, 'cause DoS (slot exhaustion) via large number of connections.'],
|
||||
['0.39', '0.47', 1, 'CVE-2006-0225', 4.6, 'execute arbitrary commands via scp with crafted filenames.'],
|
||||
['0.28', '0.46', 1, 'CVE-2005-4178', 6.5, 'execute arbitrary code via buffer overflow vulnerability.'],
|
||||
['0.28', '0.42', 1, 'CVE-2004-2486', 7.5, 'execute arbitrary code via DSS verification code.'],
|
||||
]
|
||||
['0.44', '2015.71', 1, 'CVE-2016-3116', 5.5, 'bypass command restrictions via xauth command injection'],
|
||||
['0.28', '2013.58', 1, 'CVE-2013-4434', 5.0, 'discover valid usernames through different time delays'],
|
||||
['0.28', '2013.58', 1, 'CVE-2013-4421', 5.0, 'cause DoS (memory consumption) via a compressed packet'],
|
||||
['0.52', '2011.54', 1, 'CVE-2012-0920', 7.1, 'execute arbitrary code or bypass command restrictions'],
|
||||
['0.40', '0.48.1', 1, 'CVE-2007-1099', 7.5, 'conduct a MitM attack (no warning for hostkey mismatch)'],
|
||||
['0.28', '0.47', 1, 'CVE-2006-1206', 7.5, 'cause DoS (slot exhaustion) via large number of connections'],
|
||||
['0.39', '0.47', 1, 'CVE-2006-0225', 4.6, 'execute arbitrary commands via scp with crafted filenames'],
|
||||
['0.28', '0.46', 1, 'CVE-2005-4178', 6.5, 'execute arbitrary code via buffer overflow vulnerability'],
|
||||
['0.28', '0.42', 1, 'CVE-2004-2486', 7.5, 'execute arbitrary code via DSS verification code']],
|
||||
'libssh': [
|
||||
['0.1', '0.7.2', 1, 'CVE-2016-0739', 4.3, 'conduct a MitM attack (weakness in DH key generation)'],
|
||||
['0.5.1', '0.6.4', 1, 'CVE-2015-3146', 5.0, 'cause DoS via kex packets (null pointer dereference)'],
|
||||
['0.5.1', '0.6.3', 1, 'CVE-2014-8132', 5.0, 'cause DoS via kex init packet (dangling pointer)'],
|
||||
['0.4.7', '0.6.2', 1, 'CVE-2014-0017', 1.9, 'leak data via PRNG state reuse on forking servers'],
|
||||
['0.4.7', '0.5.3', 1, 'CVE-2013-0176', 4.3, 'cause DoS via kex packet (null pointer dereference)'],
|
||||
['0.4.7', '0.5.2', 1, 'CVE-2012-6063', 7.5, 'cause DoS or execute arbitrary code via sftp (double free)'],
|
||||
['0.4.7', '0.5.2', 1, 'CVE-2012-4562', 7.5, 'cause DoS or execute arbitrary code (overflow check)'],
|
||||
['0.4.7', '0.5.2', 1, 'CVE-2012-4561', 5.0, 'cause DoS via unspecified vectors (invalid pointer)'],
|
||||
['0.4.7', '0.5.2', 1, 'CVE-2012-4560', 7.5, 'cause DoS or execute arbitrary code (buffer overflow)'],
|
||||
['0.4.7', '0.5.2', 1, 'CVE-2012-4559', 6.8, 'cause DoS or execute arbitrary code (double free)']]
|
||||
}
|
||||
TXT = {
|
||||
'Dropbear SSH': [
|
||||
['0.28', '0.34', 1, 'remote root exploit', 'remote format string buffer overflow exploit (exploit-db#387).'],
|
||||
]
|
||||
['0.28', '0.34', 1, 'remote root exploit', 'remote format string buffer overflow exploit (exploit-db#387)']],
|
||||
'libssh': [
|
||||
['0.3.3', '0.3.3', 1, 'null pointer check', 'missing null pointer check in "crypt_set_algorithms_server"'],
|
||||
['0.3.3', '0.3.3', 1, 'integer overflow', 'integer overflow in "buffer_get_data"'],
|
||||
['0.3.3', '0.3.3', 3, 'heap overflow', 'heap overflow in "packet_decrypt"']]
|
||||
}
|
||||
|
||||
class Socket(ReadBuf, WriteBuf):
|
||||
@ -964,29 +984,29 @@ class KexDB(object):
|
||||
|
||||
ALGORITHMS = {
|
||||
'kex': {
|
||||
'diffie-hellman-group1-sha1': [['2.3.0,d0.28', '6.6', '6.9'], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
|
||||
'diffie-hellman-group14-sha1': [['3.9,d0.53'], [], [WARN_HASH_WEAK]],
|
||||
'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]],
|
||||
'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [], [WARN_HASH_WEAK]],
|
||||
'diffie-hellman-group14-sha256': [['7.3,d2016.73']],
|
||||
'diffie-hellman-group16-sha512': [['7.3,d2016.73']],
|
||||
'diffie-hellman-group18-sha512': [['7.3']],
|
||||
'diffie-hellman-group-exchange-sha1': [['2.3.0', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]],
|
||||
'diffie-hellman-group-exchange-sha256': [['4.4'], [], [WARN_MODULUS_CUSTOM]],
|
||||
'ecdh-sha2-nistp256': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
|
||||
'ecdh-sha2-nistp256': [['5.7,d2013.62,l10.6.0'], [WARN_CURVES_WEAK]],
|
||||
'ecdh-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
|
||||
'ecdh-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK]],
|
||||
'curve25519-sha256@libssh.org': [['6.5,d2013.62']],
|
||||
'curve25519-sha256@libssh.org': [['6.5,d2013.62,l10.6.0']],
|
||||
'kexguess2@matt.ucc.asn.au': [['d2013.57']],
|
||||
},
|
||||
'key': {
|
||||
'rsa-sha2-256': [['7.2']],
|
||||
'rsa-sha2-512': [['7.2']],
|
||||
'ssh-ed25519': [['6.5']],
|
||||
'ssh-ed25519': [['6.5,l10.7.0']],
|
||||
'ssh-ed25519-cert-v01@openssh.com': [['6.5']],
|
||||
'ssh-rsa': [['2.5.0,d0.28']],
|
||||
'ssh-dss': [['2.1.0,d0.28', '6.9'], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||
'ecdsa-sha2-nistp256': [['5.7,d2013.62'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
'ecdsa-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
'ecdsa-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
'ssh-rsa': [['2.5.0,d0.28,l10.2']],
|
||||
'ssh-dss': [['2.1.0,d0.28,l10.2', '6.9'], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||
'ecdsa-sha2-nistp256': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
'ecdsa-sha2-nistp384': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
'ecdsa-sha2-nistp521': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
'ssh-rsa-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], []],
|
||||
'ssh-dss-cert-v00@openssh.com': [['5.4', '6.9'], [FAIL_OPENSSH70_LEGACY], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]],
|
||||
'ssh-rsa-cert-v01@openssh.com': [['5.6']],
|
||||
@ -996,10 +1016,10 @@ class KexDB(object):
|
||||
'ecdsa-sha2-nistp521-cert-v01@openssh.com': [['5.7'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]],
|
||||
},
|
||||
'enc': {
|
||||
'none': [['1.2.2,d2013.56'], [FAIL_PLAINTEXT]],
|
||||
'3des-cbc': [['1.2.2,d0.28', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
|
||||
'none': [['1.2.2,d2013.56,l10.2'], [FAIL_PLAINTEXT]],
|
||||
'3des-cbc': [['1.2.2,d0.28,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_WEAK, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
|
||||
'3des-ctr': [['d0.52']],
|
||||
'blowfish-cbc': [['1.2.2,d0.28', '6.6,d0.52', '7.1,d0.52'], [FAIL_OPENSSH67_UNSAFE, FAIL_DBEAR53_DISABLED], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
|
||||
'blowfish-cbc': [['1.2.2,d0.28,l10.2', '6.6,d0.52', '7.1,d0.52'], [FAIL_OPENSSH67_UNSAFE, FAIL_DBEAR53_DISABLED], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE, WARN_BLOCK_SIZE]],
|
||||
'twofish-cbc': [['d0.28', 'd2014.66'], [FAIL_DBEAR67_DISABLED], [WARN_CIPHER_MODE]],
|
||||
'twofish128-cbc': [['d0.47', 'd2014.66'], [FAIL_DBEAR67_DISABLED], [WARN_CIPHER_MODE]],
|
||||
'twofish256-cbc': [['d0.47', 'd2014.66'], [FAIL_DBEAR67_DISABLED], [WARN_CIPHER_MODE]],
|
||||
@ -1009,27 +1029,27 @@ class KexDB(object):
|
||||
'arcfour': [['2.1.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_WEAK]],
|
||||
'arcfour128': [['4.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_WEAK]],
|
||||
'arcfour256': [['4.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_WEAK]],
|
||||
'aes128-cbc': [['2.3.0,d0.28', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
|
||||
'aes192-cbc': [['2.3.0', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
|
||||
'aes256-cbc': [['2.3.0,d0.47', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
|
||||
'aes128-cbc': [['2.3.0,d0.28,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
|
||||
'aes192-cbc': [['2.3.0,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
|
||||
'aes256-cbc': [['2.3.0,d0.47,l10.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_CIPHER_MODE]],
|
||||
'rijndael128-cbc': [['2.3.0', '3.0.2'], [FAIL_OPENSSH31_REMOVE], [WARN_CIPHER_MODE]],
|
||||
'rijndael192-cbc': [['2.3.0', '3.0.2'], [FAIL_OPENSSH31_REMOVE], [WARN_CIPHER_MODE]],
|
||||
'rijndael256-cbc': [['2.3.0', '3.0.2'], [FAIL_OPENSSH31_REMOVE], [WARN_CIPHER_MODE]],
|
||||
'rijndael-cbc@lysator.liu.se': [['2.3.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_CIPHER_MODE]],
|
||||
'aes128-ctr': [['3.7,d0.52']],
|
||||
'aes192-ctr': [['3.7']],
|
||||
'aes256-ctr': [['3.7,d0.52']],
|
||||
'aes128-ctr': [['3.7,d0.52,l10.4.1']],
|
||||
'aes192-ctr': [['3.7,l10.4.1']],
|
||||
'aes256-ctr': [['3.7,d0.52,l10.4.1']],
|
||||
'aes128-gcm@openssh.com': [['6.2']],
|
||||
'aes256-gcm@openssh.com': [['6.2']],
|
||||
'chacha20-poly1305@openssh.com': [['6.5'], [], [], [INFO_OPENSSH69_CHACHA]],
|
||||
},
|
||||
'mac': {
|
||||
'none': [['d2013.56'], [FAIL_PLAINTEXT]],
|
||||
'hmac-sha1': [['2.1.0,d0.28'], [], [WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
|
||||
'hmac-sha1': [['2.1.0,d0.28,l10.2'], [], [WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
|
||||
'hmac-sha1-96': [['2.5.0,d0.47', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
|
||||
'hmac-sha2-256': [['5.9,d2013.56'], [], [WARN_ENCRYPT_AND_MAC]],
|
||||
'hmac-sha2-256': [['5.9,d2013.56,l10.7.0'], [], [WARN_ENCRYPT_AND_MAC]],
|
||||
'hmac-sha2-256-96': [['5.9', '6.0'], [FAIL_OPENSSH61_REMOVE], [WARN_ENCRYPT_AND_MAC]],
|
||||
'hmac-sha2-512': [['5.9,d2013.56'], [], [WARN_ENCRYPT_AND_MAC]],
|
||||
'hmac-sha2-512': [['5.9,d2013.56,l10.7.0'], [], [WARN_ENCRYPT_AND_MAC]],
|
||||
'hmac-sha2-512-96': [['5.9', '6.0'], [FAIL_OPENSSH61_REMOVE], [WARN_ENCRYPT_AND_MAC]],
|
||||
'hmac-md5': [['2.1.0,d0.28', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
|
||||
'hmac-md5-96': [['2.5.0', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_ENCRYPT_AND_MAC, WARN_HASH_WEAK]],
|
||||
@ -1053,6 +1073,8 @@ class KexDB(object):
|
||||
def get_ssh_version(version_desc):
|
||||
if version_desc.startswith('d'):
|
||||
return (SSH.Product.DropbearSSH, version_desc[1:])
|
||||
elif version_desc.startswith('l1'):
|
||||
return (SSH.Product.LibSSH, version_desc[2:])
|
||||
else:
|
||||
return (SSH.Product.OpenSSH, version_desc)
|
||||
|
||||
@ -1116,6 +1138,8 @@ def get_alg_since_text(alg_desc):
|
||||
ssh_prefix, ssh_version = get_ssh_version(v)
|
||||
if not ssh_version:
|
||||
continue
|
||||
if ssh_prefix in [SSH.Product.LibSSH]:
|
||||
continue
|
||||
if ssh_version.endswith('C'):
|
||||
ssh_version = '{0} (client only)'.format(ssh_version[:-1])
|
||||
tv.append('{0} {1}'.format(ssh_prefix, ssh_version))
|
||||
@ -1142,12 +1166,15 @@ def get_alg_pairs(kex, pkm):
|
||||
|
||||
def get_alg_recommendations(software, kex, pkm, for_server=True):
|
||||
alg_pairs = get_alg_pairs(kex, pkm)
|
||||
vproducts = [SSH.Product.OpenSSH,
|
||||
SSH.Product.DropbearSSH,
|
||||
SSH.Product.LibSSH]
|
||||
if software is not None:
|
||||
if software.product not in [SSH.Product.OpenSSH, SSH.Product.DropbearSSH]:
|
||||
if software.product not in vproducts:
|
||||
software = None
|
||||
if software is None:
|
||||
ssh_timeframe = get_ssh_timeframe(alg_pairs, for_server)
|
||||
for product in [SSH.Product.OpenSSH, SSH.Product.DropbearSSH]:
|
||||
for product in vproducts:
|
||||
if product not in ssh_timeframe:
|
||||
continue
|
||||
version = ssh_timeframe[product][0]
|
||||
@ -1302,7 +1329,8 @@ def output_security_sub(sub, software, padlen):
|
||||
continue
|
||||
target, name = line[2:4]
|
||||
is_server, is_client = target & 1 == 1, target & 2 == 2
|
||||
if is_client:
|
||||
is_local = target & 4 == 4
|
||||
if not is_server:
|
||||
continue
|
||||
p = '' if out.batch else ' ' * (padlen - len(name))
|
||||
if sub == 'cve':
|
||||
|
@ -2,6 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
|
||||
class TestVersionCompare(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
@ -15,26 +16,30 @@ class TestVersionCompare(object):
|
||||
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('0.43') > 0
|
||||
assert s.compare_version('0.44') == 0
|
||||
assert s.compare_version('0.45') < 0
|
||||
assert s.between_versions('0.43', '0.45') == True
|
||||
assert s.between_versions('0.43', '0.45')
|
||||
|
||||
def test_dropbear_compare_version_with_years(self):
|
||||
s = self.get_dropbear_software('2015.71')
|
||||
assert s.compare_version('2014.67') > 0
|
||||
assert s.compare_version('2015.71') == 0
|
||||
assert s.compare_version('2016.74') < 0
|
||||
assert s.between_versions('2014.67', '2016.74') == True
|
||||
assert s.between_versions('2014.67', '2016.74')
|
||||
|
||||
def test_dropbear_compare_version_mixed(self):
|
||||
s = self.get_dropbear_software('0.53.1')
|
||||
assert s.compare_version('0.53') > 0
|
||||
assert s.compare_version('0.53.1') == 0
|
||||
assert s.compare_version('2011.54') < 0
|
||||
assert s.between_versions('0.53', '2011.54') == True
|
||||
assert s.between_versions('0.53', '2011.54')
|
||||
|
||||
def test_dropbear_compare_version_patchlevel(self):
|
||||
s1 = self.get_dropbear_software('0.44')
|
||||
@ -85,8 +90,7 @@ class TestVersionCompare(object):
|
||||
assert s.compare_version('3.7') > 0
|
||||
assert s.compare_version('3.7.1') == 0
|
||||
assert s.compare_version('3.8') < 0
|
||||
assert s.between_versions('3.7', '3.8') == True
|
||||
|
||||
assert s.between_versions('3.7', '3.8')
|
||||
|
||||
def test_openssh_compare_version_patchlevel(self):
|
||||
s1 = self.get_openssh_software('2.1.1')
|
||||
@ -130,3 +134,36 @@ class TestVersionCompare(object):
|
||||
if i + 1 < l:
|
||||
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('0.2') > 0
|
||||
assert s.compare_version('0.3') == 0
|
||||
assert s.compare_version('0.3.1') < 0
|
||||
assert s.between_versions('0.2', '0.3.1')
|
||||
|
||||
def test_libssh_compare_version_sequential(self):
|
||||
versions = []
|
||||
for v in ['0.2', '0.3']:
|
||||
versions.append(v)
|
||||
for i in range(1, 5):
|
||||
versions.append('0.3.{0}'.format(i))
|
||||
for i in range(0, 9):
|
||||
versions.append('0.4.{0}'.format(i))
|
||||
for i in range(0, 6):
|
||||
versions.append('0.5.{0}'.format(i))
|
||||
for i in range(0, 6):
|
||||
versions.append('0.6.{0}'.format(i))
|
||||
for i in range(0, 4):
|
||||
versions.append('0.7.{0}'.format(i))
|
||||
l = len(versions)
|
||||
for i in range(l):
|
||||
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:
|
||||
vnext = versions[i + 1]
|
||||
assert s.compare_version(vnext) < 0
|
||||
|
Loading…
Reference in New Issue
Block a user