jinjaturtle/tests/test_release_blockers.py
Miguel Jacq f63cb92b35
Some checks failed
CI / test (push) Successful in 48s
CI / test (debian, docker.io/library/debian:13, python3) (push) Successful in 1m21s
Lint / test (push) Failing after 38s
Byebye
2026-06-24 20:29:20 +10:00

98 lines
3 KiB
Python

from pathlib import Path
import pytest
import yaml
from defusedxml.common import DTDForbidden
from jinjaturtle.core import (
VariableNameCollisionError,
generate_ansible_yaml,
parse_config,
)
from jinjaturtle.handlers.xml import XmlHandler
from jinjaturtle.multi import iter_supported_files, process_directory
def test_defaults_string_values_are_ansible_unsafe():
output = generate_ansible_yaml(
"role", [(("section", "key"), "{{ lookup('pipe', 'id') }}")]
)
assert "!unsafe" in output
assert yaml.safe_load(output)["role_section_key"] == "{{ lookup('pipe', 'id') }}"
def test_variable_name_collisions_fail_closed():
with pytest.raises(VariableNameCollisionError):
generate_ansible_yaml(
"role",
[
(("section", "a-b"), "safe"),
(("section", "a_b"), "evil"),
],
)
def test_folder_mode_does_not_follow_file_symlinks(tmp_path: Path):
outside = tmp_path / "outside.ini"
outside.write_text("[s]\nsecret = value\n", encoding="utf-8")
root = tmp_path / "root"
root.mkdir()
(root / "link.ini").symlink_to(outside)
(root / "real.ini").write_text("[s]\nname = ok\n", encoding="utf-8")
files = iter_supported_files(root, recursive=True)
assert files == [root / "real.ini"]
defaults, _outputs = process_directory(root, recursive=True, role_prefix="role")
assert "secret" not in defaults
assert "real.ini" in defaults
def test_xml_parser_rejects_dtd_and_entities(tmp_path: Path):
xml_path = tmp_path / "evil.xml"
xml_path.write_text('<!DOCTYPE r [<!ENTITY x "boom">]><r>&x;</r>', encoding="utf-8")
with pytest.raises(DTDForbidden):
parse_config(xml_path)
def test_xml_source_comments_cannot_forge_internal_markers():
source = (
"<root><!--IF:role_missing--><name>ok</name><!--ENDIF:role_missing--></root>"
)
template = XmlHandler().generate_jinja2_template(None, "role", original_text=source)
assert "{% if role_missing is defined %}" not in template
assert "{% endif %}" not in template
assert "IF:role_missing" in template
assert "ENDIF:role_missing" in template
def test_cli_refuses_to_write_output_symlink(tmp_path: Path):
import subprocess
import sys
src = tmp_path / "input.ini"
src.write_text("[s]\nname = ok\n", encoding="utf-8")
target = tmp_path / "target.yml"
target.write_text("do not overwrite\n", encoding="utf-8")
link = tmp_path / "defaults.yml"
link.symlink_to(target)
result = subprocess.run(
[
sys.executable,
"-m",
"jinjaturtle.cli",
str(src),
"-d",
str(link),
],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env={"PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src")},
check=False,
)
assert result.returncode != 0
assert target.read_text(encoding="utf-8") == "do not overwrite\n"