Make var name collapse underscores
This commit is contained in:
parent
5ae85ad11e
commit
3573e8e750
2 changed files with 57 additions and 2 deletions
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable
|
||||
|
||||
|
|
@ -57,8 +58,20 @@ class BaseHandler:
|
|||
role_prefix_section_subsection_key
|
||||
|
||||
Sanitises parts to lowercase [a-z0-9_] and strips extras.
|
||||
|
||||
Consecutive separators are collapsed to a single underscore. This is
|
||||
required for correctness, not just aesthetics: a source key such as
|
||||
``log..level`` or ``cache--size`` would otherwise sanitise to a name
|
||||
containing a double underscore (``log__level``). The output safety gate
|
||||
in ``safety.py`` deliberately rejects *any* ``__`` in a generated
|
||||
identifier because ``__`` is the gateway to every Jinja2 SSTI gadget
|
||||
(``__class__``/``__globals__``/...). Emitting a dunder here would make
|
||||
JinjaTurtle's own gate reject JinjaTurtle's own placeholder, aborting
|
||||
generation on entirely benign config. Collapsing runs keeps every
|
||||
generated name a plain single-underscore-delimited identifier that the
|
||||
gate accepts.
|
||||
"""
|
||||
role_prefix = role_prefix.strip().lower()
|
||||
role_prefix = re.sub(r"_+", "_", role_prefix.strip().lower())
|
||||
clean_parts: list[str] = []
|
||||
|
||||
for part in path:
|
||||
|
|
@ -70,7 +83,9 @@ class BaseHandler:
|
|||
cleaned_chars.append(c.lower())
|
||||
else:
|
||||
cleaned_chars.append("_")
|
||||
cleaned_part = "".join(cleaned_chars).strip("_")
|
||||
# Collapse runs of underscores (from adjacent separators) to a
|
||||
# single "_" so the result can never contain a forbidden "__".
|
||||
cleaned_part = re.sub(r"_+", "_", "".join(cleaned_chars)).strip("_")
|
||||
if cleaned_part:
|
||||
clean_parts.append(cleaned_part)
|
||||
|
||||
|
|
|
|||
|
|
@ -191,3 +191,43 @@ def test_flatten_config_unsupported_format():
|
|||
flatten_config("bogusfmt", parsed=None)
|
||||
|
||||
assert "Unsupported format" in str(exc.value)
|
||||
|
||||
|
||||
def test_make_var_name_collapses_adjacent_separators():
|
||||
"""Regression: adjacent separators must not produce a forbidden ``__``.
|
||||
|
||||
A source key such as ``log..level`` or ``cache--size`` previously sanitised
|
||||
to a name containing a double underscore (``..._log__level``). The output
|
||||
safety gate in safety.py rejects *any* ``__`` in a generated identifier
|
||||
(it is the gateway to Jinja2 SSTI gadgets), so emitting one made JinjaTurtle
|
||||
reject its own placeholder and abort generation on entirely benign config.
|
||||
make_var_name now collapses runs of underscores to a single ``_``.
|
||||
"""
|
||||
for raw_key in ("log..level", "cache--size", "a...b", "x.-.y", "a..b--c"):
|
||||
name = make_var_name("role", ("main", raw_key))
|
||||
assert "__" not in name, f"{raw_key!r} produced {name!r}"
|
||||
# Still a valid Ansible/Jinja identifier.
|
||||
assert name.replace("_", "").isalnum() or name == "role"
|
||||
|
||||
# The double-underscore-free collapse is stable and predictable.
|
||||
assert make_var_name("role", ("main", "log..level")) == "role_main_log_level"
|
||||
assert make_var_name("role", ("main", "cache--size")) == "role_main_cache_size"
|
||||
# A leading-digit-free prefix with its own repeated separators is collapsed too.
|
||||
assert make_var_name("My__Role", ("k",)) == "my_role_k"
|
||||
|
||||
|
||||
def test_make_var_name_collapsed_names_pass_output_safety_gate():
|
||||
"""The names make_var_name emits must be accepted by the safety gate.
|
||||
|
||||
This binds the two modules together: whatever identifier make_var_name
|
||||
produces for a hostile-looking key must lex as the JinjaTurtle subset, so a
|
||||
real template built from it is not refused.
|
||||
"""
|
||||
from jinjaturtle.safety import verify_jinja2_template_safe
|
||||
|
||||
for raw_key in ("log..level", "cache--size", "a...b", "weird..key..name"):
|
||||
var = make_var_name("demo", ("section", raw_key))
|
||||
# Build the kind of expression a handler would emit for this variable.
|
||||
template = "{{ " + var + " }}"
|
||||
# Must not raise TemplateSafetyError.
|
||||
verify_jinja2_template_safe(template)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue