T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_report.py:40
- Finding
- Stored HTML and JavaScript Injection Through Unescaped Report Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py`, lines 40–44; additional affected sinks at lines 143–145, 165, 177, 190, 224, and 318–319 **Vulnerability Type**: Stored HTML injection / cross-site scripting **Risk Level**: High ### Vulnerable Code ```python cards.append(f""" <div class="kpi"> <div class="kpi-label">{kpi.get('label', '')}</div> <div class="kpi-value" style="color:{color_var}">{kpi.get('value', '')}</div> <div class="kpi-sub">{kpi.get('sub', '')}</div> </div>""") ``` The same unsafe interpolation pattern is used in other report sections: ```python cards_html.append(f""" <div class="chart-card"> <h3>{title}</h3> <div class="chart-wrap"><canvas id="chart{i}"></canvas></div> </div>""") ``` ```python header_cells = "".join([f"<th>{col}</th>" for col in columns]) ``` ```python value = row.get(col, "") cell_html = f"<td>{value}</td>" ``` ```python <title>{title}</title> ``` ```python <h1>📊 {title}</h1> <p class="subtitle">{subtitle}</p> ``` ```python <footer>{footer}</footer> ``` ### Technical Analysis Report content is inserted directly into HTML through Python formatted strings without context-appropriate escaping. Affected inputs include: - Report title, subtitle, and footer - KPI labels, values, and subtitles - Chart and table titles - Table column names and cell values These values may originate from command-line arguments, JSON input, database query results, CSV files, or direct calls to `build_report`. Consequently, imported data cannot safely be treated as trusted markup. An attacker can supply an HTML element with an event handler, such as an image whose error handler executes JavaScript. The payload is preserved in the generated report and runs when a user opens that report in a browser. This is a stored injection issue because the payload is written into the output HTML before execution. ### Attack Path 1. An attacker places mal ...[truncated 1263 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape every value inserted into HTML text using `html.escape`: ```python from html import escape def _html_text(value) -> str: return escape(str(value), quote=True) ``` 2. Apply the helper to all report title, subtitle, footer, KPI, chart title, table title, column, and cell values: ```python label = _html_text(kpi.get("label", "")) value = _html_text(kpi.get("value", "")) sub = _html_text(kpi.get("sub", "")) ``` 3. Do not use one generic transformation for every output context. Use: - HTML escaping for element text. - Attribute escaping for attribute values. - JSON serialization for JavaScript values. - Strict validation for enumerated configuration values. 4. Treat CSV, SQL, JSON, and query-derived values as untrusted, even when the report is generated locally. 5. Consider adding a restrictive Content Security Policy. If scripts remain inline, migrate them to a separately generated trusted script or use hashes/nonces rather than enabling unrestricted inline script execution. 6. Add regression tests covering payloads in every supported text field, including tags, quotes, event handlers, closing elements, and Unicode edge cases. ]]>
