VYPR
Medium severity4.6NVD Advisory· Published May 4, 2026· Updated May 5, 2026

CVE-2026-42078

CVE-2026-42078

Description

PPTAgent is an agentic framework for reflective PowerPoint generation. Prior to commit 418491a, PPTAgent is vulnerable to arbitrary file write and directory creation via markdown_table_to_image. This issue has been patched via commit 418491a.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
pptagentPyPI
< 1.1.361.1.36

Affected products

1

Patches

1
418491a9a1c0

fix: pptagent

https://github.com/icip-cas/PPTAgentForce1essApr 19, 2026via ghsa
3 files changed · +30 5
  • pptagent/apis.py+2 1 modified
    @@ -23,6 +23,7 @@
     
     logger = get_logger(__name__)
     TABLE_REGEX = re.compile(r".*table_[0-9a-fA-F]{4}\.png$")
    +SAFE_EVAL_GLOBALS = {"__builtins__": {}}
     
     
     class SlideRenderer(HTMLRenderer):
    @@ -182,7 +183,7 @@ def execute_actions(
                     partial_func = partial(self.registered_functions[func], edit_slide)
                     if func == "replace_image":
                         partial_func = partial(partial_func, doc)
    -                eval(line, {}, {func: partial_func})
    +                eval(line, SAFE_EVAL_GLOBALS, {func: partial_func})
                     self.code_history[-1][0] = HistoryMark.CODE_RUN_CORRECT
                 except Exception as e:
                     if not isinstance(e, SlideEditError):
    
  • pptagent/mcp_server.py+3 2 modified
    @@ -23,6 +23,7 @@
         get_html_table_image,
         get_logger,
         package_join,
    +    resolve_path_in_workspace,
     )
     
     logger = get_logger(__name__)
    @@ -291,13 +292,13 @@ async def save_generated_slides(pptx_path: str):
                 Args:
                     pptx_path: The path to save the PowerPoint file
                 """
    -            pptx = Path(pptx_path)
    +            pptx = resolve_path_in_workspace(pptx_path)
                 assert len(self.slides), (
                     "No slides generated, please call `generate_slide` first"
                 )
                 pptx.parent.mkdir(parents=True, exist_ok=True)
                 self.empty_prs.slides = self.slides
    -            self.empty_prs.save(pptx_path)
    +            self.empty_prs.save(str(pptx))
                 self.slides = []
                 self._initialized = False
                 return f"total {len(self.empty_prs.slides)} slides saved to {pptx}"
    
  • pptagent/utils.py+25 2 modified
    @@ -98,6 +98,28 @@ def get_logger(name="pptagent", level=None):
             "unoconvert/soffice is not installed, pptx to images conversion will not work"
         )
     
    +
    +def resolve_path_in_workspace(path_str: str, workspace: Path | None = None) -> Path:
    +    """
    +    Resolve a path and ensure it stays inside the active workspace.
    +
    +    Args:
    +        path_str (str): User-provided file path.
    +        workspace (Path | None): Allowed workspace root. Defaults to current working
    +            directory.
    +
    +    Returns:
    +        Path: Resolved path within the workspace.
    +    """
    +    workspace_root = (workspace or Path.cwd()).resolve()
    +    target = Path(path_str).resolve()
    +    if not target.is_relative_to(workspace_root):
    +        raise ValueError(
    +            f"Access denied: path outside allowed workspace: {workspace_root}"
    +        )
    +    return target
    +
    +
     # Set of supported image extensions
     IMAGE_EXTENSIONS: set[str] = {
         "bmp",
    @@ -347,7 +369,8 @@ def get_html_table_image(html: str, output_path: str, css: str = None):
         """
         if css is None:
             css = TABLE_CSS
    -    parent_dir, base_name = os.path.split(output_path)
    +    output_file = resolve_path_in_workspace(output_path)
    +    parent_dir, base_name = os.path.split(output_file)
     
         if parent_dir and not os.path.exists(parent_dir):
             os.makedirs(parent_dir)
    @@ -364,7 +387,7 @@ def get_html_table_image(html: str, output_path: str, css: str = None):
             save_as=base_name,
             size=(1000, 600),
         )
    -    manual_scan_crop(output_path)
    +    manual_scan_crop(str(output_file))
     
     
     async def _is_unoserver_running(host: str, port: int) -> bool:
    

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

4

News mentions

0

No linked articles in our index yet.