CVE-2025-59940
Description
mkdocs-include-markdown-plugin is an Mkdocs Markdown includer plugin. In versions 7.1.7 and below, there is a vulnerability where unvalidated input can collide with substitution placeholders. This issue is fixed in version 7.1.8.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In mkdocs-include-markdown-plugin before 7.1.8, unvalidated input can collide with substitution placeholders, leading to potential content injection.
Vulnerability
The mkdocs-include-markdown plugin uses internal placeholders (based on STX/ETX markers) to substitute included content during Markdown processing. In versions 7.1.7 and earlier, user-supplied input is not sanitized for these placeholder patterns. This allows an attacker to craft input that interferes with the substitution mechanism [1].
Exploitation
To exploit this, an attacker must supply content that includes placeholder-like strings. This is relevant in scenarios where user-controlled data (e.g., comments, form fields) is included via the plugin's directives. No authentication is required if the content is publicly writable, but the attack surface depends on the plugin's usage within a Mkdocs site [2].
Impact
If successful, the attacker can cause the plugin to misinterpret the placeholders, potentially resulting in inclusion of unintended content or omission of intended content. In a worst case, this could lead to injection of arbitrary Markdown or HTML, depending on how the final output is rendered. The CVSS score of 6.5 reflects medium severity [1][4].
Mitigation
The issue has been patched in version 7.1.8 by escaping placeholder characters in input [2][4]. Users are advised to update immediately. No workarounds are available.
AI Insight generated on May 19, 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.
| Package | Affected versions | Patched versions |
|---|---|---|
mkdocs-include-markdown-pluginPyPI | < 7.1.8 | 7.1.8 |
Affected products
2v1.0.0, v2.0.0, v2.1.0, …+ 1 more
- (no CPE)range: v1.0.0, v2.0.0, v2.1.0, …
- (no CPE)range: <=7.1.7
Patches
17466d67aa0deEscape placeholders to avoid input collisions (#277)
4 files changed · +67 −26
pyproject.toml+1 −1 modified@@ -1,6 +1,6 @@ [project] name = "mkdocs-include-markdown-plugin" -version = "7.1.7" +version = "7.1.8" description = "Mkdocs Markdown includer plugin." readme = "README.md" license = "Apache-2.0"
src/mkdocs_include_markdown_plugin/event.py+7 −25 modified@@ -28,6 +28,11 @@ ) from mkdocs_include_markdown_plugin.files_watcher import FilesWatcher from mkdocs_include_markdown_plugin.logger import logger +from mkdocs_include_markdown_plugin.placeholders import ( + escape_placeholders, + save_placeholder, + unescape_placeholders, +) if TYPE_CHECKING: # pragma: no cover @@ -46,30 +51,6 @@ ) -# Placeholders (taken from Python-Markdown) -STX = '\u0002' -''' "Start of Text" marker for placeholder templates. ''' -ETX = '\u0003' -''' "End of Text" marker for placeholder templates. ''' -INLINE_PLACEHOLDER_PREFIX = f'{STX}klzzwxh:' - - -def build_placeholder(num: int) -> str: - """Return a placeholder.""" - return f'{INLINE_PLACEHOLDER_PREFIX}{num}{ETX}' - - -def save_placeholder( - placeholders_contents: list[tuple[str, str]], - text_to_include: str, -) -> str: - """Save the included text and return the placeholder.""" - inclusion_index = len(placeholders_contents) - placeholder = build_placeholder(inclusion_index) - placeholders_contents.append((placeholder, text_to_include)) - return placeholder - - @dataclass class Settings: # noqa: D101 exclude: list[str] | None @@ -105,6 +86,7 @@ def get_file_content( # noqa: PLR0913, PLR0915 else: settings_ignore_paths = [] + markdown = escape_placeholders(markdown) placeholders_contents: list[tuple[str, str]] = [] def found_include_tag( # noqa: PLR0912, PLR0915 @@ -624,7 +606,7 @@ def found_include_markdown_tag( # noqa: PLR0912, PLR0915 # Replace placeholders by contents for placeholder, text in placeholders_contents: markdown = markdown.replace(placeholder, text, 1) - return markdown + return unescape_placeholders(markdown) def on_page_markdown(
src/mkdocs_include_markdown_plugin/placeholders.py+37 −0 added@@ -0,0 +1,37 @@ +"""Module for placeholders processing.""" + +# Placeholders (taken from Python-Markdown) +from __future__ import annotations + + +STX = '\u0002' +''' "Start of Text" marker for placeholder templates. ''' +ETX = '\u0003' +''' "End of Text" marker for placeholder templates. ''' +INLINE_PLACEHOLDER_PREFIX = f'{STX}klzzwxh:' + + +def build_placeholder(num: int) -> str: + """Return a placeholder.""" + return f'{INLINE_PLACEHOLDER_PREFIX}{num}{ETX}' + + +def escape_placeholders(text: str) -> str: + """Escape placeholders in the given text.""" + return text.replace(STX, f'\\{STX}').replace(ETX, f'\\{ETX}') + + +def unescape_placeholders(text: str) -> str: + """Unescape placeholders in the given text.""" + return text.replace(f'\\{STX}', STX).replace(f'\\{ETX}', ETX) + + +def save_placeholder( + placeholders_contents: list[tuple[str, str]], + text_to_include: str, +) -> str: + """Save the included text and return the placeholder.""" + inclusion_index = len(placeholders_contents) + placeholder = build_placeholder(inclusion_index) + placeholders_contents.append((placeholder, text_to_include)) + return placeholder
tests/test_unit/test_include_markdown.py+22 −0 modified@@ -3,6 +3,7 @@ import pytest from mkdocs_include_markdown_plugin.event import on_page_markdown +from mkdocs_include_markdown_plugin.placeholders import build_placeholder @pytest.mark.parametrize( @@ -776,6 +777,27 @@ [], id='internal-anchor', ), + + # Placeholder collision + pytest.param( + '''# Header + +''' + build_placeholder(0) + ''' + +{% + include-markdown "{filepath}" +%} +''', + 'Content to include', + '''# Header + +''' + build_placeholder(0) + ''' + +Content to include +''', + [], + id='placeholder-collision', + ), ), ) def test_include_markdown(
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-v39m-5m9j-m9w9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-59940ghsaADVISORY
- github.com/mondeja/mkdocs-include-markdown-plugin/commit/7466d67aa0de8ffbc427204ad2475fed07678915nvdWEB
- github.com/mondeja/mkdocs-include-markdown-plugin/issues/274nvdWEB
- github.com/mondeja/mkdocs-include-markdown-plugin/pull/277nvdWEB
- github.com/mondeja/mkdocs-include-markdown-plugin/security/advisories/GHSA-v39m-5m9j-m9w9nvdWEB
News mentions
0No linked articles in our index yet.