T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/gen.py:61
- Finding
- Stored HTML Injection in the Generated Gallery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gen.py`, lines 61-76 and 101-102 **Vulnerability Type**: Stored HTML injection caused by incomplete contextual escaping **Risk Level**: Medium ### Complete Code Snippet ```python def _write_gallery(out_dir: Path, items: list[dict]) -> Path: """生成图库预览 HTML""" html_items = "" for it in items: fname = it["file"] prompt_escaped = it["prompt"].replace("<", "<").replace(">", ">") html_items += f""" <div class="card"> <a href="{fname}" target="_blank"> <img src="{fname}" alt="{prompt_escaped}" loading="lazy"> </a> <div class="meta"> <div class="prompt">{prompt_escaped}</div> <div class="info">{it.get('model','')} · {it.get('size','')} · {it.get('index','')}</div> </div> </div>""" ``` The accumulated markup is subsequently written directly to the gallery: ```python index_path = out_dir / "index.html" index_path.write_text(html, encoding="utf-8") ``` ### Technical Analysis The application attempts to sanitize the prompt by replacing only `<` and `>`. This is insufficient for an HTML attribute context because quotation marks and ampersands are not escaped. The prompt is placed inside the double-quoted `alt` attribute of an `<img>` element, so an attacker-controlled quotation mark can terminate the attribute and introduce new attributes such as `onload`. The user-controlled `--model` value is also inserted directly into an HTML element without any escaping. If a supplied model string is accepted through the generation workflow, it can introduce arbitrary HTML elements or event handlers. This is a stored injection issue: the malicious input is persisted in `index.html` and becomes active when the generated gallery is opened in a browser. ### Attack Path 1. An attacker supplies or influences the image prompt passed to the Skill. 2. The prompt contains an attribute-break ...[truncated 1612 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Apply contextual HTML escaping to every dynamic value, including quotation marks: ```python import html fname_escaped = html.escape(it["file"], quote=True) prompt_escaped = html.escape(it["prompt"], quote=True) model_escaped = html.escape(it.get("model", ""), quote=True) size_escaped = html.escape(it.get("size", ""), quote=True) index_escaped = html.escape(it.get("index", ""), quote=True) ``` 2. Use the escaped filename in both `href` and `src`, and use the other escaped values only in their intended contexts. 3. Prefer a template engine with automatic escaping rather than constructing HTML through f-strings. 4. Validate `--model` against an explicit allowlist of supported model identifiers. 5. Add regression tests using prompts containing `"`, `'`, `&`, `<`, `>`, and event-handler payloads. 6. Consider adding a restrictive Content Security Policy to the generated page, such as one that disallows inline scripts and external network connections. ]]>
