2020import operator
2121import pathlib
2222import re
23- import uuid
2423from typing import Any , Dict , Optional
2524
26- from jinja2 import ChoiceLoader , DictLoader , Environment , FileSystemLoader , Template
25+ from jinja2 import Environment , FileSystemLoader , Template
2726
2827from ruamel .yaml import YAML
2928
4948class JinjaCmd (CommandPlusDocs ):
5049 """Transform an input template to an output document using jinja templating."""
5150
52- max_recursion_depth = 2
53-
5451 name = 'jinja'
5552
5653 def _init_arguments (self ) -> None :
@@ -192,9 +189,7 @@ def jinja_ify(
192189 ) -> int :
193190 """Run jinja over an input file with additional booleans."""
194191 template_folder = pathlib .Path .cwd ()
195- jinja_env = Environment (
196- loader = FileSystemLoader (template_folder ), extensions = extensions (), trim_blocks = True , autoescape = True
197- )
192+ jinja_env = JinjaCmd ._create_jinja_environment (template_folder )
198193 template = jinja_env .get_template (str (r_input_file ))
199194 # create boolean dict
200195 if operator .xor (bool (ssp ), bool (profile )):
@@ -286,9 +281,7 @@ def jinja_multiple_md(
286281
287282 control_writer = DocsControlWriter ()
288283
289- jinja_env = Environment (
290- loader = FileSystemLoader (template_folder ), extensions = extensions (), trim_blocks = True , autoescape = True
291- )
284+ jinja_env = JinjaCmd ._create_jinja_environment (template_folder )
292285 template = jinja_env .get_template (str (r_input_file ))
293286 lut ['catalog_interface' ] = catalog_interface
294287 lut ['control_interface' ] = ControlInterface ()
@@ -308,27 +301,16 @@ def jinja_multiple_md(
308301 return CmdReturnCodes .SUCCESS .value
309302
310303 @staticmethod
311- def render_template (template : Template , lut : Dict [str , Any ], template_folder : pathlib .Path ) -> str :
312- """Render template."""
313- new_output = template .render (** lut )
314- output = ''
315- # This recursion allows nesting within expressions (e.g. an expression can contain jinja templates).
316- error_countdown = JinjaCmd .max_recursion_depth
317- while new_output != output and error_countdown > 0 :
318- error_countdown = error_countdown - 1
319- output = new_output
320- random_name = uuid .uuid4 () # Should be random and not used.
321- dict_loader = DictLoader ({str (random_name ): new_output })
322- jinja_env = Environment (
323- loader = ChoiceLoader ([dict_loader , FileSystemLoader (template_folder )]),
324- extensions = extensions (),
325- autoescape = True ,
326- trim_blocks = True ,
327- )
328- template = jinja_env .get_template (str (random_name ))
329- new_output = template .render (** lut )
304+ def _create_jinja_environment (template_folder : pathlib .Path ) -> Environment :
305+ """Create the trusted Jinja environment used for loading template files."""
306+ return Environment (
307+ loader = FileSystemLoader (template_folder ), extensions = extensions (), trim_blocks = True , autoescape = True
308+ )
330309
331- return output
310+ @staticmethod
311+ def render_template (template : Template , lut : Dict [str , Any ], template_folder : pathlib .Path ) -> str :
312+ """Render a trusted template exactly once to avoid recursive SSTI of untrusted data."""
313+ return template .render (** lut )
332314
333315
334316def _number_captions (md_body : str ) -> str :
0 commit comments