VYPR
Moderate severityNVD Advisory· Published Feb 1, 2021· Updated Sep 16, 2024

Regular Expression Denial of Service (ReDoS)

CVE-2020-28493

Description

This affects the package jinja2 from 0.0.0 and before 2.11.3. The ReDoS vulnerability is mainly due to the _punctuation_re regex operator and its use of multiple wildcards. The last wildcard is the most exploitable as it searches for trailing punctuation. This issue can be mitigated by Markdown to format user content instead of the urlize filter, or by implementing request timeouts and limiting process memory.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Jinja2 before 2.11.3 contains a ReDoS vulnerability in the urlize filter's `_punctuation_re` regex, allowing denial of service via crafted input.

Vulnerability

Overview

The vulnerability is a Regular Expression Denial of Service (ReDoS) in the Jinja2 templating engine, affecting versions from 0.0.0 up to (but not including) 2.11.3. The flaw resides in the _punctuation_re regex used by the urlize filter. This regex employs multiple wildcards, with the final wildcard being particularly exploitable as it searches for trailing punctuation, leading to catastrophic backtracking on certain inputs [1][4].

Exploitation

Conditions

An attacker can trigger the ReDoS by providing a specially crafted string to an application that uses the urlize filter on user-controlled content. No authentication is required if the application exposes the filter to unauthenticated users. The attack vector is network-based, with low attack complexity, as the attacker only needs to send a malicious payload that causes the regex engine to consume excessive CPU time [1][2].

Impact

Successful exploitation results in a denial of service (DoS) condition, where the affected application becomes unresponsive or crashes due to resource exhaustion. This can degrade service availability for legitimate users. The CVSS score is 7.5 (High) with a vector emphasizing network exploitability and low privileges required [1][2].

Mitigation

The vulnerability is fixed in Jinja2 version 2.11.3, which replaces the problematic regex with a more efficient implementation [3]. As a workaround, developers can use Markdown to format user content instead of the urlize filter, or implement request timeouts and limit process memory to mitigate the impact of potential ReDoS attacks [1][4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Jinja2PyPI
< 2.11.32.11.3

Affected products

124

Patches

1
15ef8f09b659

Merge pull request #1343 from pallets/urlize-speedup

https://github.com/pallets/jinjaDavid LordJan 31, 2021via ghsa
2 files changed · +66 51
  • CHANGES.rst+10 0 modified
    @@ -1,5 +1,15 @@
     .. currentmodule:: jinja2
     
    +Version 2.11.3
    +--------------
    +
    +Unreleased
    +
    +-   Improve the speed of the ``urlize`` filter by reducing regex
    +    backtracking. Email matching requires a word character at the start
    +    of the domain part, and only word characters in the TLD. :pr:`1343`
    +
    +
     Version 2.11.2
     --------------
     
    
  • src/jinja2/utils.py+56 51 modified
    @@ -6,6 +6,8 @@
     from collections import deque
     from random import choice
     from random import randrange
    +from string import ascii_letters as _letters
    +from string import digits as _digits
     from threading import Lock
     
     from markupsafe import escape
    @@ -16,20 +18,6 @@
     from ._compat import text_type
     from ._compat import url_quote
     
    -_word_split_re = re.compile(r"(\s+)")
    -_punctuation_re = re.compile(
    -    "^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$"
    -    % (
    -        "|".join(map(re.escape, ("(", "<", "&lt;"))),
    -        "|".join(map(re.escape, (".", ",", ")", ">", "\n", "&gt;"))),
    -    )
    -)
    -_simple_email_re = re.compile(r"^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$")
    -_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
    -_entity_re = re.compile(r"&([^;]+);")
    -_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    -_digits = "0123456789"
    -
     # special singleton representing missing values for the runtime
     missing = type("MissingType", (), {"__repr__": lambda x: "missing"})()
     
    @@ -210,48 +198,65 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
             and (x[:limit] + (len(x) >= limit and "..." or ""))
             or x
         )
    -    words = _word_split_re.split(text_type(escape(text)))
    +    words = re.split(r"(\s+)", text_type(escape(text)))
         rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ""
         target_attr = target and ' target="%s"' % escape(target) or ""
     
         for i, word in enumerate(words):
    -        match = _punctuation_re.match(word)
    +        head, middle, tail = "", word, ""
    +        match = re.match(r"^([(<]|&lt;)+", middle)
    +
             if match:
    -            lead, middle, trail = match.groups()
    -            if middle.startswith("www.") or (
    -                "@" not in middle
    -                and not middle.startswith("http://")
    -                and not middle.startswith("https://")
    -                and len(middle) > 0
    -                and middle[0] in _letters + _digits
    -                and (
    -                    middle.endswith(".org")
    -                    or middle.endswith(".net")
    -                    or middle.endswith(".com")
    -                )
    -            ):
    -                middle = '<a href="http://%s"%s%s>%s</a>' % (
    -                    middle,
    -                    rel_attr,
    -                    target_attr,
    -                    trim_url(middle),
    -                )
    -            if middle.startswith("http://") or middle.startswith("https://"):
    -                middle = '<a href="%s"%s%s>%s</a>' % (
    -                    middle,
    -                    rel_attr,
    -                    target_attr,
    -                    trim_url(middle),
    -                )
    -            if (
    -                "@" in middle
    -                and not middle.startswith("www.")
    -                and ":" not in middle
    -                and _simple_email_re.match(middle)
    -            ):
    -                middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
    -            if lead + middle + trail != word:
    -                words[i] = lead + middle + trail
    +            head = match.group()
    +            middle = middle[match.end() :]
    +
    +        # Unlike lead, which is anchored to the start of the string,
    +        # need to check that the string ends with any of the characters
    +        # before trying to match all of them, to avoid backtracking.
    +        if middle.endswith((")", ">", ".", ",", "\n", "&gt;")):
    +            match = re.search(r"([)>.,\n]|&gt;)+$", middle)
    +
    +            if match:
    +                tail = match.group()
    +                middle = middle[: match.start()]
    +
    +        if middle.startswith("www.") or (
    +            "@" not in middle
    +            and not middle.startswith("http://")
    +            and not middle.startswith("https://")
    +            and len(middle) > 0
    +            and middle[0] in _letters + _digits
    +            and (
    +                middle.endswith(".org")
    +                or middle.endswith(".net")
    +                or middle.endswith(".com")
    +            )
    +        ):
    +            middle = '<a href="http://%s"%s%s>%s</a>' % (
    +                middle,
    +                rel_attr,
    +                target_attr,
    +                trim_url(middle),
    +            )
    +
    +        if middle.startswith("http://") or middle.startswith("https://"):
    +            middle = '<a href="%s"%s%s>%s</a>' % (
    +                middle,
    +                rel_attr,
    +                target_attr,
    +                trim_url(middle),
    +            )
    +
    +        if (
    +            "@" in middle
    +            and not middle.startswith("www.")
    +            and ":" not in middle
    +            and re.match(r"^\S+@\w[\w.-]*\.\w+$", middle)
    +        ):
    +            middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
    +
    +        words[i] = head + middle + tail
    +
         return u"".join(words)
     
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.