From d47b791957e64f063521b7febe5c752b5885f00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Sat, 5 Jul 2025 21:06:53 +0200 Subject: [PATCH] parser: fix `check_expr()` recursion `check_expr()` did not recurse for all recursive `Expr`s. This PR fixes the problem. Resolves . --- askama_parser/src/expr.rs | 21 ++++++---- askama_parser/src/tests.rs | 42 +++++++++++++++++++ ...testcase-minimized-derive-4793932351602688 | 1 + 3 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-4793932351602688 diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs index 132c2f09..e8b6a095 100644 --- a/askama_parser/src/expr.rs +++ b/askama_parser/src/expr.rs @@ -110,9 +110,11 @@ fn check_expr<'a>(expr: &WithSpan<'a, Expr<'a>>, allowed: Allowed) -> ParseResul } Ok(()) } - Expr::As(elem, _) | Expr::Unary(_, elem) | Expr::Group(elem) => { - check_expr(elem, Allowed::default()) - } + Expr::As(elem, _) + | Expr::Unary(_, elem) + | Expr::Group(elem) + | Expr::NamedArgument(_, elem) + | Expr::Try(elem) => check_expr(elem, Allowed::default()), Expr::Call(v) => { check_expr( &v.path, @@ -126,17 +128,20 @@ fn check_expr<'a>(expr: &WithSpan<'a, Expr<'a>>, allowed: Allowed) -> ParseResul } Ok(()) } + Expr::Filter(filter) => { + for arg in &filter.arguments { + check_expr(arg, Allowed::default())?; + } + Ok(()) + } + Expr::LetCond(cond) => check_expr(&cond.expr, Allowed::default()), Expr::ArgumentPlaceholder => cut_error!("unreachable", expr.span), Expr::BoolLit(_) | Expr::NumLit(_, _) | Expr::StrLit(_) | Expr::CharLit(_) - | Expr::Filter(_) - | Expr::NamedArgument(_, _) | Expr::RustMacro(_, _) - | Expr::Try(_) - | Expr::FilterSource - | Expr::LetCond(_) => Ok(()), + | Expr::FilterSource => Ok(()), } } diff --git a/askama_parser/src/tests.rs b/askama_parser/src/tests.rs index 97717ceb..8af1d232 100644 --- a/askama_parser/src/tests.rs +++ b/askama_parser/src/tests.rs @@ -1592,3 +1592,45 @@ fn test_macro_call_nested_comments() { )], ); } + +#[test] +fn test_try_reserved_raw_identifier() { + // Regression test for . + let syntax = Syntax::default(); + + for id in ["crate", "super", "Self"] { + let msg = format!("`{id}` cannot be used as an identifier"); + assert!( + Ast::from_str(&format!("{{{{ {id}? }}}}"), None, &syntax) + .unwrap_err() + .to_string() + .contains(&msg), + ); + assert!( + Ast::from_str(&format!("{{{{ {id}|filter }}}}"), None, &syntax) + .unwrap_err() + .to_string() + .contains(&msg), + ); + assert!( + Ast::from_str( + &format!("{{{{ var|filter(arg1, {id}, arg3) }}}}"), + None, + &syntax + ) + .unwrap_err() + .to_string() + .contains(&msg), + ); + assert!( + Ast::from_str( + &format!("{{{{ var|filter(arg1=arg1, arg2={id}, arg3=arg3) }}}}"), + None, + &syntax + ) + .unwrap_err() + .to_string() + .contains(&msg), + ); + } +} diff --git a/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-4793932351602688 b/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-4793932351602688 new file mode 100644 index 00000000..f1a66cae --- /dev/null +++ b/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-4793932351602688 @@ -0,0 +1 @@ +ÿÿÿ{{crate?}} ÿdÿÅ \ No newline at end of file