Back to skill

Security audit

Md To Pptx

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward Markdown-to-PPTX converter, but users should notice that it reads Obsidian configuration and may write converted files into an Obsidian vault by default.

Install only if you are comfortable with the converter reading your Obsidian configuration and placing output in your active vault by default. Prefer passing an explicit output path, and avoid running it on Markdown files in directories writable by other users because the intermediate .html file can overwrite an existing sibling path.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/md2pptx.py:40
Finding

Predictable Intermediate HTML File Allows Arbitrary Writable-File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/md2pptx.py, lines 40–43
Vulnerability Type: Predictable temporary file and symlink-following file overwrite
Risk Level: Medium

Vulnerable Code

python
# Save intermediate HTML
html_file = input_path.with_suffix('.html')
with open(html_file, 'w', encoding='utf-8') as f:
    f.write(html_content)

The related cleanup operation occurs at line 58:

python
html_file.unlink()  # Clean up HTML

Technical Analysis

The intermediate HTML pathname is deterministically derived from the input pathname. For example, converting presentation.md causes the script to write to presentation.html in the same directory.

Opening this predictable path with mode w silently truncates any existing file. It also follows symbolic links. Therefore, if an attacker can create files in the input directory, the attacker can create presentation.html as a symbolic link to another file writable by the user running the converter. The script then follows that link and replaces the target's contents with generated HTML.

On successful LibreOffice conversion, unlink() removes the intermediate pathname. If it was a symbolic link, this removes only the link after the linked target has already been corrupted. The implementation also lacks exclusive file creation, symlink checks, restrictive temporary-file permissions, and guaranteed cleanup through a finally block.

Attack Path

  1. The attacker obtains write access to a directory from which the victim will convert a Markdown file.
  2. The attacker predicts the intermediate name from the input name. For presentation.md, the intermediate path is presentation.html.
  3. The attacker creates presentation.html as a symbolic link to a sensitive file that the victim can write.
  4. The victim runs md2pptx.py presentation.md.
  5. The open(..., 'w') operation follows the symbolic link and truncates the target.

...[truncated 984 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create intermediate files with Python's tempfile module instead of deriving their names from user-controlled input:

    python
    import tempfile
    
    output_dir = Path(output_file).parent
    output_dir.mkdir(parents=True, exist_ok=True)
    
    with tempfile.TemporaryDirectory(prefix="md2pptx-") as temp_dir:
        html_file = Path(temp_dir) / "input.html"
        html_file.write_text(html_content, encoding="utf-8")
        # Run the converter and copy or move the validated result afterward.
    
  2. Use an isolated temporary directory with restrictive permissions, and let its context manager guarantee cleanup on success, timeout, conversion failure, or unexpected exception.

  3. Never overwrite a sibling file merely because it has the same stem as the Markdown input.

  4. If a fixed destination must be used, create it atomically and exclusively, reject symbolic links, and verify that the resolved path remains inside the intended directory.

  5. Create the output directory explicitly and validate the generated file before moving it to the requested destination.

  6. Handle output replacement deliberately using a documented overwrite policy and an atomic move, rather than relying on rename() behavior.

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises behavior that reads files, writes output files, and invokes shell commands, but it declares no explicit tool scope or permissions boundaries. That creates an authorization gap: an agent may execute broader file-system or shell actions than users expect, especially because the default output path targets a real user vault directory under the home folder.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script reads the user's Obsidian configuration to discover the active vault and then uses that unrelated personal path as a default output destination. This creates an unnecessary privacy boundary crossing and can cause sensitive data placement inside a user knowledge base without explicit consent, which is especially risky for an agent skill expected to perform a simple file conversion.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script writes an intermediate HTML file and a final PPTX file, and if no output path is supplied it silently places the result in the active Obsidian vault. Silent writes to user content locations can overwrite expectations, leak converted content into indexed note repositories, and create persistence in a sensitive workspace without informed consent.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest presents the skill as a document conversion utility, but the implementation shells out to system-installed binaries via subprocess. Spawning external executables is a materially broader capability than in-process conversion and is not disclosed in the manifest text.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/md2pptx.py (reported line 50)May include surrounding context.

python
# Use LibreOffice to convert HTML to PPTX
    try:
        result = subprocess.run([
            'soffice', '--headless', '--convert-to', 'pptx',
            '--outdir', str(Path(output_file).parent),
            str(html_file)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/md2pptx.py (reported line 68)May include surrounding context.

python
# Fallback: try pandoc
    try:
        result = subprocess.run([
            'pandoc', str(input_file), '-o', str(output_file)
        ], capture_output=True, text=True, timeout=30)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill defaults to writing the generated PPTX into the user's Obsidian vault, but the description does not prominently warn that content will be written there unless another path is specified. This can cause unintended data placement or overwriting in a sensitive knowledge base, especially when users assume conversion is in-place or ephemeral.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.