T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate_board.py:153
- Finding
- Stored HTML Injection in Generated Project Boards<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_board.py`, lines 153–221 **Vulnerability Type**: Stored HTML injection / cross-site scripting **Risk Level**: Medium ### Vulnerable Code ```python html = f'''<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>{project['name']} - 项目看板</title> ... <body> <div class="header"> <h1>📊 {project['name']}</h1> ... <div class="task"> <div class="task-title">{t['name']}</div> <div class="task-meta">负责人: {t.get('assignee', '未分配')} | 截止: {t.get('endDate', '未设置')}</div> </div> ... <div class="task"> <div class="task-title">{t['name']}</div> <div class="task-meta">负责人: {t.get('assignee', '未分配')}</div> <div class="task-progress"> <div class="task-progress-bar" style="width: {t.get('progress', 0)}%"></div> </div> ... <div class="task"> <div class="task-title">{t['name']}</div> <div class="task-meta">负责人: {t.get('assignee', '未分配')}</div> <div class="task-meta" style="color: #f44336;">阻塞: {', '.join(t.get('blockers', []))}</div> </div> ``` ### Technical Analysis The HTML board generator interpolates project data directly into an HTML document without context-appropriate escaping. Untrusted fields include the project name, task name, assignee, end date, blockers, and progress value. Several of these fields can be supplied through normal command-line operations and are persisted in project JSON files. An attacker able to influence project data can inject arbitrary HTML elements or event handlers. The payload becomes persistent because it is stored in the project data and reproduced whenever an HTML board is generated. For example, a task name containing the following value would be inserted directly into the document: ```html <img src=x onerror="alert('inj ...[truncated 1519 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape every text value before inserting it into HTML: ```python from html import escape safe_project_name = escape(str(project.get("name", "")), quote=True) safe_task_name = escape(str(t.get("name", "")), quote=True) safe_assignee = escape(str(t.get("assignee", "Unassigned")), quote=True) safe_end_date = escape(str(t.get("endDate", "Not set")), quote=True) safe_blockers = escape(", ".join(map(str, t.get("blockers", []))), quote=True) ``` 2. Validate numeric values separately before placing them into CSS: ```python progress = t.get("progress", 0) if not isinstance(progress, (int, float)): progress = 0 progress = max(0, min(100, progress)) ``` 3. Prefer a templating engine with automatic HTML escaping enabled rather than constructing the complete document with f-strings. 4. Add a restrictive Content Security Policy, preferably disallowing inline scripts and event handlers: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:"> ``` 5. Validate the complete loaded project schema before rendering it, even when the JSON file is expected to have been created by another project script. ]]>
