diff --git a/gateway/pairing.py b/gateway/pairing.py index 989153b351..bac6f74e78 100644 --- a/gateway/pairing.py +++ b/gateway/pairing.py @@ -464,7 +464,9 @@ class PairingStore: """ with self._lock: self._cleanup_expired(platform) - code = code.upper().strip() + # Chat UIs insert visual spacing between code characters; strip all + # whitespace, then match exactly (surrounding words still fail). #89937 + code = "".join(str(code or "").upper().split()) # Before the lookup, or an already-issued valid code would bypass lockout. if self._is_locked_out(platform): return None diff --git a/tests/gateway/test_pairing.py b/tests/gateway/test_pairing.py index a8ef808b5d..f97bfcb3cf 100644 --- a/tests/gateway/test_pairing.py +++ b/tests/gateway/test_pairing.py @@ -275,6 +275,25 @@ class TestApprovalFlow: assert result["user_id"] == "user1" assert result["user_name"] == "Alice" + def test_approve_code_with_internal_spacing(self, tmp_path): + with patch("gateway.pairing.PAIRING_DIR", tmp_path): + store = PairingStore() + code = store.generate_code("telegram", "user1", "Alice") + spaced_code = " ".join(code) + result = store.approve_code("telegram", f" {spaced_code} ") + + assert isinstance(result, dict) + assert result["user_id"] == "user1" + assert result["user_name"] == "Alice" + + def test_approve_code_with_words_still_fails(self, tmp_path): + with patch("gateway.pairing.PAIRING_DIR", tmp_path): + store = PairingStore() + code = store.generate_code("telegram", "user1", "Alice") + result = store.approve_code("telegram", f"code {code}") + + assert result is None + def test_approved_user_is_approved(self, tmp_path): with patch("gateway.pairing.PAIRING_DIR", tmp_path): store = PairingStore()