PYSEC-2026-2893

See a problem?
Import Source
https://github.com/pypa/advisory-database/blob/main/vulns/pptagent/PYSEC-2026-2893.yaml
JSON Data
https://api.osv.dev/v1/vulns/PYSEC-2026-2893
Aliases
Published
2026-07-13T15:15:37Z
Modified
2026-07-13T16:33:00Z
Severity
  • 4.6 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:L CVSS Calculator
Summary
PPTAgent: Arbitrary File Write + Directory Creation via markdown_table_to_image
Details

Summary

The markdown_table_to_image tool accepts a caller-controlled path parameter and passes it directly to get_html_table_image:

# pptagent/mcp_server.py:127-143
def markdown_table_to_image(markdown_table: str, path: str, css: str) -> str:
    """
    Args:
        path (str): The file path where the image will be saved
    """
    html = markdown_to_html(markdown_table)
    get_html_table_image(html, path, css)           # ← no path validation
    return f"Markdown table converted to image and saved to {path}"

# pptagent/utils.py:337-366
def get_html_table_image(html: str, output_path: str, css: str = None):
    parent_dir, base_name = os.path.split(output_path)
    if parent_dir and not os.path.exists(parent_dir):
        os.makedirs(parent_dir)                     # ← creates arbitrary directories
    hti = Html2Image(...)
    hti.screenshot(
        html_str=html,
        css_str=css,
        save_as=base_name,                          # ← writes image to any directory
        size=(1000, 600),
    )

os.makedirs(parent_dir) creates arbitrary directory trees, and Html2Image.screenshot writes the rendered image to parent_dir/base_name. Unlike download_file in the same project, there is no is_relative_to(workspace) guard. This behaviour can be fixed with the same pattern as the above.

Impact

The concrete attack scenarios include

  • SSH key replacement: path = "/home/user/.ssh/authorized_keys" — replaces the authorized_keys file with an image binary (breaks - SSH but could be an image crafted with a specific PNG/JPEG payload).
  • Web shell: path = "/var/www/html/uploads/shell.php" — writes the rendered PNG there; the file has the .php extension but PNG content; combined with Apache Options +MultiViews or file-include vulnerabilities could be dangerous.
  • Directory creation oracle: path = "/root/test/probe.png" — if the directory is created, confirms the target path exists; if it errors, reveals permissions information.
References

Affected packages

PyPI / pptagent

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
1.1.36

Affected versions

0.*
0.2.0
0.2.1
0.2.2
0.2.3
0.2.4
0.2.5
0.2.6
0.2.7
0.2.8
0.2.9
0.2.10
0.2.11
0.2.13
0.2.14
0.2.15
0.2.16
0.2.17
0.2.18
1.*
1.0.0
1.0.1
1.0.2
1.1.1
1.1.2
1.1.3
1.1.5
1.1.6
1.1.7
1.1.8
1.1.9
1.1.10
1.1.11
1.1.12
1.1.13
1.1.14
1.1.15
1.1.16
1.1.17
1.1.18
1.1.19
1.1.20
1.1.21
1.1.22
1.1.24
1.1.26
1.1.27
1.1.28
1.1.29
1.1.30
1.1.31
1.1.32
1.1.33
1.1.34
1.1.35

Database specific

source
"https://github.com/pypa/advisory-database/blob/main/vulns/pptagent/PYSEC-2026-2893.yaml"