Back to skill

Security audit

Xhs Md2img

Security checks for vulnerabilities and agentic risk

Overview

The skill’s purpose is coherent, but it needs review because it may render user Markdown as active browser HTML without documented sanitization or network blocking.

Install only if you are comfortable with external LLM/image-generation services and optional OSS upload. For shared or untrusted Markdown, require a renderer that sanitizes HTML, blocks scripts and unexpected network requests, uses an isolated browser context, and scopes API/OSS credentials narrowly. Pin dependencies before production use.

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
SKILL.md:97
Finding
Unsanitized Markdown HTML May Execute Active Content in the Browser Renderer<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:97-100`, `templates/card-template.html:261-264`, and `references/browser-screenshot-spec.md:12-19` **Vulnerability Type**: Active-content injection through unsanitized Markdown HTML **Risk Level**: Medium ### Vulnerable Code and Configuration `SKILL.md:97-100` enables Markdown processing extensions that include raw HTML support: ```markdown ### 2. Markdown to HTML Use `python-markdown` with extensions: - `tables`, `fenced_code`, `codehilite`, `nl2br`, `sane_lists`, `smarty`, `attr_list`, `md_in_html`, `toc` ``` `templates/card-template.html:261-264` inserts the generated card markup into an executable HTML document: ```html <div class="render-root"> <!-- Rendered card nodes injected by renderer --> {{CARDS_HTML}} </div> ``` `references/browser-screenshot-spec.md:12-19` requires that the resulting document be loaded in a headless browser: ```markdown ## 2) Browser Context - Launch browser in headless mode. - Create context with: - `viewport = { width: viewport_width, height: viewport_height }` - `deviceScaleFactor = export_scale` - Keep default zoom (100%). - Do not use print/PDF rendering mode. ``` No corresponding requirement for HTML sanitization, JavaScript disabling, Content Security Policy enforcement, or browser request filtering is specified. ### Technical Analysis The skill instructs implementations to process attacker-controllable Markdown using the `md_in_html` extension and then inject rendered card HTML into a page opened by a headless browser. Raw HTML support can preserve active elements and attributes supplied through Markdown, depending on how the renderer constructs `CARDS_HTML`. Without a strict sanitization step, hostile input could contain constructs such as: - `<script>` elements. - Event-handler attributes such as `onerror` or `onload`. - `<iframe>`, `<object>`, or other embedded browsing contexts. - Remote images and other resource-loading elements. - Da ...[truncated 2324 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Sanitize all generated HTML** - Process Markdown output through a mature HTML sanitizer with a strict allowlist. - Permit only formatting elements required by the card renderer. - Remove `<script>`, `<iframe>`, `<object>`, `<embed>`, `<form>`, `<meta>`, `<link>`, and unneeded SVG/MathML content. - Remove all event-handler attributes, including attributes beginning with `on`. - Reject `javascript:`, `vbscript:`, and unsafe `data:` URLs. 2. **Disable raw HTML unless explicitly required** - Remove `md_in_html`. - Escape embedded HTML in Markdown by default. - If limited inline HTML is necessary, allow only explicitly documented, sanitized elements. 3. **Restrict browser network access** - Use Playwright request interception to deny all outbound requests by default. - Allow only resources that are necessary for rendering. - Block loopback, private, carrier-grade NAT, link-local, multicast, and metadata-service address ranges after DNS resolution. - Revalidate redirects and resolved addresses to prevent DNS rebinding and redirect-based bypasses. 4. **Harden the browser document** - Disable JavaScript if pagination and capture can operate without it; replace the font-readiness script with an implementation-side wait where feasible. - Otherwise, enforce a restrictive Content Security Policy such as `default-src 'none'`, with narrowly scoped exceptions for required local images and styles. - Use an isolated browser context without cookies, authentication state, extensions, or access to unrelated origins. - Keep the browser sandbox enabled and avoid unsafe launch flags such as `--no-sandbox`. 5. **Constrain resource consumption** - Add a maximum length for `markdown`, `title`, `author`, and `description`. - Enforce navigation, rendering, resource-download, and total-job timeouts. - Limit response sizes, image dimensions, DOM node counts, and concurrent rendering jobs. - ...[truncated 440 chars]
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (22)

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Vague Triggers

