VYPR
High severityNVD Advisory· Published Mar 23, 2026· Updated Mar 24, 2026

Indico discloses local files resulting in Remote Code Execution through LaTeX injection

CVE-2026-33046

Description

Indico is an event management system that uses Flask-Multipass, a multi-backend authentication system for Flask. In versions prior to 3.3.12, due to vulnerabilities in TeXLive and obscure LaTeX syntax that allowed circumventing Indico's LaTeX sanitizer, it is possible to use specially-crafted LaTeX snippets which can read local files or execute code with the privileges of the user running Indico on the server. Note that if server-side LaTeX rendering is not in use (ie XELATEX_PATH was not set in indico.conf), this vulnerability does not apply. It is recommended to update to Indico 3.3.12 as soon as possible. It is also strongly recommended to enable the containerized LaTeX renderer (using podman), which isolates it from the rest of the system. As a workaround, remove the XELATEX_PATH setting from indico.conf (or comment it out or set it to None) and restart the indico-uwsgi and indico-celery services to disable LaTeX functionality.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
indicoPyPI
< 3.3.123.3.12

Affected products

1

Patches

4
5f24d23ce9c4

Use more thorough LaTeX caret parser (#7382)

https://github.com/indico/indicoAdrianMar 13, 2026via ghsa
2 files changed · +86 36
  • indico/util/mdx_latex.py+31 7 modified
    @@ -161,11 +161,37 @@ def unescape_html_entities(text):
     
     
     def _resolve_latex_carets(text):
    -    while True:
    -        text, n = re.subn(r'\^\^0*([0-9a-fA-F]{2})', lambda m: chr(int(m.group(1), 16)), text)
    -        if not n:
    -            break
    -    return text.replace('^^\x1c', '\\')
    +    """Resolve LaTeX double-caret escape sequences.
    +
    +    See this TeX.SE answer for details on how LaTeX handles such sequences:
    +    https://tex.stackexchange.com/a/64848/1651
    +    """
    +    done = False
    +    while not done:
    +        done = True
    +        while m := re.search(r'(\^{2,})(?=[a-f0-9])', text):
    +            num = len(m.group(1))
    +            start = m.start(1)
    +            end = m.end(1)
    +            if not re.match(rf'[a-f0-9]{{{num}}}', text[end : end + num]):
    +                break
    +            ccode = int(text[end : end + num], 16)
    +            char = chr(ccode) if ccode and ccode <= 0x10ffff else ''  # avoid NULs and invalid charchodes
    +            text = text[:start] + char + text[end + num :]
    +            done = False
    +        if m := re.search(r'(\^\^)([\x00-\xbf])', text):
    +            start = m.start(1)
    +            end = m.end(2)
    +            ccode = ord(m.group(2))
    +            # we ignore the -64 case for low char codes since this mostly adds nonsense
    +            # and simply swallowing that character instead of inserting it should be safe.
    +            # this also matches what the latex renderer on overleaf does when there's a
    +            # string with an invalid caret escape sequence, ie the carets disappear and
    +            # the rest remains as-is.
    +            char = chr(ccode + 64) if ccode < 64 else ''
    +            text = text[:start] + char + text[end:]
    +            done = False
    +    return text
     
     
     def latex_escape(text, ignore_math=True, ignore_braces=False):
    @@ -206,8 +232,6 @@ def math_replace(m):
             text = re.sub(r'\$[^\$]+\$|\$\$[^\$]+\$\$', math_replace, text)
     
         pattern = re.compile('|'.join(re.escape(k) for k in chars))
    -    # handle encoded backslashes, the `chars` replacement below escapes them
    -    text = re.sub(r'\^{2,}(?:0*5c|\x1c)', r'\\', text)
         text = pattern.sub(substitute, text)
     
         if ignore_math:
    
  • indico/util/mdx_latex_test.py+55 29 modified
    @@ -10,39 +10,65 @@
     import pytest
     from markdown import Markdown
     
    -from indico.util.mdx_latex import LaTeXExtension, latex_escape
    +from indico.util.mdx_latex import LaTeXExtension, _resolve_latex_carets, latex_escape
     
     
    -def test_escape():
    -    assert latex_escape(r'\naughty') == r'\textbackslash{}naughty'
    -    assert latex_escape(r'^^5cnaughty') == r'\textbackslash{}naughty'
    -    assert latex_escape('^^\x1cnaughty') == r'\textbackslash{}naughty'
    -    assert latex_escape(r'^^^^^^00005cnaughty') == r'\textbackslash{}naughty'
    -    assert latex_escape(r'^^^^005cnaughty') == r'\textbackslash{}naughty'
    -    assert (latex_escape(r'this\\is\\harmless') ==
    -            r'this\textbackslash{}\textbackslash{}is\textbackslash{}\textbackslash{}harmless')
    -    assert latex_escape(r'\\\extranaughty') == r'\textbackslash{}\textbackslash{}\textbackslash{}extranaughty'
    -    assert latex_escape(r'\mbox{\naughty}') == r'\textbackslash{}mbox\{\textbackslash{}naughty\}'
    -    assert latex_escape(r'\mbox{\^^6eaughty}') == r'\textbackslash{}mbox\{\textbackslash{}\^{}\^{}6eaughty\}'
    +@pytest.mark.parametrize(('input', 'expected'), (
    +    (r'\naughty', r'\textbackslash{}naughty'),
    +    (r'^^5cnaughty', r'\^{}\^{}5cnaughty'),
    +    ('^^\x1cnaughty', '\\^{}\\^{}\x1cnaughty'),
    +    ('^^\x1c^^.aughty', '\\^{}\\^{}\x1c\\^{}\\^{}.aughty'),
    +    (r'^^^^^^00005cnaughty', r'\^{}\^{}\^{}\^{}\^{}\^{}00005cnaughty'),
    +    (r'^^^^005cnaughty', r'\^{}\^{}\^{}\^{}005cnaughty'),
    +    (r'this\\is\\harmless', r'this\textbackslash{}\textbackslash{}is\textbackslash{}\textbackslash{}harmless'),
    +    (r'\\\extranaughty', r'\textbackslash{}\textbackslash{}\textbackslash{}extranaughty'),
    +    (r'\mbox{\naughty}', r'\textbackslash{}mbox\{\textbackslash{}naughty\}'),
    +    (r'\mbox{\^^6eaughty}', r'\textbackslash{}mbox\{\textbackslash{}\^{}\^{}6eaughty\}'),
    +))
    +def test_escape(input, expected):
    +    assert latex_escape(input) == expected
    +
    +
    +@pytest.mark.parametrize(('input', 'expected'), (
    +    (r'\naughty', r'\protect $\\naughty$'),
    +    (r'\begin{naughty}', r'\protect $\\begin{naughty}$'),
    +    (r'\begin{equation}', r'\protect $\begin{equation}$'),
    +    (r'^^5cnaughty', r'\protect $\\naughty$'),
    +    ('^^\x1cnaughty', r'\protect $\\naughty$'),
    +    ('^^\x1c^^.aughty', r'\protect $\\naughty$'),
    +    (r'^^^^^^00005cnaughty', r'\protect $\\naughty$'),
    +    (r'^^^^005cnaughty', r'\protect $\\naughty$'),
    +    (r'\\naughty', r'\protect $\\naughty$'),
    +    (r'^^5e^5cnaughty', r'\protect $\\naughty$'),
    +    (r'\to^^64ay', r'\protect $\\today$'),
    +    (r'harm\\less', r'\protect $harm\\less$'),
    +    (r'\\\extranaughty', r'\protect $\\\\extranaughty$'),
    +    (r'\mbox{\naughty}', r'\protect $\mbox{\\naughty}$'),
    +    (r'\mbox{\^^6eaughty}', r'\protect $\mbox{\\naughty}$'),
    +    (r'\mbox{\very^^6eaughty}', r'\protect $\mbox{\\verynaughty}$'),
    +    (r'\epsilon_\psi^\theta', r'\protect $\epsilon_\psi^\theta$'),
    +))
    +def test_escape_math(input, expected):
    +    assert latex_escape(f'${input}$') == expected
     
     
    -def test_escape_math():
    -    assert latex_escape(r'$\naughty$') == r'\protect $\\naughty$'
    -    assert latex_escape(r'$\begin{naughty}$') == r'\protect $\\begin{naughty}$'
    -    assert latex_escape(r'$\begin{equation}$') == r'\protect $\begin{equation}$'
    -    assert latex_escape(r'$^^5cnaughty$') == r'\protect $\\naughty$'
    -    assert latex_escape('$^^\x1cnaughty$') == r'\protect $\\naughty$'
    -    assert latex_escape(r'$^^^^^^00005cnaughty$') == r'\protect $^^^^\\naughty$'
    -    assert latex_escape(r'$^^^^005cnaughty$') == r'\protect $^^\\naughty$'
    -    assert latex_escape(r'$\\naughty$') == r'\protect $\\naughty$'
    -    assert latex_escape(r'$^^5e^5cnaughty$') == r'\protect $\\naughty$'
    -    assert latex_escape(r'$\to^^64ay$') == r'\protect $\\today$'
    -    assert latex_escape(r'$harm\\less$') == r'\protect $harm\\less$'
    -    assert latex_escape(r'$\\\extranaughty$') == r'\protect $\\\\extranaughty$'
    -    assert latex_escape(r'$\mbox{\naughty}$') == r'\protect $\mbox{\\naughty}$'
    -    assert latex_escape(r'$\mbox{\^^6eaughty}$') == r'\protect $\mbox{\\naughty}$'
    -    assert latex_escape(r'$\mbox{\very^^6eaughty}$') == r'\protect $\mbox{\\verynaughty}$'
    -    assert latex_escape(r'$\epsilon_\psi^\theta$') == r'\protect $\epsilon_\psi^\theta$'
    +@pytest.mark.parametrize(('input', 'expected'), (
    +    (r'\^^4oday we are ^^5cnaughty \^^4aday', r'\today we are \naughty \Jday'),
    +    (r'\^^4aday we are ^^5cnaughty \^^4oday', r'\Jday we are \naughty \today'),
    +    (r'\^^4aday we are ^^5cnaughty \^^4aday', r'\Jday we are \naughty \Jday'),
    +    (r'\^^4oday', r'\today'),
    +    (r'^^5e^5ctoday', r'\today'),
    +    (r'^^5cnaughty', r'\naughty'),
    +    ('^^\x1cnaughty', r'\naughty'),
    +    ('^^\x1c^^.aughty', r'\naughty'),
    +    (r'^^^^^^00005cnaughty', r'\naughty'),
    +    (r'^^^^^^00x05cnaughty', r'00x05cnaughty'),
    +    (r'^^^^00x05cnaughty', r'^00x05cnaughty'),
    +    (r'^^^^^^10ffff', '\U0010ffff'),
    +    (r'^^^^^^110000', ''),
    +))
    +def test_resolve_carets(input, expected):
    +    assert _resolve_latex_carets(input) == expected
     
     
     @pytest.mark.parametrize(('input', 'expected'), (
    
0adb70f0ed66

Fix LaTeX regexps (third time's the charm) (#7377)

https://github.com/indico/indicoAdrianMar 11, 2026via ghsa
3 files changed · +56 8
  • indico/legacy/pdfinterface/latex.py+4 0 modified
    @@ -10,6 +10,7 @@
     import os
     import subprocess
     import tempfile
    +import time
     from importlib.resources import as_file
     from importlib.resources import files as res_files
     from io import BytesIO
    @@ -253,6 +254,7 @@ def run(self, template_name, **kwargs):
             source_filename, target_filename = self.prepare(template_name, **kwargs)
             log_filename = os.path.join(self.source_dir, 'output.log')
             log_file = open(log_filename, 'a+')  # noqa: SIM115
    +        start = time.time()
             try:
                 self.run_latex(source_filename, log_file)
                 if self.has_toc:
    @@ -264,6 +266,8 @@ def run(self, template_name, **kwargs):
                     # something went terribly wrong, no LaTeX file was produced
                     raise LaTeXRuntimeException(source_filename, log_filename)
     
    +        duration = time.time() - start
    +        Logger.get('pdflatex').info('Generated PDF in %.02f seconds', duration)
             return target_filename
     
     
    
  • indico/util/mdx_latex.py+38 6 modified
    @@ -103,7 +103,7 @@
     safe_mathmode_commands = {
         'above', 'abovewithdelims', 'acute', 'aleph', 'alpha', 'amalg', 'And', 'angle', 'approx', 'arccos', 'arcsin',
         'arctan', 'arg', 'array', 'Arrowvert', 'arrowvert', 'ast', 'asymp', 'atop', 'atopwithdelims', 'backslash',
    -    'bar', 'Bbb', 'begin', 'beta', 'bf', 'Big', 'big', 'bigcap', 'bigcirc', 'bigcup', 'Bigg', 'bigg',
    +    'bar', 'Bbb', 'beta', 'bf', 'Big', 'big', 'bigcap', 'bigcirc', 'bigcup', 'Bigg', 'bigg',
         'Biggl', 'biggl', 'Biggm', 'biggm', 'Biggr', 'biggr', 'Bigl', 'bigl', 'Bigm', 'bigm', 'bigodot', 'bigoplus',
         'bigotimes', 'Bigr', 'bigr', 'bigsqcup', 'bigtriangledown', 'bigtriangleup', 'biguplus', 'bigvee', 'bigwedge',
         'bmod', 'bot', 'bowtie', 'brace', 'bracevert', 'brack', 'breve', 'buildrel', 'bullet', 'cap', 'cases', 'cdot',
    @@ -141,6 +141,13 @@
         'vee', 'Vert', 'vert', 'vphantom', 'wedge', 'widehat', 'widetilde', 'wp', 'wr', 'Xi', 'xi', 'zeta', '\\'
     }
     
    +# XXX not sure if all of them make sense inside math blocks, but they're safe and used by people...
    +safe_environments = {
    +    'eqnarray', 'equation', 'center', 'equation*', 'array', 'align', 'figure', 'itemize', 'align*', 'table', 'tabular',
    +    'aligned', 'eqnarray*', 'enumerate', 'acronym', 'justify', 'gathered', 'pmatrix', 'description', 'multline',
    +    'cases', 'matrix',
    +}
    +
     
     class ImageURLException(Exception):
         pass
    @@ -153,6 +160,14 @@ def unescape_html_entities(text):
         return out.replace('&quot;', '"')
     
     
    +def _resolve_latex_carets(text):
    +    while True:
    +        text, n = re.subn(r'\^\^0*([0-9a-fA-F]{2})', lambda m: chr(int(m.group(1), 16)), text)
    +        if not n:
    +            break
    +    return text.replace('^^\x1c', '\\')
    +
    +
     def latex_escape(text, ignore_math=True, ignore_braces=False):
         if text is None:
             return ''
    @@ -191,7 +206,8 @@ def math_replace(m):
             text = re.sub(r'\$[^\$]+\$|\$\$[^\$]+\$\$', math_replace, text)
     
         pattern = re.compile('|'.join(re.escape(k) for k in chars))
    -    text = re.sub(r'\^{2,}0*5c', r'\\', text)  # handle encoded backslashes, the `chars` replacement below escapes them
    +    # handle encoded backslashes, the `chars` replacement below escapes them
    +    text = re.sub(r'\^{2,}(?:0*5c|\x1c)', r'\\', text)
         text = pattern.sub(substitute, text)
     
         if ignore_math:
    @@ -204,10 +220,26 @@ def math_replace(m):
     
     def sanitize_mathmode(text):
         def _escape_unsafe_command(m):
    -        command = m.group(1)
    -        return m.group(0) if command in safe_mathmode_commands else r'\\' + command
    -
    -    return re.sub(r'(?:\\|\^{2,}0*5c)([a-zA-Z]+|(?:\\|\^{2,}0*5c))', _escape_unsafe_command, text)
    +        fullcommand = m.group('fullcmd')  # full command w/ args but without leading backslash
    +        command = m.group('cmd1') or m.group('cmd2')  # just the command name
    +        arg = m.group('arg')  # the arg from inside {...} after the command
    +        if (command == 'begin' and arg in safe_environments) or command in safe_mathmode_commands:
    +            return m.group(0)
    +        else:
    +            return fr'\\{fullcommand}'
    +
    +    text = _resolve_latex_carets(text)
    +    return re.sub(r'''
    +        \\
    +        (?P<fullcmd>
    +            (?:
    +                (?P<cmd1>begin)        # command name if it's one where we care about the arg
    +                \s*\{(?P<arg>[^}]+)\}  # {arg}
    +            )
    +            |
    +            (?P<cmd2>[a-zA-Z]+|\\)     # command name if it's anything else or a backslash
    +        )
    +    ''', _escape_unsafe_command, text, flags=re.VERBOSE)
     
     
     def escape_latex_entities(text):
    
  • indico/util/mdx_latex_test.py+14 2 modified
    @@ -16,21 +16,33 @@
     def test_escape():
         assert latex_escape(r'\naughty') == r'\textbackslash{}naughty'
         assert latex_escape(r'^^5cnaughty') == r'\textbackslash{}naughty'
    +    assert latex_escape('^^\x1cnaughty') == r'\textbackslash{}naughty'
         assert latex_escape(r'^^^^^^00005cnaughty') == r'\textbackslash{}naughty'
         assert latex_escape(r'^^^^005cnaughty') == r'\textbackslash{}naughty'
         assert (latex_escape(r'this\\is\\harmless') ==
                 r'this\textbackslash{}\textbackslash{}is\textbackslash{}\textbackslash{}harmless')
         assert latex_escape(r'\\\extranaughty') == r'\textbackslash{}\textbackslash{}\textbackslash{}extranaughty'
    +    assert latex_escape(r'\mbox{\naughty}') == r'\textbackslash{}mbox\{\textbackslash{}naughty\}'
    +    assert latex_escape(r'\mbox{\^^6eaughty}') == r'\textbackslash{}mbox\{\textbackslash{}\^{}\^{}6eaughty\}'
     
     
     def test_escape_math():
         assert latex_escape(r'$\naughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$\begin{naughty}$') == r'\protect $\\begin{naughty}$'
    +    assert latex_escape(r'$\begin{equation}$') == r'\protect $\begin{equation}$'
         assert latex_escape(r'$^^5cnaughty$') == r'\protect $\\naughty$'
    -    assert latex_escape(r'$^^^^^^00005cnaughty$') == r'\protect $\\naughty$'
    -    assert latex_escape(r'$^^^^005cnaughty$') == r'\protect $\\naughty$'
    +    assert latex_escape('$^^\x1cnaughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$^^^^^^00005cnaughty$') == r'\protect $^^^^\\naughty$'
    +    assert latex_escape(r'$^^^^005cnaughty$') == r'\protect $^^\\naughty$'
         assert latex_escape(r'$\\naughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$^^5e^5cnaughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$\to^^64ay$') == r'\protect $\\today$'
         assert latex_escape(r'$harm\\less$') == r'\protect $harm\\less$'
         assert latex_escape(r'$\\\extranaughty$') == r'\protect $\\\\extranaughty$'
    +    assert latex_escape(r'$\mbox{\naughty}$') == r'\protect $\mbox{\\naughty}$'
    +    assert latex_escape(r'$\mbox{\^^6eaughty}$') == r'\protect $\mbox{\\naughty}$'
    +    assert latex_escape(r'$\mbox{\very^^6eaughty}$') == r'\protect $\mbox{\\verynaughty}$'
    +    assert latex_escape(r'$\epsilon_\psi^\theta$') == r'\protect $\epsilon_\psi^\theta$'
     
     
     @pytest.mark.parametrize(('input', 'expected'), (
    
fb169ced710c

Fix LaTeX regexps even more (#7373)

https://github.com/indico/indicoAdrianMar 5, 2026via ghsa
2 files changed · +9 5
  • indico/util/mdx_latex.py+5 5 modified
    @@ -164,7 +164,6 @@ def latex_escape(text, ignore_math=True, ignore_braces=False):
             '&': r'\&',
             '~': r'\~{}',
             '_': r'\_',
    -        '^^5c': r'\textbackslash{}',
             '^': r'\^{}',
             '\\': r'\textbackslash{}',
             '\x0c': '',
    @@ -192,22 +191,23 @@ def math_replace(m):
             text = re.sub(r'\$[^\$]+\$|\$\$[^\$]+\$\$', math_replace, text)
     
         pattern = re.compile('|'.join(re.escape(k) for k in chars))
    -    res = pattern.sub(substitute, text)
    +    text = re.sub(r'\^{2,}0*5c', r'\\', text)  # handle encoded backslashes, the `chars` replacement below escapes them
    +    text = pattern.sub(substitute, text)
     
         if ignore_math:
             # Sanitize math-mode segments and put them back in place
             math_segments = list(map(sanitize_mathmode, math_segments))
    -        res = re.sub(re.escape(math_placeholder), lambda _: '\\protect ' + math_segments.pop(0), res)
    +        text = re.sub(re.escape(math_placeholder), lambda _: '\\protect ' + math_segments.pop(0), text)
     
    -    return res
    +    return text
     
     
     def sanitize_mathmode(text):
         def _escape_unsafe_command(m):
             command = m.group(1)
             return m.group(0) if command in safe_mathmode_commands else r'\\' + command
     
    -    return re.sub(r'(?:\\|\^\^5c)([a-zA-Z]+|(?:\\|\^\^5c))', _escape_unsafe_command, text)
    +    return re.sub(r'(?:\\|\^{2,}0*5c)([a-zA-Z]+|(?:\\|\^{2,}0*5c))', _escape_unsafe_command, text)
     
     
     def escape_latex_entities(text):
    
  • indico/util/mdx_latex_test.py+4 0 modified
    @@ -16,6 +16,8 @@
     def test_escape():
         assert latex_escape(r'\naughty') == r'\textbackslash{}naughty'
         assert latex_escape(r'^^5cnaughty') == r'\textbackslash{}naughty'
    +    assert latex_escape(r'^^^^^^00005cnaughty') == r'\textbackslash{}naughty'
    +    assert latex_escape(r'^^^^005cnaughty') == r'\textbackslash{}naughty'
         assert (latex_escape(r'this\\is\\harmless') ==
                 r'this\textbackslash{}\textbackslash{}is\textbackslash{}\textbackslash{}harmless')
         assert latex_escape(r'\\\extranaughty') == r'\textbackslash{}\textbackslash{}\textbackslash{}extranaughty'
    @@ -24,6 +26,8 @@ def test_escape():
     def test_escape_math():
         assert latex_escape(r'$\naughty$') == r'\protect $\\naughty$'
         assert latex_escape(r'$^^5cnaughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$^^^^^^00005cnaughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$^^^^005cnaughty$') == r'\protect $\\naughty$'
         assert latex_escape(r'$\\naughty$') == r'\protect $\\naughty$'
         assert latex_escape(r'$harm\\less$') == r'\protect $harm\\less$'
         assert latex_escape(r'$\\\extranaughty$') == r'\protect $\\\\extranaughty$'
    
1dbb12525b3d

Fix LaTeX regexps (#7365)

https://github.com/indico/indicoAdrianMar 5, 2026via ghsa
2 files changed · +5 2
  • indico/util/mdx_latex.py+3 2 modified
    @@ -164,6 +164,7 @@ def latex_escape(text, ignore_math=True, ignore_braces=False):
             '&': r'\&',
             '~': r'\~{}',
             '_': r'\_',
    +        '^^5c': r'\textbackslash{}',
             '^': r'\^{}',
             '\\': r'\textbackslash{}',
             '\x0c': '',
    @@ -188,7 +189,7 @@ def math_replace(m):
     
         if ignore_math:
             # Extract math-mode segments and replace with placeholder
    -        text = re.sub(r'\$[^\$]+\$|\$\$(^\$)\$\$', math_replace, text)
    +        text = re.sub(r'\$[^\$]+\$|\$\$[^\$]+\$\$', math_replace, text)
     
         pattern = re.compile('|'.join(re.escape(k) for k in chars))
         res = pattern.sub(substitute, text)
    @@ -206,7 +207,7 @@ def _escape_unsafe_command(m):
             command = m.group(1)
             return m.group(0) if command in safe_mathmode_commands else r'\\' + command
     
    -    return re.sub(r'\\([a-zA-Z]+|\\)', _escape_unsafe_command, text)
    +    return re.sub(r'(?:\\|\^\^5c)([a-zA-Z]+|(?:\\|\^\^5c))', _escape_unsafe_command, text)
     
     
     def escape_latex_entities(text):
    
  • indico/util/mdx_latex_test.py+2 0 modified
    @@ -15,13 +15,15 @@
     
     def test_escape():
         assert latex_escape(r'\naughty') == r'\textbackslash{}naughty'
    +    assert latex_escape(r'^^5cnaughty') == r'\textbackslash{}naughty'
         assert (latex_escape(r'this\\is\\harmless') ==
                 r'this\textbackslash{}\textbackslash{}is\textbackslash{}\textbackslash{}harmless')
         assert latex_escape(r'\\\extranaughty') == r'\textbackslash{}\textbackslash{}\textbackslash{}extranaughty'
     
     
     def test_escape_math():
         assert latex_escape(r'$\naughty$') == r'\protect $\\naughty$'
    +    assert latex_escape(r'$^^5cnaughty$') == r'\protect $\\naughty$'
         assert latex_escape(r'$\\naughty$') == r'\protect $\\naughty$'
         assert latex_escape(r'$harm\\less$') == r'\protect $harm\\less$'
         assert latex_escape(r'$\\\extranaughty$') == r'\protect $\\\\extranaughty$'
    

Vulnerability mechanics

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

References

8

News mentions

0

No linked articles in our index yet.