Repository: rinja-rs/askama (language: rust)

Issue:
Fuzzing askama_parser results in panic
Hi, fuzzing `askama_parser` resulted in panic at following line.
https://github.com/djc/askama/blob/43e92aa3b6b9cd967a70bd0fd54d1f087d6ed76b/askama_parser/src/expr.rs#L290
I suppose it happens because crash input contains Cyrillic letters which are multi-byte and we need exact byte indices to avoid such panic in `[..]` notation.
```rust
#[test]
fn testing() {
    let d = "{eeuuu{b&{!!&{!!11{{
        0!(!1q҄א!)!!!!!!n!";
    if let Ok(_) = Ast::from_str(d, &Syntax::default()) {}
}
```
```
running 1 test
thread 'tests::testing' panicked at 'byte index 6 is not a char boundary; it is inside 'א' (bytes 5..7) of `!1q҄א!)!!!!!!n!`', askama_parser/src/expr.rs:290:22
stack backtrace:
```


Discussion / hints:

Fix patch:
diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs
index ca1dc9b4b..7f822668b 100644
--- a/askama_parser/src/expr.rs
+++ b/askama_parser/src/expr.rs
@@ -248,15 +248,15 @@ impl<'a> Suffix<'a> {
     }
 
     fn r#macro(i: &'a str) -> IResult<&'a str, Self> {
-        fn nested_parenthesis(i: &str) -> IResult<&str, ()> {
+        fn nested_parenthesis(input: &str) -> IResult<&str, ()> {
             let mut nested = 0;
             let mut last = 0;
             let mut in_str = false;
             let mut escaped = false;
 
-            for (i, b) in i.chars().enumerate() {
-                if !(b == '(' || b == ')') || !in_str {
-                    match b {
+            for (i, c) in input.char_indices() {
+                if !(c == '(' || c == ')') || !in_str {
+                    match c {
                         '(' => nested += 1,
                         ')' => {
                             if nested == 0 {
@@ -281,16 +281,16 @@ impl<'a> Suffix<'a> {
                     }
                 }
 
-                if escaped && b != '\\' {
+                if escaped && c != '\\' {
                     escaped = false;
                 }
             }
 
             if nested == 0 {
-                Ok((&i[last..], ()))
+                Ok((&input[last..], ()))
             } else {
                 Err(nom::Err::Error(error_position!(
-                    i,
+                    input,
                     ErrorKind::SeparatedNonEmptyList
                 )))
             }


Test patch:
diff --git a/askama_parser/src/tests.rs b/askama_parser/src/tests.rs
index ce300d36a..c23d13f69 100644
--- a/askama_parser/src/tests.rs
+++ b/askama_parser/src/tests.rs
@@ -788,3 +788,10 @@ fn test_parse_array() {
         )],
     );
 }
+
+#[test]
+fn fuzzed_unicode_slice() {
+    let d = "{eeuuu{b&{!!&{!!11{{
+            0!(!1q҄א!)!!!!!!n!";
+    assert!(Ast::from_str(d, &Syntax::default()).is_err());
+}