From 2a05c59b5f820bd7f4eeee0d84df86e6295b5643 Mon Sep 17 00:00:00 2001 From: Marc Worrell Date: Fri, 13 Dec 2024 16:04:03 +0100 Subject: [PATCH] Fix a problem where having duplicated blocks could result in endless recursion (#55) --- src/template_compiler.erl | 45 +++++++++++++++++++++----- test/template_compiler_basic_SUITE.erl | 5 +++ test/test-data/block_nested_error.tpl | 9 ++++++ 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 test/test-data/block_nested_error.tpl diff --git a/src/template_compiler.erl b/src/template_compiler.erl index 19e965b..40f65fa 100644 --- a/src/template_compiler.erl +++ b/src/template_compiler.erl @@ -423,18 +423,33 @@ cs(Module, Filename, Options, Context) -> compile_tokens({ok, {extends, {string_literal, _, Extend}, Elements}}, CState, _Options) -> Blocks = find_blocks(Elements), - {Ws, BlockAsts} = compile_blocks(Blocks, CState), - {ok, {Extend, Ws#ws.includes, BlockAsts, undefined, Ws#ws.is_autoid_var}}; + case check_duplicate_blocks(Blocks) of + ok -> + {Ws, BlockAsts} = compile_blocks(Blocks, CState), + {ok, {Extend, Ws#ws.includes, BlockAsts, undefined, Ws#ws.is_autoid_var}}; + {error, _} = Error -> + Error + end; compile_tokens({ok, {overrules, Elements}}, CState, _Options) -> Blocks = find_blocks(Elements), - {Ws, BlockAsts} = compile_blocks(Blocks, CState), - {ok, {overrules, Ws#ws.includes, BlockAsts, undefined, Ws#ws.is_autoid_var}}; + case check_duplicate_blocks(Blocks) of + ok -> + {Ws, BlockAsts} = compile_blocks(Blocks, CState), + {ok, {overrules, Ws#ws.includes, BlockAsts, undefined, Ws#ws.is_autoid_var}}; + {error, _} = Error -> + Error + end; compile_tokens({ok, {base, Elements}}, CState, _Options) -> Blocks = find_blocks(Elements), - {Ws, BlockAsts} = compile_blocks(Blocks, CState), - CStateElts = CState#cs{blocks = BlockAsts}, - {Ws1, TemplateAsts} = template_compiler_element:compile(Elements, CStateElts, Ws), - {ok, {undefined, Ws1#ws.includes, BlockAsts, TemplateAsts, Ws1#ws.is_autoid_var}}; + case check_duplicate_blocks(Blocks) of + ok -> + {Ws, BlockAsts} = compile_blocks(Blocks, CState), + CStateElts = CState#cs{blocks = BlockAsts}, + {Ws1, TemplateAsts} = template_compiler_element:compile(Elements, CStateElts, Ws), + {ok, {undefined, Ws1#ws.includes, BlockAsts, TemplateAsts, Ws1#ws.is_autoid_var}}; + {error, _} = Error -> + Error + end; compile_tokens({error, {Loc, template_compiler_parser, Msg}}, #cs{ filename = Filename }, Options) -> % Try format the Yecc error Err = split_loc(Loc), @@ -522,6 +537,20 @@ block_elements({filter, _, Elts}) -> Elts; block_elements(_) -> []. +check_duplicate_blocks(Blocks) -> + check_duplicate_blocks_1(Blocks, #{}). + +check_duplicate_blocks_1([], _Acc) -> + ok; +check_duplicate_blocks_1([{block, {identifier, _Pos, Name}, _Elements}|Blocks], Acc) -> + case maps:is_key(Name, Acc) of + true -> + {error, {duplicate_block, Name}}; + false -> + check_duplicate_blocks_1(Blocks, Acc#{ Name => true }) + end. + + %% @doc Optionally drop text before {% extends %} or {% overrules %}. maybe_drop_text([{text, _SrcRef, _Text}|Rest], OrgTks) -> maybe_drop_text(Rest, OrgTks); diff --git a/test/template_compiler_basic_SUITE.erl b/test/template_compiler_basic_SUITE.erl index 2e87647..dd175c8 100644 --- a/test/template_compiler_basic_SUITE.erl +++ b/test/template_compiler_basic_SUITE.erl @@ -23,6 +23,7 @@ groups() -> ,hello_world_block2_test ,hello_world_block3_test ,hello_world_comment_test + ,block_nested_error_test ,block_render_test ,raw_test ]}]. @@ -72,6 +73,10 @@ hello_world_comment_test(_Config) -> <<"Hello World!">> = iolist_to_binary(Bin), ok. +block_nested_error_test(_Config) -> + {error, {duplicate_block, <<"main">>}} = template_compiler:render("block_nested_error.tpl", #{}, [], undefined), + ok. + block_render_test(_Config) -> {ok, BinA} = template_compiler:render_block(a, "block_render.tpl", #{}, [], undefined), <<"A">> = iolist_to_binary(BinA), diff --git a/test/test-data/block_nested_error.tpl b/test/test-data/block_nested_error.tpl new file mode 100644 index 0000000..54239eb --- /dev/null +++ b/test/test-data/block_nested_error.tpl @@ -0,0 +1,9 @@ +{% block main %} + Hallo + {% block test %} + Daar + {% block main %} + oops - a block with the same name + {% endblock %} + {% endblock %} +{% endblock%}