Back to skill

Security audit

Cn Markdown To Html

Security checks for vulnerabilities and agentic risk

Overview

This is a simple local Markdown-to-HTML helper with quality and generated-HTML safety risks, but no evidence of hidden agent control, credential access, persistence, exfiltration, or destructive behavior.

Install only if you want a basic local converter and are comfortable reviewing or fixing the script first. Do not use it to process untrusted Markdown for a website or trusted browser origin unless HTML escaping, URL validation, and sanitization are added.

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

Error
Location
scripts/md_to_html.py:6
Finding
Unescaped Markdown Enables Script Injection in Generated HTML<![CDATA[ ## Vulnerability Details **File Location**: `scripts/md_to_html.py`, lines 6-23 **Vulnerability Type**: Generated-document HTML and JavaScript injection **Risk Level**: High ### Vulnerable Code ```python def md_to_html(md): html = md # Headers html = re.sub(r"^### (.+)$", r"<h3>\1</h3>", html, flags=re.MULTILINE) html = re.sub(r"^## (.+)$", r"<h2>\1</h2>", html, flags=re.MULTILINE) html = re.sub(r"^# (.+)$", r"<h1>\1</h1>", html, flags=re.MULTILINE) # Bold/italic html = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", html) html = re.sub(r"\*(.+?)\*", r"<em>\1</em>", html) # Code html = re.sub(r"`(.+?)`", r"<code>\1</code>", html) # Links html = re.sub(r"\[(.+?)\]\((.+?)\)", r'<a href="\2">\1</a>', html) # Paragraphs paragraphs = [p.strip() for p in html.split(" ") if p.strip()] html = "\n".join(f"<p>{p}</p>" if not p.startswith("<") else p for p in paragraphs) return f"""<!DOCTYPE html> <html><head><meta charset="utf-8"><title>Converted</title></head> <body>{html}</body></html>""" ``` ### Technical Analysis The converter initially assigns the complete, untrusted Markdown input to `html` and then performs regular-expression substitutions without first HTML-encoding input text. Any HTML elements already present in the Markdown therefore remain active in the generated document. The paragraph construction further preserves content beginning with `<` instead of wrapping or sanitizing it: ```python html = "\n".join(f"<p>{p}</p>" if not p.startswith("<") else p for p in paragraphs) ``` Consequently, payloads such as the following can become executable browser content: ```html <script>alert(document.domain)</script> ``` ```html <img src=x onerror=alert(document.domain)> ``` The link conversion also inserts the attacker-controlled destination directly into an `href` attribute without attribute escaping or URL-scheme validation: ```python html = re.sub(r"\[(.+?)\]\((.+?)\)", r'<a href= ...[truncated 2427 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Escape untrusted text by default** - Use `html.escape(value, quote=True)` for text and attribute values. - Do not treat input beginning with `<` as trusted HTML. 2. **Use a structured Markdown parser** - Replace regular-expression-based conversion with a maintained parser that supports disabling raw HTML. - Configure the parser to escape or remove embedded HTML by default. 3. **Sanitize HTML when raw HTML is required** - Make raw HTML support an explicit opt-in feature. - Process generated HTML through an allowlist-based sanitizer. - Exclude scripts, event-handler attributes, dangerous embedded content, and other active elements. 4. **Validate link destinations** - Parse URLs before emitting them. - Permit only explicitly required schemes, such as `https`, `http`, and optionally `mailto`. - Reject or neutralize `javascript:`, dangerous `data:` content, and other executable schemes. - HTML-encode the validated URL before placing it in `href`. 5. **Correct the syntax error safely** - Replace the literal multiline string in the `split` call with: ```python paragraphs = [p.strip() for p in html.split("\n\n") if p.strip()] ``` - Apply the output-encoding and sanitization corrections at the same time so restoring execution does not expose the injection flaw. 6. **Add security regression tests** - Test raw `<script>` elements. - Test event attributes such as `onerror`. - Test attribute-breaking quotation marks. - Test `javascript:` and dangerous `data:` URLs. - Verify that ordinary Markdown still produces correctly encoded HTML. ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The overall purpose mostly matches: this is a standard-library Python Markdown-to-HTML converter. However, there are two notable discrepancies. First, the description claims support for 'code blocks,' but the implementation only converts inline backtick code (`...`) and has no handling for fenced or block code sections. Second, the code requires reading a file via the --file argument, which is a resource access capability not reflected in the declared permissions section. These are material enough to count as a mismatch, though the primary purpose remains aligned.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The file is primarily written in English, but the closing promotional section switches to Chinese-only text. This creates a language/locale policy concern because the skill presents content in a specific language without user opt-in or any explanation that the skill is intended for a Chinese-speaking audience.

Static analysis

No suspicious patterns detected.