diff --git a/README.md b/README.md index 04a381d..95db9eb 100644 --- a/README.md +++ b/README.md @@ -318,3 +318,4 @@ mypy redmine_reporter - Используйте аккаунт с минимальными правами, достаточными для чтения time entries и задач. - Инструмент работает только в режиме чтения и не изменяет данные в Redmine. - `REDMINE_URL` обязан использовать HTTPS: API-ключ передаётся в заголовках запроса, и без TLS он может быть перехвачен. +- `REDMINE_VERIFY=false` (или `verify_ssl: false`) отключает проверку TLS-сертификата — соединение уязвимо для MITM-атак; при старте выводится предупреждение. Используйте только в доверенной сети. diff --git a/redmine_reporter/config.py b/redmine_reporter/config.py index 53198b8..b41e9d6 100644 --- a/redmine_reporter/config.py +++ b/redmine_reporter/config.py @@ -1,5 +1,6 @@ import logging import os +import sys from dataclasses import dataclass, field from datetime import date, timedelta from pathlib import Path @@ -406,6 +407,13 @@ class Config: "REDMINE_URL must use HTTPS: the API key is sent in request " "headers and requires TLS" ) + if cls.get_redmine_verify() is False: + print( + "⚠️ TLS certificate verification is disabled " + "(REDMINE_VERIFY=false / verify_ssl: false): connection is " + "vulnerable to MITM attacks", + file=sys.stderr, + ) if cls.get_redmine_api_key(): return if not (cls.get_redmine_user() and cls.get_redmine_password()): diff --git a/tests/test_config.py b/tests/test_config.py index a1a58d6..079b6aa 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -176,6 +176,46 @@ class TestConfigRequiresHttpsUrl: Config.validate() +# -- #57: предупреждение при отключённой проверке TLS -- + + +class TestWarnWhenTlsVerificationDisabled: + """verify_ssl: false должен давать видимый warning в stderr (риск MITM).""" + + _ENV = {"REDMINE_URL": "https://red.eltex.loc/", "REDMINE_API_KEY": "token"} + + def _validate_env(self, verify: str): + return {**self._ENV, "REDMINE_VERIFY": verify} + + def test_verify_false_warns_in_stderr(self, capsys): + with mock.patch.dict(os.environ, self._validate_env("false"), clear=True): + Config.validate() + + err = capsys.readouterr().err + assert "⚠️" in err + assert "TLS" in err or "SSL" in err + + def test_verify_true_no_warning(self, capsys): + with mock.patch.dict(os.environ, self._validate_env("true"), clear=True): + Config.validate() + + assert capsys.readouterr().err == "" + + def test_verify_ca_path_no_warning(self, capsys): + with mock.patch.dict( + os.environ, self._validate_env("/tmp/redmine-ca.pem"), clear=True + ): + Config.validate() + + assert capsys.readouterr().err == "" + + def test_verify_default_no_warning(self, capsys): + with mock.patch.dict(os.environ, self._ENV, clear=True): + Config.validate() + + assert capsys.readouterr().err == "" + + # -- #15: .env не должен переопределять переменные окружения --