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
This commit is contained in:
Кокос Артем Николаевич
2026-07-17 12:50:40 +07:00
parent a1febd6999
commit b624a8b8c2
3 changed files with 49 additions and 0 deletions

View File

@@ -318,3 +318,4 @@ mypy redmine_reporter
- Используйте аккаунт с минимальными правами, достаточными для чтения time entries и задач. - Используйте аккаунт с минимальными правами, достаточными для чтения time entries и задач.
- Инструмент работает только в режиме чтения и не изменяет данные в Redmine. - Инструмент работает только в режиме чтения и не изменяет данные в Redmine.
- `REDMINE_URL` обязан использовать HTTPS: API-ключ передаётся в заголовках запроса, и без TLS он может быть перехвачен. - `REDMINE_URL` обязан использовать HTTPS: API-ключ передаётся в заголовках запроса, и без TLS он может быть перехвачен.
- `REDMINE_VERIFY=false` (или `verify_ssl: false`) отключает проверку TLS-сертификата — соединение уязвимо для MITM-атак; при старте выводится предупреждение. Используйте только в доверенной сети.

View File

@@ -1,5 +1,6 @@
import logging import logging
import os import os
import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import date, timedelta from datetime import date, timedelta
from pathlib import Path from pathlib import Path
@@ -406,6 +407,13 @@ class Config:
"REDMINE_URL must use HTTPS: the API key is sent in request " "REDMINE_URL must use HTTPS: the API key is sent in request "
"headers and requires TLS" "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(): if cls.get_redmine_api_key():
return return
if not (cls.get_redmine_user() and cls.get_redmine_password()): if not (cls.get_redmine_user() and cls.get_redmine_password()):

View File

@@ -176,6 +176,46 @@ class TestConfigRequiresHttpsUrl:
Config.validate() 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 не должен переопределять переменные окружения -- # -- #15: .env не должен переопределять переменные окружения --