class

Krikri::TemplateActionPlugin

Inherits Krikri::ActionPlugin < Reference < Object

Constants

FOR_TUPLE_PARENS = /(\{%-?\s*for\s+)\(([^)]+)\)(\s+in\s+)/

{% for (key, value) in dict.items() %} - the idiomatic real- Jinja2 way to iterate a dict's key/value pairs (mysql_hardening's own hardening.cnf.j2 writes it exactly this way). Only the parens around the loop variables need stripping - the vendored crinja fork's .items() is a real method on Hash values (see lib/crinja/src/runtime/python_hash_methods.cr), so .items() itself is left alone and evaluated for real rather than textually stripped. Was NOT always true: an earlier FOR_ITEMS_METHOD regex used to strip .items() out entirely and rely on Crinja's bare {% for k, v in dict %} already yielding (key, value) pairs - a real deviation from Python/Jinja2 (where a bare dict for-loop iterates keys only) that happened to produce the right pairs, but silently broke .items() | sort (jtyr.nsswitch's own nsswitch. conf.j2: {% for key, val in nsswitch_config.items() | sort %}) by sorting the raw dict instead of its item tuples. Removed once .items() support landed in the fork - verified live against both jtyr.nsswitch (.items() | sort) and jtyr.motd (.items() alone, item.motd.j2), byte-for-byte identical to Ansible.

JOIN_METHOD = /("(?:[^"\\\n]|\\.)*")\s*\.join\(\s*([^)\n]*?)\s*\)/

Method-call .join( form that Jinja2 permits but Crinja's parser rejects: {{ "SEP".join(LIST) }} is the standard sep.join(list) idiom (dev-sec os_hardening's securetty template uses it). Rewritten into the equivalent LIST | join("SEP") filter, which Crinja supports. $1 = the sep string literal, $2 = the list expression being joined. [^"\\] excludes \n too (not just cosmetic): a template with an ODD number of " characters total has no valid second quote to close a literal at all, so the unbounded (?:[^"\\]|\\.)* tries every possible split of the REST OF THE FILE between its two alternatives looking for one - mrlesmithjr.netdata's own 5934-line netdata.conf.j2 has exactly one stray " in the whole file, and this pattern alone was the one that actually overflowed PCRE2's JIT match-time stack. Real quoted string literals here are always single-line.

SPLIT_METHOD = /([A-Za-z_]\w*(?:\.[A-Za-z_]\w*|\[[^\]]*\])*)\.split\(([^)]*)\)(?:\[(\d+)\])?/

Method-call .split(...) - real Python's own str.split() method (not a Jinja2 filter - Jinja2 exposes native object methods directly), rejected by Crinja's parser the same way .items() was. dev-sec apache_hardening's own httpd.conf.j2 uses it to pick the minor version out of an already-parsed apache_version string: {% if apache_version.split('.')[1] == '4' %}. Rewritten to VAR | split(ARGS) (see jinja_filters.cr's own :split filter). $1 = the dotted/bracketed variable expression being split (apache_version, _apache_version.stdout, ...), $2 = the raw argument text (possibly empty, for Python's own no-arg whitespace- split form), $3 = an optional trailing literal numeric index (apache_version.split('.')[1] - split()'s result is almost always indexed immediately). Crinja can parse (EXPR).1 (dot- numeric indexing on a parenthesized expression) but NOT (EXPR)[1] (bracket indexing on one) - confirmed by direct testing, not assumed - so a captured trailing [N] is rewritten to .N rather than carried through as-is; #rewrite_crinja_compat's own gsub call is responsible for making that substitution (see below).

Group 1 alternates .ident/[...] suffixes in any order - real bug found benchmarking githubixx.ansible_role_wireguard's own wg.conf.j2: the old pattern only allowed dot-segments before any bracket-index suffix (foo.bar[0]), so hostvars[host]. wireguard_address.split('/')[0] (a bracket, THEN more dots) never matched starting from "hostvars" at all - the regex engine instead found a match starting mid-expression, from "wireguard_address" alone, silently leaving the "hostvars[host]." prefix untouched and producing the syntactically invalid "hostvars[host].(wireguard_ address | split('/', 0))" (a stray .( Crinja's own parser rejects outright: "Expected IDENTIFIER, got LEFT_PAREN").

TAG_IF_ELIF = /\{%(-|\+?)\s*(if|elif)\s+(.*?)\s*(-|\+?)%\}/

A {% if EXPR %}/{% elif EXPR %} statement tag - $1/$4 are the optional whitespace-control markers, either the trim - or the keep + (both preserved as-is on rewrite - the + must survive so the vendored Crinja fork's native + handling still sees it; the + forms used to be pre-stripped out of the whole template, see #render_template's comment for why that's gone), $2 the keyword, $3 the condition - each is wrapped with | pytruthy below (see the comment inside the gsub block) so a falsy-but- non-empty condition behaves like real Jinja2. Deliberately does NOT match {% for %} - for x in list is valid Crinja syntax on its own and must never be touched.

Instance methods

execute

Execute action on controller Returns: modified params to send to remote plugin, or nil if action failed

Source

Nested types