Medium
Confidence
85% confidence
Finding
This markdown skill describes capabilities and purpose, but does not specify explicit trigger phrases, invocation boundaries, or exclusion conditions. Without clear trigger scope, an agent may match broadly on generic requests to convert Markdown or create social-media images, which fits the vague-trigger category for markdown files.

Natural-Language Policy Violations

Medium
Confidence
73% confidence
Finding
The description and examples center the skill on Xiaohongshu/XHS output and include Chinese-language example content, but the documentation does not state that this locale/style is optional or user-selected. This can amount to a language/locale policy issue because the skill appears to force a specific regional format without explicit opt-in.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The dependency set includes outbound networking and cloud-upload libraries such as httpx, openai, and oss2 in a skill whose stated purpose is local Markdown-to-image conversion. While AI-generated decorative backgrounds may justify some remote API use, cloud upload capability is not clearly necessary from the description and expands the attack surface for data exfiltration, credential misuse, and unexpected external communication.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The HTML root sets `lang="zh-CN"`, which hard-codes a specific language/locale for all rendered output. Under the policy, forcing a locale without user opt-in is a natural-language policy concern unless it is clearly documented as region-specific or optional.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This markdown file contains user-facing example content and labels predominantly in Chinese, but does not indicate that the skill is region-specific or that users may choose another language. Under the natural-language policy rules, forcing a specific language without opt-in can be a policy concern.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastapi>=0.100.0
uvicorn>=0.23.0
httpx>=0.24.0
markdown>=3.4
Confidence
93% confidence
Finding
Using a lower-bound version specifier for fastapi allows installation of arbitrary newer releases, making builds non-reproducible and weakening assurance that known-safe versions are used. This can silently introduce vulnerable or incompatible versions through dependency resolution changes over time.

Unverifiable Dependency: fastapi has 3 known advisory(ies) (CVE-2021-32677 (Cross-Site Request Forgery (CSRF) in FastAPI); CVE-2021-32677 (FastAPI is a web framework for building APIs with Python 3.6+ based on standard ); CVE-2024-24762 (FastAPI is a web framework for building APIs with Python 3.8+ based on standard )), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding
FastAPI has known advisories, and because the manifest does not pin a version, there is no reliable way to determine whether the installed package is patched. The risk here is uncertainty: deployments may resolve to affected versions, especially across environments or over time.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastapi>=0.100.0
uvicorn>=0.23.0
httpx>=0.24.0
markdown>=3.4
Pygments>=2.15
Confidence
93% confidence
Finding
Using an unpinned uvicorn dependency permits uncontrolled upgrades and non-reproducible installs. That increases supply-chain risk because future installs may pull releases with newly introduced vulnerabilities or behavior changes.

