VYPR
Medium severity6.5OSV Advisory· Published Sep 29, 2025· Updated Apr 15, 2026

CVE-2025-59940

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.

PackageAffected versionsPatched versions
mkdocs-include-markdown-pluginPyPI
< 7.1.87.1.8

Affected products

2

Patches

1
7466d67aa0de

Escape placeholders to avoid input collisions (#277)

https://github.com/mondeja/mkdocs-include-markdown-pluginÁlvaro Mondéjar RubioSep 26, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.