T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_dashboard.py:396
- Finding
- Stored Cross-Site Scripting in Generated Dashboards<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_dashboard.py`, lines 396, 480, 525–527, 540, 548–553, and 601–606 **Vulnerability Type**: Stored cross-site scripting through unescaped HTML and unsafe inline JSON embedding **Risk Level**: High ### Vulnerable Code ```python <title>{title}</title> ``` ```html <header> <h1>📊 {title}</h1> <button class="theme-toggle" onclick="toggleTheme()">🌓 Toggle Theme</button> </header> ``` ```javascript grid.innerHTML = stats.map(s => ` <div class="stat-card"> <div class="label">${s.label}</div> <div class="value">${s.value}</div> </div> `).join(''); ``` ```javascript card.innerHTML = `<h3>${c.title}</h3><div class="chart-wrap"><canvas id="chart${i}"></canvas></div>`; ``` ```javascript let html = '<thead><tr>' + TABLE_HEADERS.map(h => `<th>${h}</th>`).join('') + '</tr></thead>'; html += '<tbody>' + TABLE_ROWS.map(r => '<tr>' + TABLE_HEADERS.map(h => `<td>${r[h] || ''}</td>`).join('') + '</tr>' ).join('') + '</tbody>'; table.innerHTML = html; ``` ```python chart_data_js = json.dumps({'charts': chart_data_list, 'stats': stats}, ensure_ascii=False) headers_js = json.dumps(headers, ensure_ascii=False) table_rows_js = json.dumps(table_rows, ensure_ascii=False) html = HTML_TEMPLATE.format( title=title, chart_data=chart_data_js, headers=headers_js, table_rows=table_rows_js ) ``` ### Technical Analysis CSV and JSON headers, cell values, chart titles derived from headers, and the command-line `--title` value can all contain attacker-controlled text. These values are embedded in the generated dashboard without context-appropriate encoding. There are two distinct injection mechanisms: 1. The dashboard title is inserted directly into the HTML `<title>` and `<h1>` contexts through `str.format()` without HTML escaping. 2. Headers, raw table values, statistics labels, and chart titles are passed to JavaScript and subsequently rendered through `innerHTML`. Although `json ...[truncated 2064 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace HTML-string construction with safe DOM operations: - Create elements using `document.createElement()`. - Assign untrusted values through `textContent`. - Append elements using `appendChild()` or `replaceChildren()`. - Do not pass headers, cell values, chart titles, or statistics labels to `innerHTML`. 2. Escape the dashboard title before inserting it into HTML: ```python import html safe_title = html.escape(title, quote=True) ``` Use `safe_title` for both the `<title>` and `<h1>` contexts. 3. Safely embed serialized JSON. At minimum, encode characters that are significant to HTML parsing: ```python def safe_json_for_html(value): return ( json.dumps(value, ensure_ascii=False) .replace('&', r'\u0026') .replace('<', r'\u003c') .replace('>', r'\u003e') .replace('\u2028', r'\u2028') .replace('\u2029', r'\u2029') ) ``` A stronger design is to place JSON in a non-executable element such as `<script type="application/json">`, safely encode it for HTML, and parse the element's `textContent`. 4. Add a restrictive Content Security Policy. Prefer moving inline JavaScript to a separate local script so that `script-src` does not require `'unsafe-inline'`. 5. Add regression tests covering malicious input in titles, headers, JSON property names, and cells. Test payload classes should include: - HTML tags. - Event-handler attributes. - Quotes and template-expression characters. - Closing `</script>` sequences. - Encoded and mixed-case variants. 6. Treat every input dataset as untrusted, even when the dashboard is intended for local use. ]]>
