From 943613a194ee76be9a73eedcb82c0036bb37b3f0 Mon Sep 17 00:00:00 2001 From: Nic West Date: Tue, 21 Apr 2015 17:05:52 +0100 Subject: [PATCH] add thing for when known hosts have changed --- tests/rules/test_ssh_known_host.py | 74 ++++++++++++++++++++++++++++++ thefuck/rules/ssh_known_hosts.py | 38 +++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/rules/test_ssh_known_host.py create mode 100644 thefuck/rules/ssh_known_hosts.py diff --git a/tests/rules/test_ssh_known_host.py b/tests/rules/test_ssh_known_host.py new file mode 100644 index 00000000..74a81469 --- /dev/null +++ b/tests/rules/test_ssh_known_host.py @@ -0,0 +1,74 @@ +import os +import pytest +from thefuck.main import Command +from thefuck.rules.ssh_known_hosts import match, get_new_command, remove_offending_keys + + +@pytest.fixture +def ssh_error(tmpdir): + path = os.path.join(str(tmpdir), 'known_hosts') + + def reset(path): + with open(path, 'w') as fh: + lines = [ + '123.234.567.890 asdjkasjdakjsd\n' + '98.765.432.321 ejioweojwejrosj\n' + '111.222.333.444 qwepoiwqepoiss\n' + ] + fh.writelines(lines) + + def known_hosts(path): + with open(path, 'r') as fh: + return fh.readlines() + + reset(path) + + errormsg = u"""@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! +Someone could be eavesdropping on you right now (man-in-the-middle attack)! +It is also possible that a host key has just been changed. +The fingerprint for the RSA key sent by the remote host is +b6:cb:07:34:c0:a0:94:d3:0d:69:83:31:f4:c5:20:9b. +Please contact your system administrator. +Add correct host key in {0} to get rid of this message. +Offending RSA key in {0}:2 +RSA host key for {1} has changed and you have requested strict checking. +Host key verification failed.""".format(path, '98.765.432.321') + + return errormsg, path, reset, known_hosts + + +def test_match(ssh_error): + errormsg, _, _, _ = ssh_error + assert match(Command('ssh', '', errormsg), None) + assert match(Command('ssh', '', errormsg), None) + assert match(Command('scp something something', '', errormsg), None) + assert match(Command('scp something something', '', errormsg), None) + assert not match(Command('', '', errormsg), None) + assert not match(Command('notssh', '', errormsg), None) + assert not match(Command('ssh', '', ''), None) + + +def test_remove_offending_keys(ssh_error): + errormsg, path, reset, known_hosts = ssh_error + command = Command('ssh user@host', '', errormsg) + remove_offending_keys(command, None) + expected =['123.234.567.890 asdjkasjdakjsd\n', '111.222.333.444 qwepoiwqepoiss\n'] + assert known_hosts(path) == expected + + +def test_get_new_command(ssh_error, monkeypatch): + errormsg, _, _, _ = ssh_error + + class Mock: + was_called = False + + def __call__(self, *args, **kwargs): + self.was_called = True + + method = Mock() + monkeypatch.setattr('thefuck.rules.ssh_known_hosts.remove_offending_keys', method) + assert get_new_command(Command('ssh user@host', '', errormsg), None) == 'ssh user@host' + assert method.was_called diff --git a/thefuck/rules/ssh_known_hosts.py b/thefuck/rules/ssh_known_hosts.py new file mode 100644 index 00000000..2f425bfe --- /dev/null +++ b/thefuck/rules/ssh_known_hosts.py @@ -0,0 +1,38 @@ +import re +patterns = [ + r'WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!', + r'WARNING: POSSIBLE DNS SPOOFING DETECTED!', + r"Warning: the \S+ host key for '([^']+)' differs from the key for the IP address '([^']+)'", +] +offending_pattern = re.compile( + r'(?:Offending (?:key for IP|\S+ key)|Matching host key) in ([^:]+):(\d+)', + re.MULTILINE) + +commands = ['ssh', 'scp'] + + +def match(command, settings): + if not command.script: + return False + if not command.script.split()[0] in commands: + return False + if not any([re.findall(pattern, command.stderr) for pattern in patterns]): + return False + return True + + +def remove_offending_keys(command, settings): + offending = offending_pattern.findall(command.stderr) + print offending + for filepath, lineno in offending: + with open(filepath, 'r') as fh: + lines = fh.readlines() + del lines[int(lineno) - 1] + print lines + with open(filepath, 'w') as fh: + fh.writelines(lines) + + +def get_new_command(command, settings): + remove_offending_keys(command, settings) + return command.script