Fix ZTE hop false auth rejects by treating CLI prompt as success.

ZTE post-login banners can line-wrap login-failure stats and were misclassified before the target prompt was recognized.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-31 10:46:16 +08:00
parent fbf73cbaaf
commit f8d4a396f1
4 changed files with 114 additions and 22 deletions

View file

@ -156,5 +156,34 @@ class HuaweiStelnetAuthTests(unittest.TestCase):
self.assertTrue(any("password-change" in p for p in seen))
class ZteHopAuthTests(unittest.TestCase):
@patch("netx_api.ne_session_connect._read_channel")
@patch("netx_api.ne_session_connect._send_line")
def test_zte_banner_with_prompt_not_auth_failure(
self,
mock_send: MagicMock,
mock_read: MagicMock,
) -> None:
"""ZTE login stats contain 'authentication failure' — prompt is ground truth."""
mock_read.side_effect = [
"Username:",
"Password:",
(
"Welcome to ZXR10 ZXCTN 6120H\n"
"Login at 09:43:53 08-31-2026 from 10.229.147.122 through SSH.\n"
"The last successful login was performed at 09:43:44 08-31-2026 "
"from 10.229.147.122 through SSH. Afterwa\n"
"rwards, 0 authentication failure occurred.\n"
"AL5458-ACC-6120HS#"
),
]
conn = MagicMock()
_interactive_target_auth(conn, "ipran", "secret")
self.assertEqual(
[c.args[1] for c in mock_send.call_args_list],
["ipran", "secret"],
)
if __name__ == "__main__":
unittest.main()

View file

@ -67,6 +67,37 @@ class CliAuthClassifyTests(unittest.TestCase):
)
self.assertIn("Username or password is wrong", find_auth_failure_snippet(text) or "")
def test_zte_post_login_banner_not_auth_failure(self):
"""ZTE nested ssh hop: '0 authentication failure occurred' is login stats."""
text = (
"Welcome to ZXR10 ZXCTN 6120H Carrier-Class Router of ZTE Corporation\n"
"Login at 09:43:53 08-31-2026 from 10.229.147.122 through SSH.\n"
"The last successful login was performed at 09:43:44 08-31-2026 "
"from 10.229.147.122 through SSH. Afterwards, 0 authentication "
"failure occurred.\n"
"AL5458-ACC-6120HS#"
)
self.assertIsNone(find_auth_failure_snippet(text))
def test_zte_wrapped_afterwards_banner_not_auth_failure(self):
"""Narrow PTY wraps 'Afterwards' — must not classify as auth reject."""
text = (
"The last successful login was performed at 09:43:44 08-31-2026 "
"from 10.229.147.122 through SSH. After\n"
"wards, 0 authentication failure occurred.\n"
"AL5458-ACC-6120HS#"
)
self.assertIsNone(find_auth_failure_snippet(text))
def test_zte_severely_wrapped_afterwards_banner_not_auth_failure(self):
"""Production saw 'rwards' after mid-word wrap — still login stats, not reject."""
text = (
"from 10.229.147.122 through SSH. Afterwa\n"
"rwards, 0 authentication failure occurred.\n"
"AL5458-ACC-6120HS#"
)
self.assertIsNone(find_auth_failure_snippet(text))
if __name__ == "__main__":
unittest.main()