T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate_worksheet.py:84
- Finding
- Stored HTML and JavaScript Injection Through the Worksheet Name Field<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_worksheet.py:84` **Vulnerability Type**: Stored HTML injection / local cross-site scripting **Risk Level**: Medium ### Vulnerable Code ```python name_html = f'<span class="fill">{name}</span>' if name else '<span class="fill"> </span>' ``` The value originates from the user-controlled `--name` command-line argument and is passed to `render_html()` before the resulting document is written to disk: ```python html = render_html(args.name, level, activities, args.columns, not args.no_answers, lang, args.score) ``` ### Technical Analysis The `name` value is interpolated directly into an HTML document without contextual HTML escaping. An attacker can therefore terminate the intended `<span>` content and insert arbitrary HTML, including elements with JavaScript event handlers. For example, the following value is interpreted as markup rather than displayed as a literal name: ```html <img src=x onerror="alert(document.domain)"> ``` This behavior contradicts the security statement in `SKILL.md` claiming that user input is escaped before being added to HTML. The generated document is stored locally, making this a stored injection vulnerability. The payload runs when a user opens the generated worksheet in a browser. Browser restrictions for `file://` documents limit some access, but they do not prevent arbitrary manipulation of the worksheet DOM or all outbound requests initiated by injected elements or scripts. ### Attack Path 1. An attacker persuades a user or Agent to generate a worksheet with a crafted child name. 2. The crafted value is supplied through `--name`, for example: ```text <img src=x onerror="document.body.innerHTML='Modified worksheet'"> ``` 3. `render_html()` inserts the value directly into the worksheet header. 4. The generator writes the malicious markup into the output HTML file. 5. The user opens the worksheet ...[truncated 1029 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape the name before inserting it into HTML: ```python import html safe_name = html.escape(name, quote=True) name_html = ( f'<span class="fill">{safe_name}</span>' if name else '<span class="fill"> </span>' ) ``` 2. Treat every value originating from command-line arguments, review JSON files, or future custom content as untrusted. Escape text according to its output context before including it in HTML. 3. Keep trusted generator markup separate from plain-text data. Do not permit plugins to convert user-controlled strings into trusted HTML without sanitization. 4. Add regression tests covering names containing: ```text < > & " ' </span> <script>alert(1)</script> <img src=x onerror=alert(1)> ``` Tests should verify that these values appear as literal text and do not create executable elements. 5. Add a restrictive Content Security Policy as defense in depth. Because the current print button uses an inline `onclick` handler, first move that behavior into a permitted external or nonce-protected script. The policy should disable object embedding and unauthorized network destinations. 6. Update the documented HTML-escaping security claim only after escaping is implemented and tested. ]]>
