T09 · Insecure Skill Coding Practices
Error
- Location
- token_report.py:251
- Finding
- Stored HTML and Script Injection in Generated Dashboard<![CDATA[ ## Vulnerability Details **File Location**: `token_report.py:251-254`, `token_report.py:289-292`, `token_report.py:319`, and `token_report.py:344-347` **Vulnerability Type**: Stored HTML injection / cross-site scripting in a generated local report **Risk Level**: High ### Vulnerable Code Provider names are inserted directly into HTML: ```python bars_html += f""" <div class="bar-item"> <div class="bar-label"> <span><strong>{provider}</strong></span> <span>${breakdown.total_cost:.2f} ({percentage:.1f}%)</span> </div> <div class="bar-bg"> <div class="bar-fill" style="width: {percentage}%"></div> </div> </div> """ ``` Model names are also inserted directly: ```python bars_html += f""" <div class="bar-item"> <div class="bar-label"> <span><strong>{model}</strong></span> <span>${breakdown.total_cost:.2f} ({breakdown.request_count} requests)</span> </div> <div class="bar-bg"> <div class="bar-fill" style="width: {percentage}%"></div> </div> </div> """ ``` Dates derived from imported timestamps are placed inside an HTML attribute: ```python bars_html += f'<div class="line-bar" style="height: {height_percent}%" title="{point.date}: ${point.cost:.2f}"></div>' ``` Waste alerts embed the unescaped model name: ```python alerts_html += f""" <div class="waste-alert"> <strong>⚠️ {case['model']}</strong><br> Cost: ${case['total_cost']:.2f} across {case['request_count']} requests<br> <em>{case['suggestion']}</em> </div> """ ``` The affected values originate from imported, potentially attacker-controlled CSV or JSON records. Relevant source assignments include: ```python timestamp = row.get('Timestamp') or row.get('timestamp') or row.get('Date') model = row.get('Model') or row.get('model') ``` and: ```python timestamp = item.get('timestamp') or item.get('created_at') model = item.get('model') ``` No HTML escaping or contextual output encoding is applied before these ...[truncated 2968 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Import Python's standard HTML-escaping function: ```python from html import escape ``` 2. Escape every untrusted string at the final output boundary. Use quote escaping for values that may appear in attributes: ```python safe_provider = escape(str(provider), quote=True) safe_model = escape(str(model), quote=True) safe_date = escape(str(point.date), quote=True) safe_suggestion = escape(str(case['suggestion']), quote=True) ``` 3. Use only escaped variables in HTML: ```python <span><strong>{safe_provider}</strong></span> ``` ```python <span><strong>{safe_model}</strong></span> ``` ```python bars_html += ( f'<div class="line-bar" ' f'style="height: {height_percent:.2f}%" ' f'title="{safe_date}: ${point.cost:.2f}"></div>' ) ``` 4. Validate imported records before report generation: - Require provider and model fields to be strings. - Set reasonable maximum lengths. - Reject control characters. - Parse timestamps into a strict accepted format and regenerate their display representation from the parsed date. - Reject non-finite or negative numeric values where they are not meaningful. 5. Prefer a template engine with automatic HTML escaping if external dependencies become acceptable. Keep auto-escaping enabled and avoid marking imported content as safe. 6. Add a restrictive Content Security Policy to provide defense in depth. For example: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src 'none'; script-src 'none'; connect-src 'none'; base-uri 'none'; form-action 'none'"> ``` Output encoding remains necessary because a policy does not prevent all HTML-based report spoofing. 7. Add regression tests using payloads in every imported text field, including: - `<script>alert(1)</script>` - `<img src=x onerror=alert(1)>` - `" onmouseover="aler ...[truncated 218 chars]
