Files
computer-playbook/tests/unit/python/roles/web-app-gitlab/test_smtp_settings.py
T
kevinveenbirkenbachandClaude Opus 5.5 3610e7b9ef fix(code-scanning): resolve the alerts that describe real code
Of the 76 open code scanning alerts, 39 point at code that can change.

svc-ai-mcp-adapter:
- A path argument that is empty or made only of dots is refused.
  Percent-encoding keeps a separator inside its segment, but "." and ".."
  need none, so an upstream that resolves dot segments could answer from a
  path the contract does not name.
- parse_duration splits on the units. The pattern it used before rescanned
  a long run of digits from every start position.

Tests and the README generators:
- Every Jinja environment built with autoescape=False and a "noqa: S701"
  uses select_autoescape now, as six test modules and
  utils/templating/jinja.py already did. It escapes templates named
  *.html, *.htm and *.xml, which none of these call sites loads.
- Dead code goes: a doubled "rel" block in the email lint, an unread
  _SKIP_DIRS, and a module that _resolve returned to no caller.
- Two "except ...: pass" blocks become contextlib.suppress and
  assertRaises.
- Nine test modules import unittest.mock one way instead of two, and one
  lookup test imports its module once.

Closes #807

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:18:00 +02:00

112 lines
3.9 KiB
Python

"""Whether GitLab announces SMTP AUTH, and on which listener.
Ruby's ``net/smtp`` authenticates whenever a ``user_name`` or ``password`` is
present, falling back to its default auth type — ``authentication: nil`` does
NOT switch it off. So on the provider's SSO relay, where port 25 offers no
AUTH at all, leaving the credentials in the hash makes every send die with
``503 5.5.1 AUTH not allowed`` while GitLab still reports the mail as queued.
The credentials therefore have to be omitted, not merely unaccompanied by an
auth type.
``tls`` and ``enable_starttls_auto`` are the other half: they are mutually
exclusive in ActionMailer, implicit TLS on 465 or STARTTLS on the relay.
"""
from __future__ import annotations
import re
import unittest
from jinja2 import Environment, StrictUndefined, select_autoescape
from utils.cache.files import read_text
from . import PROJECT_ROOT
TEMPLATE = (
PROJECT_ROOT
/ "roles"
/ "web-app-gitlab"
/ "templates"
/ "config"
/ "smtp_settings.rb.j2"
)
RELAY = {
"host": "mail.infinito.test",
"port": 25,
"username": "no-reply@infinito.test",
"password": "s3cr3t",
"domain": "infinito.test",
"auth": False,
"start_tls": True,
"tls": False,
}
SUBMISSION = dict(RELAY, port=465, auth=True, start_tls=False, tls=True)
def _render(email: dict, ca_file: str = "") -> str:
env = Environment(
trim_blocks=True,
lstrip_blocks=True,
undefined=StrictUndefined,
autoescape=select_autoescape(default_for_string=False),
)
env.filters["bool"] = bool
env.filters["ruby_dq"] = lambda value: '"' + str(value).replace('"', '\\"') + '"'
return env.from_string(read_text(TEMPLATE)).render(
GITLAB_EMAIL=email,
GITLAB_EMAIL_CA_FILE=ca_file,
)
class TestGitlabSmtpSettings(unittest.TestCase):
def test_verification_is_never_weakened(self):
"""An onion relay is handled by turning start_tls off in the email
lookup, not by telling Net::SMTP to skip peer verification."""
self.assertNotIn("openssl_verify_mode", _render(RELAY))
def test_our_own_ca_is_trusted_where_it_is_mounted(self):
rendered = _render(RELAY, ca_file="/tmp/infinito/ca/root-ca.crt")
self.assertIn('ca_file: "/tmp/infinito/ca/root-ca.crt"', rendered)
def test_without_our_ca_no_trust_store_override_is_emitted(self):
self.assertNotIn("ca_file", _render(RELAY))
def test_the_relay_carries_no_credentials_at_all(self):
rendered = _render(RELAY)
self.assertNotIn("user_name", rendered)
self.assertNotIn("password", rendered)
self.assertNotIn("s3cr3t", rendered)
def test_the_relay_announces_no_auth_type(self):
self.assertNotIn("authentication", _render(RELAY))
def test_authenticated_submission_still_sends_credentials(self):
rendered = _render(SUBMISSION)
self.assertIn('user_name: "no-reply@infinito.test"', rendered)
self.assertIn("authentication: :login", rendered)
def test_tls_and_starttls_are_never_both_on(self):
for email in (RELAY, SUBMISSION):
rendered = _render(email)
tls = re.search(r"^\s*tls:\s*(\w+)", rendered, re.MULTILINE).group(1)
starttls = re.search(
r"^\s*enable_starttls_auto:\s*(\w+)", rendered, re.MULTILINE
).group(1)
self.assertNotEqual((tls, starttls), ("true", "true"))
def test_the_relay_uses_starttls_and_the_submission_port_implicit_tls(self):
self.assertIn("enable_starttls_auto: true", _render(RELAY))
self.assertIn("tls: false", _render(RELAY))
self.assertIn("tls: true", _render(SUBMISSION))
def test_the_hash_stays_syntactically_valid_without_credentials(self):
rendered = _render(RELAY).strip()
self.assertTrue(rendered.endswith("}"))
self.assertNotIn(",\n}", rendered)
if __name__ == "__main__":
unittest.main()