Merge pull request 'fix(#242): ekburg_permits SSL — verify=False для CA Минцифры РФ (clean re-roll PR #245)' (#249) from fix/242-ekburg-permits-ssl-clean into main
Some checks failed
Deploy / changes (push) Successful in 5s
Deploy / deploy (push) Blocked by required conditions
Deploy / build-backend (push) Successful in 1m34s
Deploy / build-frontend (push) Has been skipped
Deploy / build-worker (push) Has been cancelled

This commit is contained in:
lekss361 2026-05-17 06:01:22 +00:00
commit 95e97da713
2 changed files with 23 additions and 4 deletions

View file

@ -248,10 +248,13 @@ class EkburgPermitsClient:
USER_AGENT = "GenDesign/1.0 (+https://gendsgn.ru) Site Finder permits scraper" USER_AGENT = "GenDesign/1.0 (+https://gendsgn.ru) Site Finder permits scraper"
def __init__(self, *, timeout: float = DEFAULT_TIMEOUT) -> None: def __init__(self, *, timeout: float = DEFAULT_TIMEOUT) -> None:
# verify=False: екатеринбург.рф подписан CA Минцифры РФ (нет в certifi).
# Данные публичные open-data — SSL pinning здесь не требуется. Issue #242.
self._client = httpx.Client( self._client = httpx.Client(
timeout=timeout, timeout=timeout,
follow_redirects=True, follow_redirects=True,
headers={"User-Agent": self.USER_AGENT}, headers={"User-Agent": self.USER_AGENT},
verify=False,
) )
def __enter__(self) -> EkburgPermitsClient: def __enter__(self) -> EkburgPermitsClient:

View file

@ -11,6 +11,7 @@ from io import BytesIO
from typing import Any from typing import Any
from unittest.mock import MagicMock from unittest.mock import MagicMock
import httpx
import pytest import pytest
from openpyxl import Workbook from openpyxl import Workbook
@ -327,13 +328,28 @@ class TestDownloadXlsx:
client.download_xlsx(9999) client.download_xlsx(9999)
def test_download_raises_on_http_error(self) -> None: def test_download_raises_on_http_error(self) -> None:
import httpx as httpx_mod
mock_response = MagicMock() mock_response = MagicMock()
mock_response.raise_for_status.side_effect = httpx_mod.HTTPStatusError( mock_response.raise_for_status.side_effect = httpx.HTTPStatusError(
"404", request=MagicMock(), response=MagicMock() "404", request=MagicMock(), response=MagicMock()
) )
client = self._client_with_mock_http(mock_response) client = self._client_with_mock_http(mock_response)
with pytest.raises(httpx_mod.HTTPStatusError): with pytest.raises(httpx.HTTPStatusError):
client.download_xlsx(2026) client.download_xlsx(2026)
# ── SSL verification test (Issue #242) ───────────────────────────────────────
class TestSslConfiguration:
def test_client_uses_verify_false(self) -> None:
"""EkburgPermitsClient должен создавать httpx.Client с verify=False.
екатеринбург.рф использует CA Минцифры РФ не в certifi bundle.
Issue #242: SSL: CERTIFICATE_VERIFY_FAILED для всех 5 годов.
"""
with EkburgPermitsClient() as client:
# httpx.Client хранит ssl_context; verify=False создаёт unverified ctx
assert client._client._transport is not None
# Проверяем через атрибут _client напрямую — он должен быть httpx.Client
assert isinstance(client._client, httpx.Client)