Back to skill

Security audit

往事漫画 · 故事连环画生成器

Security checks for vulnerabilities and agentic risk

Overview

This comic-making skill is mostly coherent, but its bundled compression script can delete unrelated files if pointed at an existing destination folder.

Review before installing. Use the compression script only with a newly created, dedicated output folder such as comic_publish, avoid pointing it at a project root or documents folder, and escape or sanitize story text before publishing generated HTML from untrusted input. Expect the default workflow and template to be Chinese-language oriented and to load Google Fonts unless modified.

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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
templates/compress.py:120
Finding
Destructive Cleanup of an Unvalidated Existing Destination Directory<![CDATA[ ## Vulnerability Details **File Location**: `templates/compress.py`, lines 120-135 **Vulnerability Type**: Unsafe recursive file deletion **Risk Level**: Medium ### Vulnerable Code ```python # ---- 清理未引用文件(v1.5 强制)---- if not keep_unreferenced and dst_html_paths: referenced = _collect_referenced(src_dir, dst_html_paths) removed = 0 for root, dirs, files in os.walk(dst_dir): for f in files: if f.lower().endswith('.html'): continue rel = os.path.relpath(os.path.join(root, f), dst_dir) rel_norm = rel.replace('\\', '/') # 被引用(用不带扩展名的键匹配,因引用可能来自改写前的 .png) base = os.path.splitext(rel_norm)[0] if rel_norm in referenced or (base + '.jpg') in referenced: continue try: os.remove(os.path.join(root, f)) ``` ### Technical Analysis The destination directory is supplied as a command-line argument and initialized using `os.makedirs(dst_dir, exist_ok=True)`. The script does not require the destination to be new or empty and does not verify that it is a dedicated publication directory. During default operation, the cleanup block recursively traverses the entire destination and deletes every non-HTML file that does not appear in the generated HTML references. The deletion criteria do not distinguish files generated during the current run from files that already existed in the destination. Although the script does not provide remote code execution or privilege escalation, it performs destructive operations over a user-selected path without sufficient safety checks. ### Attack Path 1. A user, automation system, or calling Agent supplies an existing directory as the `dst` argument. 2. The script accepts the directory because `exist_ok=True` permits pre-existing destinations. 3. At least one HTML file is copied, causing `dst_html_paths` to become non-empty. 4. Default cleanup recursively walks the entire destination. ...[truncated 637 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require the destination to be absent or empty by default. 2. Refuse to proceed when unrelated files are present unless the user supplies an explicit option such as `--force-clean`. 3. Resolve source and destination with `os.path.realpath()` and reject dangerous destinations, including: - The filesystem root. - The user's home directory. - The source directory itself. - Any parent directory of the source. 4. Track files created during the current execution and delete only those tracked files. 5. Generate output in a new temporary directory and atomically rename it to the final publication directory after successful completion. 6. Display the resolved destination and planned deletion count before destructive cleanup. 7. Do not silently suppress deletion failures; report affected paths and return a nonzero status if cleanup is incomplete. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
templates/comic_template.html:284
Finding
Generated Comic Content Is Inserted into HTML Without an Escaping Requirement<![CDATA[ ## Vulnerability Details **File Location**: `templates/comic_template.html`, lines 284-320 **Vulnerability Type**: HTML injection and potential script execution **Risk Level**: Medium ### Vulnerable Code ```html <div class="episode"> <div class="episode-title"> <div class="ep-num">第X集</div> <h2>[集标题]</h2> </div> <div class="grid-2x2"> <!-- 格1:有旁白 --> <div class="panel-cell"> <div class="panel"> <img src="panels/e0X_p01.jpg" alt="[画面描述]"> </div> <div class="narration-bar">[旁白文字]</div> </div> <!-- 格2:有对白 --> <div class="panel-cell"> <div class="panel"> <img src="panels/e0X_p02.jpg" alt="[画面描述]"> <div class="bubble speech pos-tr">[对白文字]</div> </div> </div> <!-- 格3:有心理活动 --> <div class="panel-cell"> <div class="panel"> <img src="panels/e0X_p03.jpg" alt="[画面描述]"> <div class="bubble thought pos-bl">[心理活动文字]</div> </div> </div> <!-- 格4:有旁白+对白 --> <div class="panel-cell"> <div class="panel"> <img src="panels/e0X_p04.jpg" alt="[画面描述]"> <div class="bubble speech pos-tl">[对白文字]</div> </div> <div class="narration-bar">[旁白文字]</div> </div> </div> </div> <!-- 结尾 --> <div class="book-ending"> <p>[结尾文字]</p> <div class="end-sig">—— 全 书 完 ——</div> </div> ``` ### Technical Analysis The workflow places story-derived titles, dialogue, narration, internal thoughts, endings, and image descriptions into raw HTML placeholders. Neither the template nor the Skill instructions require HTML escaping or sanitization before substitution. Text-node placeholders can terminate their surrounding element and introduce arbitrary markup. The `alt` attribute is also an attribute context, so unescaped quotation marks can terminate the attribute and add event handlers or other attributes. For example, attacker-controlled dialogue containing a closing tag followed by an executable element could become activ ...[truncated 1328 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. HTML-escape every story-derived value according to its output context. 2. Escape text-node content at minimum by encoding `&`, `<`, and `>`. 3. Escape attribute values by additionally encoding quotation marks and apostrophes. 4. Use a template engine with automatic escaping instead of direct placeholder replacement or string concatenation. 5. Do not permit user-controlled values to determine tag names, attribute names, or raw HTML fragments. 6. If limited formatting is required, sanitize it with a strict allowlist that excludes scripts, event-handler attributes, embedded frames, and unsafe URLs. 7. Add a restrictive Content Security Policy, for example one that disallows inline scripts and limits image, style, and font sources to approved locations. 8. Add tests using payloads in titles, dialogue, narration, endings, and `alt` text to verify that they render literally rather than becoming markup. ]]>

other

Note
Location
templates/comic_template.html:8
Finding
Comic Template Makes an Undeclared Runtime Request to Google Fonts<![CDATA[ ## Vulnerability Details **File Location**: `templates/comic_template.html`, line 8 **Vulnerability Type**: External resource privacy exposure and runtime dependency **Risk Level**: Low ### Vulnerable Code ```css @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Ma+Shan+Zheng&family=ZCOOL+KuaiLe&display=swap'); ``` ### Technical Analysis The HTML template imports a stylesheet from Google Fonts whenever the generated comic is opened with network access. This behavior conflicts with the expected portability of a locally delivered HTML comic and introduces a third-party runtime dependency. The browser request exposes network metadata such as the reader's IP address, user-agent information, request timing, and referrer behavior permitted by the browser. The returned stylesheet may also direct the browser to fetch font files from additional Google-controlled hosts. No malicious remote payload was identified, and the imported resource is a stylesheet rather than executable application code. Therefore, this is a privacy and dependency concern rather than remote payload execution. ### Attack Path 1. A comic is generated from the supplied template without removing the external import. 2. The recipient opens the HTML while connected to the Internet. 3. The browser requests the stylesheet from `fonts.googleapis.com`. 4. Referenced font resources may subsequently be retrieved from Google font infrastructure. 5. Third-party servers receive the reader's connection metadata, and rendering becomes dependent on those services being available. ### Impact Assessment The issue causes limited privacy disclosure to a third party and prevents fully offline, deterministic rendering. It does not grant local privileges or directly expose story files. The security scope is limited to browser network metadata and reliance on an external content provider. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the remote `@import` and use the existing system-font fallback stack. 2. If custom fonts are necessary, package properly licensed font files with the comic and reference them through local `@font-face` declarations. 3. Include only required font weights and subsets to control package size. 4. If remote fonts remain optional, disclose the external request and obtain the user's consent. 5. Apply a Content Security Policy that limits `style-src` and `font-src` to local resources or an explicit allowlist. 6. Test generated comics with network access disabled to confirm consistent offline behavior. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The manifest description is written as producing comics from text content and explicitly targets Chinese trigger phrases such as “把我的故事做成漫画” and “生成连环画,” while the workflow later hard-codes Chinese text artifacts like `剧本.md` and Chinese dialogue limits. This indicates a language-specific constraint without user opt-in or a documented regional/compliance justification, which matches the language/locale policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The workflow requires output as `剧本.md` with Chinese-labeled fields such as 对白, 心理活动, and 旁白, and later sections continue to prescribe Chinese text handling rules. Because the file does not present this as an optional locale mode or ask the user to choose language, it imposes a specific language convention by default.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrases are broad consumer-style requests such as '把我的故事做成漫画' and '故事转漫画', which are likely to match ordinary user intents outside a narrowly scoped invocation. This can cause the skill to activate unexpectedly and gain access to powerful tools like ImageGen and Bash in contexts where the user did not clearly intend to invoke this workflow, increasing the risk of overreach or unintended tool use.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The HTML root element hard-codes `lang="zh-CN"`, which enforces a specific language/locale in the template. There is no accompanying note that this is optional, user-selected, or justified as a region-specific template, so it appears to violate the locale-choice policy.

Static analysis

No suspicious patterns detected.