Back to skill

Security audit

MD to PDF

Security checks for vulnerabilities and agentic risk

Overview

This Markdown-to-PDF skill mostly does what it claims, but it under-discloses risky browser-side execution and outbound sharing paths that users should review before installing.

Install only if you trust the Markdown files being converted and accept browser-based rendering with possible network access. Avoid using it on sensitive or untrusted documents unless Mermaid is disabled, raw HTML is sanitized or disabled, and QQ Bot sending is treated as a separate user-confirmed action.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
md_to_pdf_v4.py:804
Finding

Untrusted Markdown and document titles can execute active HTML in the rendering browser

Content
View full analysis
str: with open(md_path, "r", encoding="utf-8", errors="replace") as f: for line in f: line = line.strip() if line.startswith("# "): return sanitize_title(line[2:].strip()) return pathlib.Path(md_path).stem ``` The extracted title is inserted directly into HTML: ```python cover_html_content = f''' @page {{ size: A4; margin: 0; }} * {{ margin: 0; padding: 0; box-sizing: border-box; }} body {{ font-family: "Microsoft YaHei", "PingFang SC", sans-serif; height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; color: #111827; background: #ffffff; margin: 0; padding: 0; }} .cover-title {{ font-size: 28pt; font-weight: 700; margin-bottom: 80px; line-height: 1.4; }} .cover-date {{ font-size: 14pt; color: #6b7280; border-top: 2px solid #e5e7eb; padding-top: 20px; width: 200px; line-height: 1.5; }}
{title}
{today}
''' ``` The Markdown body is converted using a Pandoc reader configuration that does not explicitly disable raw HTML: ```python cmd = [ PANDOC, md_path, "-o", html_path, "--standalone", "--from", "markdown-fancy_lists", "--mathml", "--highlight-style", "pygments", "--metadata", "lang=zh-CN", ] ``` The resulting HTML is then loaded in a CDP-connected browser: ```python with sync_playwright() as p: browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222") page = browser.new_page(viewport={ ...[truncated 2948 chars]
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Warning
Location
md_to_pdf_v4.py:696
Finding

Mutable Mermaid JavaScript is downloaded and executed at conversion time

Content
View full analysis
{ if (!window.mermaid) { return { rendered: 0, failed: 0, total: 0, reason: 'mermaid_not_loaded' }; } window.mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', theme: 'default' }); ``` ### Technical Analysis When a document contains a candidate Mermaid block, the converter downloads JavaScript from jsDelivr and injects it directly into the active rendering page. The dependency uses the broad `@10` version selector rather than an immutable exact release, and the code does not verify a cryptographic digest or Subresource Integrity value. As a result, the effective JavaScript payload can change after the Skill has been reviewed. A compromised CDN, package publication, dependency account, or unexpected upstream release could cause different code to execute during a later conversion. The Mermaid configuration additionally selects `securityLevel: 'loose'`. This is less restrictive than `strict` mode and increases the attack surface presented by attacker-controlled diagram definitions. This setting does not itself prove arbitrary code execution through Mermaid, but it unnecessarily weakens isolation while processing untrusted input. ### Attack Path 1. An attacker supplies Markdown containing a Mermaid code block. 2. `render_mermaid_if_needed()` detects the block and enters the Mermaid rendering path. 3. The browser retrieves `https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js`. 4. The downloaded response is executed in the document page without local integrity verifi ...[truncated 1028 chars]
Remediation
View remediation
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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented purpose is a local Markdown-to-PDF conversion skill, but the implementation reportedly performs additional behaviors including loading remote Mermaid JavaScript from a CDN, modifying user input content, and starting a local HTTP service. Hidden network/script execution and content mutation materially expand the attack surface, enabling supply-chain risk, untrusted script execution during rendering, and surprising transformations of user data.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documentation describes shell execution, file reads, and file writes but declares no explicit tool scope or permission boundaries. In an agent environment, this can cause the skill to run with broader-than-expected capabilities, making it easier to invoke filesystem or command execution paths without clear user consent or policy enforcement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description and top-level skill text are entirely in Chinese and present the skill behavior as fixed, with no indication that users may choose another language or locale. This can violate language/locale policy when a skill implicitly constrains interaction to a specific language without opt-in or documented regional scope.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill documentation includes instructions to copy generated PDFs into a QQ Bot media directory and send them via a messaging tool, which is unrelated to the stated conversion purpose. This introduces an exfiltration path for generated documents and may encourage an agent to transmit potentially sensitive files to an external channel without the transmission capability being part of the declared scope.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The Pandoc command hard-codes --metadata lang=zh-CN, which sets the document language/locale regardless of user preference. This is a natural-language policy concern because the file does not expose a language selection option or justify the locale restriction as region-specific behavior.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The code reads a user-supplied CSS file and passes it to Pandoc at L626-L630, but then removes all blocks and injects only BUILTIN_CSS at L643-L650. This makes the actual behavior inconsistent with the apparent/documented capability of honoring custom styling, because the final HTML is forced to use only the built-in stylesheet.

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 · md_to_pdf_v4.py (reported line 636)May include surrounding context.

python
pathlib.Path(css_file).write_text(BUILTIN_CSS, encoding="utf-8")
        cmd.extend(["--css", "style.css"])

    result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
    if result.returncode != 0:
        print(f"Pandoc error: {result.stderr}", file=sys.stderr)
        sys.exit(1)

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The converter loads Mermaid from a public CDN at render time, creating undeclared outbound network access during what is expected to be a local file conversion workflow. This expands the trust boundary: an attacker controlling the network path, CDN content, or a malicious Mermaid diagram could influence browser-side execution, and document content can trigger those fetches automatically.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.