mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 03:38:23 +00:00
The unit test of the alarm script built its Jinja environment with autoescape switched off, which code scanning reports as a possible XSS sink and fails the PR on. The template is a shell script, so select_autoescape() leaves it unescaped exactly as before, and the finding no longer applies. Refs #790 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
124 lines
3.9 KiB
Python
124 lines
3.9 KiB
Python
import os
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape
|
|
|
|
from utils import PROJECT_ROOT
|
|
|
|
TEMPLATE_DIR = PROJECT_ROOT / "roles/sys-ctl-alm-email/templates"
|
|
|
|
EMPTY_REPLY = "sendmail: the server sent an empty reply"
|
|
|
|
SENDMAIL_STUB = """#!/usr/bin/env bash
|
|
cat >/dev/null
|
|
calls=$(( $(cat "${STUB_DIR}/calls" 2>/dev/null || echo 0) + 1 ))
|
|
echo "${calls}" >"${STUB_DIR}/calls"
|
|
if [ "${calls}" -gt "${STUB_FAILURES}" ]; then
|
|
\texit 0
|
|
fi
|
|
echo "${STUB_MESSAGE}" >&2
|
|
exit "${STUB_RC}"
|
|
"""
|
|
|
|
QUIET_STUB = """#!/usr/bin/env bash
|
|
exit 0
|
|
"""
|
|
|
|
ECHO_STUB = """#!/usr/bin/env bash
|
|
printf '%s\\n' "${@: -1}"
|
|
"""
|
|
|
|
|
|
def lookup(name, *_args):
|
|
"""Return what the template's two lookups resolve to.
|
|
|
|
Args:
|
|
name: lookup plugin the template calls.
|
|
"""
|
|
return {
|
|
"email": {"from": "no-reply@example.test", "timeout": "30"},
|
|
"users": {"email": "administrator@example.test"},
|
|
}[name]
|
|
|
|
|
|
def rendered_script() -> str:
|
|
env = Environment(
|
|
loader=FileSystemLoader(str(TEMPLATE_DIR)),
|
|
undefined=StrictUndefined,
|
|
autoescape=select_autoescape(),
|
|
)
|
|
return env.get_template("script.sh.j2").render(lookup=lookup, HOST_CS="utf-8")
|
|
|
|
|
|
class TestAlmEmailScript(unittest.TestCase):
|
|
def run_script(self, *, failures, rc, message):
|
|
"""Run the rendered alarm script against a stubbed sendmail.
|
|
|
|
Args:
|
|
failures: how many sendmail calls fail before one succeeds.
|
|
rc: exit code of a failing call.
|
|
message: what a failing call prints to stderr.
|
|
|
|
Returns:
|
|
Tuple of the exit code, the retry lines and the whole stderr.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
bin_dir = Path(tmp)
|
|
stubs = (
|
|
("sendmail", SENDMAIL_STUB),
|
|
("sleep", QUIET_STUB),
|
|
("systemctl", QUIET_STUB),
|
|
("systemd-escape", ECHO_STUB),
|
|
)
|
|
for name, body in stubs:
|
|
stub = bin_dir / name
|
|
stub.write_text(body)
|
|
stub.chmod(0o755)
|
|
script = bin_dir / "script.sh"
|
|
script.write_text(rendered_script())
|
|
env = dict(os.environ)
|
|
env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}"
|
|
env["STUB_DIR"] = tmp
|
|
env["STUB_FAILURES"] = str(failures)
|
|
env["STUB_RC"] = str(rc)
|
|
env["STUB_MESSAGE"] = message
|
|
done = subprocess.run(
|
|
["bash", str(script), "example.service"],
|
|
env=env,
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
retries = [
|
|
line
|
|
for line in done.stderr.splitlines()
|
|
if "sendmail transient failure" in line
|
|
]
|
|
return done.returncode, retries, done.stderr
|
|
|
|
def test_a_dropped_connection_is_retried_and_the_alarm_is_sent(self):
|
|
code, retries, _ = self.run_script(failures=1, rc=76, message=EMPTY_REPLY)
|
|
self.assertEqual(code, 0)
|
|
self.assertEqual(len(retries), 1)
|
|
self.assertIn("(rc=76), attempt 1/3", retries[0])
|
|
|
|
def test_a_permanent_rejection_is_reported_without_a_retry(self):
|
|
rejection = "sendmail: recipient address rejected"
|
|
code, retries, stderr = self.run_script(failures=99, rc=65, message=rejection)
|
|
self.assertEqual(code, 65)
|
|
self.assertEqual(retries, [])
|
|
self.assertIn(rejection, stderr)
|
|
|
|
def test_the_third_failure_ends_the_alarm_with_the_last_exit_code(self):
|
|
code, retries, stderr = self.run_script(failures=99, rc=76, message=EMPTY_REPLY)
|
|
self.assertEqual(code, 76)
|
|
self.assertEqual(len(retries), 2)
|
|
self.assertIn(EMPTY_REPLY, stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|