Back to skill

Security audit

markdown-to-html

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Markdown-to-HTML converter, with ordinary risks if untrusted Markdown is converted or the generated HTML is published.

Install this if you need local Markdown-to-HTML export and are comfortable with a Pandoc-based converter. Do not convert untrusted Markdown into HTML that will be hosted on a trusted site unless you sanitize or disable raw HTML first, and be careful with --embed-assets and broad resource paths because referenced local assets may be included in the output.

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/markdown_to_html.py:184
Finding

Unsanitized Raw HTML Allows Script Execution in Generated Documents

Content
View full analysis

Vulnerability Details

File Location: scripts/markdown_to_html.py:184-185; rendering sinks in assets/templates/docs-slate.html:31, assets/templates/magazine-amber.html:31, assets/templates/product-midnight.html:33, and assets/templates/serif-paper.html:30
Vulnerability Type: Stored cross-site scripting through untrusted Markdown conversion
Risk Level: Medium

Vulnerable Code

scripts/markdown_to_html.py:184-185:

python
"--from",
"markdown+yaml_metadata_block+emoji+footnotes+pipe_tables+grid_tables+task_lists+fenced_divs+fenced_code_attributes+raw_attribute+tex_math_dollars",

Each bundled template inserts the generated HTML body without a sanitization layer:

html
$body$

Technical Analysis

The converter invokes Pandoc's Markdown reader without disabling raw HTML. The enabled raw_attribute extension also permits attributes associated with raw output constructs. Attacker-controlled Markdown can therefore contain active HTML, including script elements, event-handler attributes, dangerous links, or embedded frames.

Pandoc converts the supplied document and the bundled templates insert the resulting content directly through $body$. No allowlist-based HTML sanitizer, Content Security Policy, or trusted-input restriction is applied before the final HTML file is written.

A malicious Markdown input could contain content such as:

markdown
# Shared Document

<script>
// Attacker-controlled browser-side behavior
alert(document.domain);
</script>

When the generated document is opened, the browser may execute the embedded script. Exploitability and accessible data depend on how the output is opened or hosted. Hosting the document under a trusted application's origin creates substantially greater risk than opening it as an isolated local file.

Attack Path

  1. An attacker creates or modifies a Markdown document containing active HTML or event-handler attr ...[truncated 1311 chars]
Remediation
View remediation

Remediation Suggestions

  1. Disable raw HTML in the Pandoc reader configuration by removing the relevant capability explicitly:

    python
    "--from",
    "markdown-raw_html+yaml_metadata_block+emoji+footnotes+pipe_tables+grid_tables+task_lists+fenced_divs+fenced_code_attributes-raw_attribute+tex_math_dollars",
    
  2. Apply a mature allowlist-based HTML sanitizer after conversion and before writing or publishing the final document. Allow only required tags and attributes, and reject:

    • script, iframe, object, and embed elements.
    • Attributes beginning with on, such as onclick and onerror.
    • Dangerous URI schemes such as javascript:.
    • Unnecessary inline styles and active embedded content.
  3. If raw HTML is a required feature, disable it by default and expose it only through an explicitly named option such as --allow-unsafe-html. Document that this option must only be used with trusted input.

  4. Add a restrictive Content Security Policy to bundled templates as defense in depth. For example, disallow inline scripts and restrict resource origins. CSP should supplement sanitization rather than replace it.

  5. When generated documents are hosted, serve untrusted output from an isolated origin without access to application cookies or sensitive browser storage.

  6. Add regression tests using raw script tags, event-handler attributes, javascript: links, SVG-based payloads, and embedded frames. Verify that active content is removed or rendered inert.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (10)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill instructs use of a shell-executed Python script and external binary (pandoc) but does not declare any tool scope such as permissions or allowed-tools. That creates an authorization gap where an agent may invoke shell capabilities more broadly than intended, especially because the skill also accepts user-controlled file paths and arguments for templates and CSS.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The template sets <html lang="zh-CN"> in the fallback branch whenever lang is absent. This imposes a specific locale by default rather than offering a user choice or using a neutral default, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The skill's stated purpose is document conversion, but the implementation depends on locating and executing an external binary via PATH and later runs it with subprocess.run. Spawning subprocesses is a materially broader capability than simple in-process formatting and is not disclosed in the manifest description.

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/markdown_to_html.py (reported line 256)May include surrounding context.

python
command = build_command(pandoc, source_path, output_path, template_path, css_paths, args)
        if args.print_command:
            print(" ".join(command), file=sys.stderr)
        completed = subprocess.run(command, check=False)
        if completed.returncode != 0:
            return completed.returncode
        print(

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The body font stack explicitly prioritizes "PingFang SC" and "Noto Sans CJK SC", and the heading stack later prioritizes "Songti SC", which reflects a locale-specific language preference embedded in the file. Under the policy rule, forcing a specific language/locale presentation without user choice or justification can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
74% confidence
Finding

The heading font-family includes "Songti SC", a Simplified Chinese serif font, as part of the preferred rendering order. This introduces a locale-specific preference in presentation without any visible opt-in or justification in the file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

If no language is supplied, the template hard-codes lang="zh-CN", which imposes a specific locale by default. This is a natural-language policy concern because the file does not offer a user choice or explain why Chinese is required.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

This HTML template sets lang="zh-CN" as the fallback whenever the lang variable is absent. That imposes a specific language/locale choice by default rather than offering neutrality or user selection, which matches the policy concern for forced locale behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This HTML template sets lang="zh-CN" as the fallback whenever $lang$ is absent. That creates a default locale policy in the output without offering user choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The inline comment at L240 is written only in Chinese, which introduces a language-specific instruction in the skill file without offering any language choice or documenting a justified locale constraint. This can violate a language/locale policy requiring user opt-in or consistent language usage.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.