Back to skill

Security audit

Generate responsive HTML pages suitable for reporting, supporting resizing and screenshot capture.

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it renders raw report content into local HTML without escaping it before opening it in a browser.

Install only if you will generate reports from trusted text or are prepared to sanitize inputs first. Do not feed it untrusted web content, user submissions, or copied HTML/Markdown until the generator escapes report fields or renders in a sandboxed browser with scripts and network access disabled.

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/generate.py:267
Finding
Unescaped Report Data Enables Stored HTML and JavaScript Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py`, lines 267–312 **Vulnerability Type**: Stored HTML injection / cross-site scripting **Risk Level**: High **Category**: T09: Insecure Skill Coding Practices ### Vulnerable Code ```python def generate_html(data, output_path): """Generates HTML file from data.""" # Helper to format list items def format_list(items): return "\n".join([f"<li>{item}</li>" for item in items]) # Prepare context for template context = { "title": data.get("title", "述职报告"), "goal": data.get("goal", ""), "q1_title": data.get("q1", {}).get("title", ""), "q1_subtitle": data.get("q1", {}).get("subtitle", ""), "q1_slogan": data.get("q1", {}).get("slogan", ""), "q1_items": format_list(data.get("q1", {}).get("items", [])), "q2_title": data.get("q2", {}).get("title", ""), "q2_subtitle": data.get("q2", {}).get("subtitle", ""), "q2_slogan": data.get("q2", {}).get("slogan", ""), "q2_items": format_list(data.get("q2", {}).get("items", [])), "q3_title": data.get("q3", {}).get("title", ""), "q3_subtitle": data.get("q3", {}).get("subtitle", ""), "q3_slogan": data.get("q3", {}).get("slogan", ""), "q3_items": format_list(data.get("q3", {}).get("items", [])), "q4_title": data.get("q4", {}).get("title", ""), "q4_subtitle": data.get("q4", {}).get("subtitle", ""), "q4_slogan": data.get("q4", {}).get("slogan", ""), "q4_items": format_list(data.get("q4", {}).get("items", [])), "summary_1": data.get("summary", ["", "", "", ""])[0], "summary_2": data.get("summary", ["", "", "", ""])[1], "summary_3": data.get("summary", ["", "", "", ""])[2], "summary_4": data.get("summary", ["", "", "", ""])[3], } html_content = HTML_TEMPLATE.format(**context) ``` ### Technical Analysis E ...[truncated 3203 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply contextual HTML escaping to every untrusted scalar and list item before interpolation: ```python from html import escape def escape_text(value): if not isinstance(value, str): raise TypeError("Report values must be strings") return escape(value, quote=True) def format_list(items): if not isinstance(items, list): raise TypeError("Report items must be a list") return "\n".join(f"<li>{escape_text(item)}</li>" for item in items) ``` 2. Escape all scalar fields, including the document title, goal, quadrant titles, subtitles, slogans, and summary values: ```python context = { "title": escape_text(data.get("title", "Default Report")), "goal": escape_text(data.get("goal", "")), # Apply escape_text to every remaining scalar field. } ``` 3. Prefer a template engine with automatic HTML escaping, such as Jinja2 configured with `select_autoescape`, rather than using unrestricted `str.format` interpolation. 4. Validate the complete JSON schema before rendering: - Require the top-level value to be an object. - Require `q1` through `q4` to be objects. - Require text fields to be strings. - Require each `items` value and `summary` to be arrays of strings. - Require exactly four summary entries or handle missing entries safely. - Enforce reasonable length limits to reduce rendering and resource-exhaustion risks. 5. Add a restrictive Content Security Policy to the generated document as defense in depth: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; font-src 'none'; connect-src 'none'; script-src 'none'; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'"> ``` 6. Render untrusted reports in a sandboxed, isolated browser context with network access disabled. Do not rely on browser isolation as a substitute for output encoding. 7. Add regression tests containing paylo ...[truncated 141 chars]
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill instructs the agent to execute a Python script and write output files, but it declares no explicit tool restrictions such as allowed-tools or permissions. This creates unnecessary capability exposure: an agent may invoke shell and file-write behaviors without a clearly scoped contract, increasing the chance of unintended command execution or filesystem access when the skill is auto-invoked.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The description uses broad triggers like 'create a report, slide, or summary card from raw content,' which can match many ordinary user requests and cause over-invocation. When combined with shell execution and file creation, this broad routing expands the attack surface by enabling the skill in contexts where the user did not clearly consent to running code or generating local artifacts.

Natural-Language Policy Violations

Medium
Confidence
81% confidence
Finding
The field example for `title` uses Chinese text and the overall data examples are entirely in Chinese, while the skill does not state that language is selectable or that the locale is intentionally region-specific. This can create an implicit language/locale constraint without user opt-in.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The template sets `lang="zh-CN"` and the generated report labels are written in Chinese throughout the embedded HTML. For a general-purpose report generator, this forces a specific language/locale without user opt-in, which matches the language/locale policy violation criteria.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The skill instructs the agent to generate HTML locally and open it via a file:// URL in a browser, but it provides no user-facing warning or consent step about creating files or accessing local content. Even if the HTML is intended output, local file rendering can expose sensitive local-path context and normalizes opening generated files without transparency to the user.

Static analysis

No suspicious patterns detected.