Unverifiable Dependency: uvicorn has 4 known advisory(ies) (CVE-2020-7694 (Log injection in uvicorn); CVE-2020-7695 (HTTP response splitting in uvicorn); CVE-2020-7694 (This affects all versions of package uvicorn. The request logger provided by the) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding
Uvicorn has multiple historical advisories, but the version is not pinned, so the actual exposure cannot be verified. In a server component, that uncertainty matters because logging and HTTP parsing flaws can become externally reachable when the service is exposed.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastapi>=0.100.0
uvicorn>=0.23.0
httpx>=0.24.0
markdown>=3.4
Pygments>=2.15
python-dotenv>=1.0.0
Confidence
94% confidence
Finding
The httpx dependency is unpinned, so builds may resolve to different versions over time. For a package with network capabilities, this increases risk of silently inheriting security issues or unsafe defaults through dependency drift.

Unverifiable Dependency: httpx has 2 known advisory(ies) (CVE-2021-41945 (Improper Input Validation in httpx); CVE-2021-41945 (Encode OSS httpx <=1.0.0.beta0 is affected by improper input validation in `http)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding
The httpx dependency has known advisories and is not pinned, making it impossible to confirm whether installations are patched. Because it is a network client, vulnerable versions can affect outbound request safety and handling of attacker-influenced inputs.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fastapi>=0.100.0
uvicorn>=0.23.0
httpx>=0.24.0
markdown>=3.4
Pygments>=2.15
python-dotenv>=1.0.0
openai>=1.0.0
Confidence
92% confidence
Finding
An unpinned markdown dependency makes the build non-deterministic and can expose the skill to future parser vulnerabilities or breaking behavior without code changes. Parser libraries are especially sensitive because they process attacker-controlled content.

Unverifiable Dependency: markdown has 2 known advisory(ies) (CVE-2025-69534 (Python-Markdown has an Uncaught Exception); CVE-2025-69534 (Python-Markdown version 3.8 contain a vulnerability where malformed HTML-like se)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
81% confidence
Finding
Python-Markdown has advisories, and without an exact version the deployment may resolve to an affected release. This is relevant in a Markdown-processing skill because parser flaws can be directly triggered by untrusted input content.

Unpinned Dependencies

Low
Category
Supply Chain
Content
uvicorn>=0.23.0
httpx>=0.24.0
markdown>=3.4
Pygments>=2.15
python-dotenv>=1.0.0
openai>=1.0.0
playwright>=1.40.0
Confidence
93% confidence
Finding
Pygments is unpinned, which permits dependency drift and makes it unclear which security posture is actually deployed. Since syntax highlighters often parse complex input, unreviewed version changes can introduce denial-of-service or parsing issues.

Unverifiable Dependency: Pygments has 10 known advisory(ies) (CVE-2026-4539 (Pygments has Regular Expression Denial of Service (ReDoS) due to Inefficient Reg); CVE-2021-20270 (Infinite Loop in Pygments); CVE-2015-8557 (Command Injection in Pygments) +7 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding
Pygments has a sizable advisory history, but the manifest does not establish which version is used. Since the skill likely parses user-provided code blocks or formatting, unresolved version exposure can translate into denial-of-service or parser-level flaws.

Unpinned Dependencies

Low
Category
Supply Chain
Content
httpx>=0.24.0
markdown>=3.4
Pygments>=2.15
python-dotenv>=1.0.0
openai>=1.0.0
playwright>=1.40.0
oss2>=2.18.0
Confidence
90% confidence
Finding
The python-dotenv package is specified with only a minimum version, so future installations may pull unexpected releases. Because dotenv libraries interact with environment and local configuration, version drift can affect secret handling and file safety assumptions.

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
80% confidence
Finding
python-dotenv has advisories related to file handling, and the unpinned requirement prevents verification that a safe version is installed. Although this may be ancillary to the core skill, environment-loading code often executes in privileged contexts and can amplify local file risks.

Unpinned Dependencies

Low
Category
Supply Chain
Content
markdown>=3.4
Pygments>=2.15
python-dotenv>=1.0.0
openai>=1.0.0
playwright>=1.40.0
oss2>=2.18.0
Confidence
94% confidence
Finding
The openai dependency is unpinned, allowing uncontrolled upgrades in a component that may send content to external services. This creates both supply-chain risk and operational risk if API behavior or security properties change unexpectedly.

Unpinned Dependencies

Low
Category
Supply Chain
Content
Pygments>=2.15
python-dotenv>=1.0.0
openai>=1.0.0
playwright>=1.40.0
oss2>=2.18.0
Confidence
93% confidence
Finding
An unpinned playwright dependency can change browser automation behavior and security characteristics between installs. Since browser engines process rendered content, deterministic and reviewed versions are important for reducing exposure to runtime or sandbox-related issues.

Unpinned Dependencies

Low
Category
Supply Chain
Content
python-dotenv>=1.0.0
openai>=1.0.0
playwright>=1.40.0
oss2>=2.18.0
Confidence
94% confidence
Finding
The oss2 package is unpinned, which is risky because it provides cloud storage access and may handle credentials and uploads. Uncontrolled version selection can introduce security regressions or unsafe changes affecting remote data movement.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
This manifest-style JSON file contains user-facing natural-language descriptions exclusively in Chinese across the schema fields. Because the file does not indicate that the skill is region-specific or provide an opt-in language choice, it may violate a language/locale policy requiring neutral or user-selectable language.

Static analysis

No suspicious patterns detected.