#2109·jinja

poor performance when parsing unclosed string with many escape characters

Author: wsparks-vcCreated Jul 22, 2025Updated Aug 26, 2026

The lexer has poor performance on unclosed strings with many escape characters. It seems to be due to the string_re regular expression. It should probably be possible to make this run in linear or near-linear time instead by adjusting the regex or lexer.

python
import time


def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        elapsed_time = end_time - start_time
        print(f"Function '{func.__name__}' executed in {elapsed_time:.4f}s")
        return result

    return wrapper


def sizer(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print(f"Payload length: {args[0]}\nPayload size: {len(result.encode()) / (1024 ** 2)} MB")
        return result

    return wrapper


from jinja2 import Environment
from jinja2.lexer import get_lexer


@sizer
def create_payload(char_length: int):
    # The slow pattern: r"('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")'
    # This is slow on a string that starts with a quote, has many escape sequences,
    # and ends with characters that will cause backtracking
    payload = "'" + ("\\a" + "b" * char_length) * char_length + "c"
    return payload


@timer
def runner(char_length: int):
    try:
        # Create the payload
        payload = create_payload(char_length)

        # Get the lexer from jinja2
        env = Environment()
        lexer = get_lexer(env)

        # Tokenize the payload - this is slow
        tokens = list(lexer.tokenize(payload))

        return tokens
    except Exception as e:
        print(f"Exception occurred: {e}")
        return None


if __name__ == '__main__':
    runner(100)
    runner(1000)
    runner(2000)
    runner(4000)
    runner(8000)
    runne(10000)

Environment:

  • Python version: 3.13.5
  • Jinja version: 3.1.6