- Add smart two-phase TLS detection with caching in tests/utils/tls.py - Add auto-discovery for Chromium-GOST and Yandex.Browser - Add --tls-mode and --ignore-https-errors CLI options - Update browser_page context with ignore_https_errors and unified viewport - Add unit tests in tests/utils/test_tls.py - Update README.md and .env.example
83 lines
3.5 KiB
Python
83 lines
3.5 KiB
Python
"""
|
||
Тесты модуля tests/utils/tls.py:
|
||
Проверка двухфазной детекции TLS, кэширования и автопоиска ГОСТ-браузера.
|
||
"""
|
||
import ssl
|
||
from unittest.mock import patch, MagicMock
|
||
import pytest
|
||
from tests.utils.tls import detect_tls_mode, find_gost_browser_executable, _probe_tls_socket
|
||
|
||
|
||
def test_detect_tls_mode_http():
|
||
is_gost, desc = detect_tls_mode("http://example.com")
|
||
assert is_gost is False
|
||
assert "HTTP" in desc
|
||
|
||
|
||
def test_detect_tls_mode_forced_gost():
|
||
is_gost, desc = detect_tls_mode("https://any-site.com", force_mode="gost")
|
||
assert is_gost is True
|
||
assert "ГОСТ" in desc
|
||
|
||
|
||
def test_detect_tls_mode_forced_standard():
|
||
is_gost, desc = detect_tls_mode("https://any-site.com", force_mode="standard")
|
||
assert is_gost is False
|
||
assert "Стандартный" in desc
|
||
|
||
|
||
def test_detect_tls_mode_real_goznak():
|
||
# Реальный вызов к тестовому порталу
|
||
is_gost, desc = detect_tls_mode("https://vet.goznak.ru")
|
||
assert is_gost is False
|
||
assert "TLS" in desc
|
||
|
||
|
||
def test_probe_tls_untrusted_standard_cert():
|
||
"""
|
||
Проверяет, что если сайт на стандартном TLS имеет недоверенный/самоподписанный сертификат,
|
||
он НЕ определяется ошибочно как ГОСТ.
|
||
"""
|
||
with patch("ssl.create_default_context") as mock_def_ctx, \
|
||
patch("ssl._create_unverified_context") as mock_unv_ctx, \
|
||
patch("socket.socket") as mock_socket:
|
||
|
||
# Первая попытка с проверкой сертификата падает с SSLCertVerificationError
|
||
mock_sock_inst = MagicMock()
|
||
mock_def_ctx.return_value.wrap_socket.side_effect = ssl.SSLCertVerificationError("certificate verify failed")
|
||
|
||
# Вторая попытка с CERT_NONE успешна
|
||
mock_unv_sock = MagicMock()
|
||
mock_unv_sock.version.return_value = "TLSv1.3"
|
||
mock_unv_sock.cipher.return_value = ("TLS_AES_256_GCM_SHA384", "TLSv1.3", 256)
|
||
mock_unv_ctx.return_value.wrap_socket.return_value.__enter__.return_value = mock_unv_sock
|
||
|
||
is_gost, desc = _probe_tls_socket("self-signed.example.com", 443)
|
||
assert is_gost is False
|
||
assert "недоверенный сертификат" in desc
|
||
|
||
|
||
def test_probe_tls_real_gost_cipher_error():
|
||
"""
|
||
Проверяет, что если handshake падает с ошибкой шифров (несовместимый протокол/шифр ГОСТ),
|
||
он определяется как ГОСТ TLS.
|
||
"""
|
||
with patch("ssl.create_default_context") as mock_def_ctx, \
|
||
patch("ssl._create_unverified_context") as mock_unv_ctx, \
|
||
patch("socket.socket") as mock_socket:
|
||
|
||
mock_def_ctx.return_value.wrap_socket.side_effect = ssl.SSLError(ssl.SSL_ERROR_SSL, "UNSUPPORTED_PROTOCOL")
|
||
mock_unv_ctx.return_value.wrap_socket.side_effect = ssl.SSLError(ssl.SSL_ERROR_SSL, "NO_CIPHERS_AVAILABLE")
|
||
|
||
is_gost, desc = _probe_tls_socket("gost.example.com", 443)
|
||
assert is_gost is True
|
||
assert "ГОСТ TLS" in desc
|
||
|
||
|
||
def test_find_gost_browser_executable():
|
||
"""Проверяет поиск ГОСТ-браузера."""
|
||
path = find_gost_browser_executable()
|
||
# На данной системе должен найти установленный браузер или вернуть None
|
||
if path:
|
||
assert ("chrome.exe" in path.lower()) or ("browser.exe" in path.lower())
|