Update dependency pymdown-extensions to v11 [SECURITY] - #71
Open
renovate[bot] wants to merge 1 commit into
Open
Conversation
renovate
Bot
force-pushed
the
renovate/pypi-pymdown-extensions-vulnerability
branch
2 times, most recently
from
April 27, 2026 23:46
513ea42 to
8049ff2
Compare
renovate
Bot
force-pushed
the
renovate/pypi-pymdown-extensions-vulnerability
branch
from
July 28, 2026 10:01
8049ff2 to
1e2ec8f
Compare
renovate
Bot
force-pushed
the
renovate/pypi-pymdown-extensions-vulnerability
branch
from
August 9, 2026 12:43
1e2ec8f to
ddd2e23
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
==9.9.2→==11.0.1Any file can be included with the pymdown-snippets extension
CVE-2023-32309 / GHSA-jh85-wwv9-24hv
More information
Details
Summary
Arbitrary file read when using include file syntax.
Details
By using the syntax
--8<--"/etc/passwd"or--8<--"/proc/self/environ"the content of these files will be rendered in the generated documentation. Additionally, a path relative to a specified, allowed base path can also be used to render the content of a file outside the specified base paths:--8<-- "../../../../etc/passwd".Within the Snippets extension, there exists a
base_pathoption but the implementation is vulnerable to Directory Traversal.The vulnerable section exists in
get_snippet_path(self, path)lines 155 to 174 in snippets.py.PoC
Impact
Any readable file on the host where the plugin is executing may have its content exposed. This can impact any use of Snippets that exposes the use of Snippets to external users.
It is never recommended to use Snippets to process user-facing, dynamic content. It is designed to process known content on the backend under the control of the host, but if someone were to accidentally enable it for user-facing content, undesired information could be exposed.
Suggestion
Specified snippets should be restricted to the configured, specified base paths as a safe default. Allowing relative or absolute paths that escape the specified base paths would need to be behind a feature switch that must be opt-in and would be at the developer's own risk.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
PyMdown Extensions has a ReDOS bug in its Figure Capture extension
CVE-2025-68142 / GHSA-r6h4-mm7h-8pmq
More information
Details
Impact
This issue describes a ReDOS bug found within the figure caption extension (
pymdownx.blocks.caption).In systems that take unchecked user content, this could cause long hangs when processing the data if a malicious payload was crafted.
Patches
This issue is patched in Release 10.16.1.
Workarounds
Some possible workarounds
If users are concerned about this vulnerability and process unknown user content without timeouts or other safeguards in place to prevent really large, malicious content being aimed at systems, the use of
pymdownx.blocks.captioncould be avoided until the library is updated to 10.16.1+.References
The original issue https://github.com/facelessuser/pymdown-extensions/issues/2716.
Description
The original issue came through PyMdown Extensions' normal issue tracker instead of the typical security flow: https://github.com/facelessuser/pymdown-extensions/issues/2716. Because this came through the normal issue flow, it was handled as a normal issue. In the future, PyMdown Extensions will ensure such issues, even if prematurely made public through the normal issue flow, are redirected through the typical security process.
The regular expression pattern in question is as follows:
The POC was provided by @ShangzhiXu
The issue with the above pattern is that
.was used, which accepts any character when we meant to use\.. The fix was to update the pattern to:Relevant PR with fix: https://github.com/facelessuser/pymdown-extensions/pull/2717
Version(s) & System Info
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:UReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
PyMdown Extensions: Path traversal in the b64 extension lets
read files outside base_path
CVE-2026-61632 / GHSA-9xwg-3r6f-jcx2
More information
Details
Summary
The
b64extension inlines images referenced by<img src="...">as base64 data URIs. When resolving thesrcpath it joins it onto the configuredbase_pathwithos.path.normpathand opens the result directly, with no check that the resolved path stays insidebase_path. Asrccontaining../sequences, or an absolute path, therefore reads a file outsidebase_pathas long as that file has an allowed image extension (.png,.jpg,.jpeg,.gif,.svg). The base64 of that file is then embedded in the rendered output, disclosing its contents.This is a separate code path from the
snippetstraversal issues (GHSA-jh85-wwv9-24hv, GHSA-62q4-447f-wv8h). It lives inpymdownx/b64.pyand has no path restriction of any kind. Confirmed on10.21.3installed from PyPI.Details
In
pymdownx/b64.py, functionrepl_path(around lines 68 to 90 onmain):There is no
startswith(base_path), noos.path.realpathcomparison, and no rejection of... Both branches are reachable from an attacker-controlledsrc.PoC
Reproduced against an unmodified
pymdown-extensions==10.21.3from PyPI. The script creates abase_pathdirectory and a PNG one level above it, then renders Markdown whose imagesrcpoints outsidebase_path, and confirms the outside file's bytes appear base64-encoded in the output.Output:
The base64 of a file outside
base_pathis present in the output. The absolute-path branch behaves the same way: an absolutesrcbypassesbase_pathentirely viaos.path.normpath(path). Both were confirmed leaking.Impact
An application that renders untrusted Markdown with
pymdownx.b64enabled exposes the contents of image-extension files on the server, or any path the process can read, to whoever controls the Markdown and whoever views the output. The reach is bounded by the image-extension check, so it is a targeted file read rather than full arbitrary read, but it still discloses file contents that were never meant to be exposed.Suggested fix
Resolve the real path and require it to stay within
base_pathbefore opening:The same containment check should apply to the absolute-path branch rather than trusting an absolute
src. Usingrealpathinstead ofabspathalso closes the related symlink-following gap in the snippets handler.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
pymdown-extensions: exponential-backtracking ReDoS in caret, tilde, betterem, and magiclink inline processors
CVE-2026-67422 / GHSA-gm37-52c6-37mw
More information
Details
Summary
Four inline processors in pymdown-extensions contain regular expressions with
exponential backtracking. A single untrusted Markdown line under
50 bytes drives
markdown.markdown()into unbounded CPU on the rendering thread(seconds at ~45 bytes, growing exponentially with each added character). All four
fire in the extension's default configuration
and are reachable through the documented public API. The
caret/tilde/betteremblow-up was introduced by the emphasis-pattern rewrite in PR #2547(first released in 10.13, Dec 2024) — earlier releases used a linear
(.+?)/([^\s]+?)content group — and is present through 11.0 (latest);magiclink's host pattern is long-standing and affects effectively all releases.Likely CWE-1333 (Inefficient Regular Expression Complexity).
This is a distinct issue from CVE-2025-68142 (ReDoS in
pymdownx.blocks.caption,RE_FIG_NUM, fixed in 10.16.1): different extensions, different regexes, and adifferent root cause (delimiter-run partition ambiguity rather than a
./\.typo).
Details
Four regexes share, or closely mirror, a vulnerable shape — an inner group that
can partition a run of the delimiter character into
{2,}-sized pieces inexponentially many ways, wrapped in a lazy
+?that must fail before the enginecan give up:
11.0)pymdownx.caret(superscript^…^)SUP2pymdownx/caret.py:56pymdownx.tilde(subscript~…~)SUB2pymdownx/tilde.py:55pymdownx.betterem(underscore_…_)SMART_UNDER_EM2(default)pymdownx/betterem.py:93pymdownx.magiclink(bare-URL autolink)RE_LINKpymdownx/magiclink.py:56(host at:59)pymdownx/caret.py:56(pymdown-extensions 11.0):The content group
(?:[^\^\s]|\^{2,})+?matches a run of carets only via the\^{2,}branch. A run of k carets can be split into ≥2-length pieces inexponentially many combinations; when no caret can serve as a valid closing
delimiter (the trailing
(?<![\^\s])(\^)cannot be satisfied), the engineexplores every partition before failing.
SUB2(tilde) andSMART_UNDER_EM2(betterem) are the same construct for
~and_. Inbetteremthe defaultsmart_enable='underscore'routes underscores toSmartUnderscoreProcessor→SMART_UNDER_EM2(betterem.py:93), which is the default-reachable,API-exploitable pattern; the non-smart
UNDER_EM2(:69, used only whensmart_enableisasterisk/disable) shares the shape but did not reproducethrough the public
markdown.markdown()pipeline on the tested payload, so a fixand regression test should target
SMART_UNDER_EM2.pymdownx/magiclink.py:59has the analogous ambiguity in the host portion, whereoverlapping character classes let a run of dots be grouped exponentially:
SUP2/SUB2/SMART_UNDER_EM2are applied at each delimiter occurrence via thedefault
PatternSequenceProcessorsubclasses (pymdownx/util.py);RE_LINKisapplied by
MagiclinkPattern(registered unconditionally at priority 85). In allfour cases, rendering
markdown.markdown(src, extensions=[ext])on untrustedsrcin default configuration is sufficient to reach the regex.PoC
Single self-contained script; runs against the pinned release in an ephemeral
env. Non-destructive — the input is ordinary Markdown text; the impact is CPU/time
(a per-render alarm caps each attempt so the script terminates).
Run:
The bug is in pymdown-extensions' own regexes run by the stdlib
reengine, so itis independent of the Markdown library version (
markdownpinned only forbyte-exact output). Observed output:
The per-step ratio stays roughly constant as the run grows (a fixed multiplicative
factor per fixed-size increment) — the signature of exponential, not polynomial,
backtracking. A valid, closed delimiter run of the same length exercises the same
regex yet renders in well under a millisecond, isolating the cost to the unclosed
crafted run. Extending it a few more characters pushes the render time into minutes
and beyond.
Impact
Denial of service: a sub-50-byte line pins the rendering thread at 100% CPU, with
no memory pressure to trip an OOM killer. Most Material/MkDocs usage renders
trusted author content at build time, but the untrusted-input exposure is concrete
in two settings:
General Python web apps that render user-supplied Markdown (comments, wikis,
issue/ticket bodies, chat, live preview) — notably any app using
pymdownx.extra, which bundlesbetteremwith the vulnerable defaultsmart_enable='underscore', or that reuses a Material-style extension block ina runtime renderer.
Hosted docs/CI systems that build untrusted, user-contributed Markdown, where a
single crafted line hangs the shared build worker.
Attacker: unauthenticated, remote (anyone who can submit Markdown).
Configuration: default for each extension.
Proposed CWE-1333. Proposed CVSS 3.1 (as proposed — the maintainer makes
the final call):
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H(7.5, High).Suggestion
The vulnerable content groups need to be rewritten so a delimiter run has exactly
one parse, removing the
{2,}partition ambiguity that lets the enginere-segment a run on backtracking. For the emphasis patterns, restructuring the
content so a delimiter run is consumed in a single, non-re-partitionable way
(rather than by a
{2,}branch inside a+?group) removes the blow-up; forRE_LINK, disambiguate the host so.is matched in exactly one place (a singlelabelled-host pattern such as
(?:[-\w]+)(?:\.[-\w]+)*) rather than byoverlapping classes. Possessive quantifiers / atomic groups are the most direct
tool but require Python 3.11+; since the project supports Python 3.10, a
structural rewrite is the portable option.
A regression fixture per extension (a short delimiter run with no valid closer,
asserted to render under a small time budget) would guard against reintroduction.
References
pymdown-extensions 11.0):pymdownx/caret.py:56(SUP2),pymdownx/tilde.py:55(SUB2),pymdownx/betterem.py:93(SMART_UNDER_EM2,default;
:69UNDER_EM2shares the shape),pymdownx/magiclink.py:56(
RE_LINK, host subexpression at:59).pymdownx.blocks.captionRE_FIG_NUM,fixed 10.16.1) but distinct extensions, regexes, and root cause. The
caret/tilde/betteremcontent groups gained the vulnerable{2,}alternation in PR #2547 (v10.13); earlier releases used a linear
(.+?)/([^\s]+?)group.magiclink's host pattern is long-standing. Noneof the four has been touched by a prior security fix; all are present in the
latest release (11.0).
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
facelessuser/pymdown-extensions (pymdown-extensions)
v11.0.1Compare Source
11.0.1
v11.0Compare Source
11.0
base_pathby default. Can be disabled by setting newrestrict_pathoption to
False. The newroot_pathcan be specified if paths are desired to be restricted to a differentlocation separate
base_pathwhich is also used as a relative base for image paths.v10.21.3Compare Source
10.21.3
restrict_base_pathis enabled (the default). Found by @gistrec.v10.21.2: 10.21. 2Compare Source
10.21.2
None.v10.2110.21
@joapuiib.
v10.20Compare Source
10.20
NEW: Quotes: New blockquotes extension added that uses a more modern approach when compared to Python Markdown's
default. Quotes specifically will not group consecutive blockquotes together in the same lazy fashion that the
default Python Markdown does which follows a more modern trend to how parsers these days handle block quotes.
In addition, Quotes also provides an optional feature to enable specifying callouts/alerts in the style used by
GitHub and Obsidian.
v10.19.1Compare Source
10.19.1
$$math used inline within a paragraph could result in nested mathparsing.
v10.19Compare Source
10.19
viewmode deprecation as some still like to use it, though further enhancements to thismode are not planned.
v10.18Compare Source
10.18
viewmode has been deprecated. To avoid warnings or future issues, explicitly setmodetoeither
acceptorreject. In the future, the new default will beacceptand theviewmode will be removedentirely.
importantshould have always been available as a default.v10.17.2Compare Source
10.17.2
and other non-conflicting blocks.
v10.17.1Compare Source
10.17.1
v10.17Compare Source
10.17
#idsyntax.v10.16.1: 10.6.1Compare Source
10.16.1
v10.16Compare Source
10.16
max_retriesandbackoff_retriesoptions to configure new retry logic for HTTP 429errors (Too Many Requests client error).
desired.
v10.15Compare Source
10.15.0
relaxed_headersoption which can tolerate bad content in the fenced code header. Whenenabled, code blocks with bad content in the header will likely still convert into code blocks, often respecting
the specified language.
combine_header_slugwas enabled and there was noheader.
v10.14.3Compare Source
10.14.3
v10.14.2Compare Source
10.14.2
md_in_html.v10.14.1Compare Source
10.14.1
.are handled correctly..or).v10.14Compare Source
10.14
customoption to specify tags and the assumed handling for them when automatic modeis assumed. This can also be used to override the handling for recognized tags with automatic handling.
v10.13Compare Source
10.13
,.v10.12Compare Source
10.12
pymdownx.blocks.detailsapproach.pymdownx.blocks.tabapproach.strictoption that will raise an exception if an emoji is used whose name has changed,removed, or never existed.
v10.11.2Compare Source
10.11.2
v10.11.1Compare Source
10.11.1
can cause a fence to not be parsed.
v10.11Compare Source
10.11
```lang {.class #id}.v10.10.2Compare Source
10.10.2
*em, **em,strong***and_em, __em,strong___cases.*sup, **sup,ins***.*sub, **sub,del***.v10.10.1Compare Source
10.10.1
v10.10Compare Source
10.10
xinstead oftwitter.twitteris still recognized but isnow deprecated and will be removed at a future time.
urlunparse.v10.9Compare Source
10.9
v10.8.1Compare Source
10.8.1
v10.8Compare Source
10.8
v10.7.1Compare Source
10.7.1
v10.7Compare Source
10.7
:octocat:are no longer resolved.
default_langwhich will cause code blocks with no language specifier to behighlighted with the specified default language instead of plain text. This affects indented code blocks and code
blocks defined with SuperFences.
style_plain_textcan be specified with a language string (in addition to its previousboolean requirement) to treat inline code blocks with no explicit language specifier with a specific default
language.
v10.6Compare Source
10.6
v10.5Compare Source
10.5
v10.4Compare Source
10.4
base_pathto better support interactions with MkDocs.v10.3.1Compare Source
10.3.1
v10.3Compare Source
10.3
v10.2.1Compare Source
10.2.1
v10.2Compare Source
10.2
stripnloption to configure Pygments' default handling of stripping leading andand trailing new lines from code blocks. Mainly affects fenced code blocks.
an exception.
v10.1Compare Source
v10.0.1Compare Source
10.0.1
v10.0Compare Source
10.0
base_pathpreventing snippetsrelative to the
base_pathbut not explicitly under it.restrict_base_pathcan be set toFalsefor legacybehavior.
v9.11Compare Source
9.11
v9.10Compare Source
9.10
containers for specialized parsing. A number of extensions utilizing general purpose blocks are included and are meant
to be an alternative to (and maybe one day replace): Admonitions, Details, Definition Lists, and Tabbed. Also adds a
new HTML plugin for quick wrapping of content with arbitrary HTML elements.
ids will be generated using that code ID instead of the code block count.
-and_.check_pathsis enabled, and a specified section is not found, raise an error.dedent_sectionsthat will de-indent (remove any common leadingwhitespace from every line in text) from that block of text.
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.