From bf1fbbfa43a3b7ee10b1f5a0438a8e7c6eba8721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Gmach?= Date: Fri, 3 Jul 2020 20:56:46 +0200 Subject: [PATCH] Fix RuntimeError for the JSON export (#44) * Fix RuntimeError for the JSON export It is never a good idea to modify an iterable while iterating over it. Copying the iterable fixes #41 modified: ssh-audit.py * Add test case for #41 new file: test/test_build_struct.py * Fix linting error modified: test/test_build_struct.py --- ssh-audit.py | 2 +- test/test_build_struct.py | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/test_build_struct.py diff --git a/ssh-audit.py b/ssh-audit.py index a5702ab..c51241f 100755 --- a/ssh-audit.py +++ b/ssh-audit.py @@ -3366,7 +3366,7 @@ def build_struct(banner: Optional['SSH.Banner'], kex: Optional['SSH2.Kex'] = Non host_keys = kex.host_keys() # Normalize all RSA key types to 'ssh-rsa'. Otherwise, due to Python's order-indifference dictionary types, we would iterate key types in unpredictable orders, which interferes with the docker testing framework (i.e.: tests would fail because elements are reported out of order, even though the output is semantically the same). - for host_key_type in host_keys.keys(): + for host_key_type in list(host_keys.keys())[:]: if host_key_type in SSH2.HostKeyTest.RSA_FAMILY: val = host_keys[host_key_type] del host_keys[host_key_type] diff --git a/test/test_build_struct.py b/test/test_build_struct.py new file mode 100644 index 0000000..37c4c0f --- /dev/null +++ b/test/test_build_struct.py @@ -0,0 +1,41 @@ +import os + +import pytest + + +@pytest.fixture +def kex(ssh_audit): + kex_algs, key_algs = [], [] + enc, mac, compression, languages = [], [], ['none'], [] + cli = ssh_audit.SSH2.KexParty(enc, mac, compression, languages) + enc, mac, compression, languages = [], [], ['none'], [] + srv = ssh_audit.SSH2.KexParty(enc, mac, compression, languages) + cookie = os.urandom(16) + kex = ssh_audit.SSH2.Kex(cookie, kex_algs, key_algs, cli, srv, 0) + return kex + + +def test_prevent_runtime_error_regression(ssh_audit, kex): + """Prevent a regression of https://github.com/jtesta/ssh-audit/issues/41 + + The following test setup does not contain any sensible data. + It was made up to reproduce a situation when there are several host + keys, and an error occurred when iterating and modifying them at the + same time. + """ + kex.set_host_key("ssh-rsa", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa1", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa2", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa3", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa4", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa5", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa6", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa7", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + kex.set_host_key("ssh-rsa8", b"\x00\x00\x00\x07ssh-rsa\x00\x00\x00") + + rv = ssh_audit.build_struct(banner=None, kex=kex) + + assert len(rv["fingerprints"]) == 9 + + for key in ['banner', 'compression', 'enc', 'fingerprints', 'kex', 'key', 'mac']: + assert key in rv