From b624a8b8c2d3ce31608572e1374e3f5a24af8126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D0=BA=D0=BE=D1=81=20=D0=90=D1=80=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8?= =?UTF-8?q?=D1=87?= Date: Fri, 17 Jul 2026 12:50:40 +0700 Subject: [PATCH] fix: warn when TLS verification is disabled REDMINE_VERIFY=false / verify_ssl: false silently disabled TLS certificate verification, exposing the connection to MITM attacks. Config.validate() now prints a warning to stderr when verification is disabled (exactly False; True or a CA-bundle path stays quiet). validate() runs exactly once at CLI startup, so the warning is emitted once per run and is visible in every scenario. Closes #57 --- README.md | 1 + redmine_reporter/config.py | 8 ++++++++ tests/test_config.py | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) 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 не должен переопределять переменные окружения --