T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_heat_balance_viz.py:61
- Finding
- Stored HTML and JavaScript Injection in Generated Visualization Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_heat_balance_viz.py:61-86` **Vulnerability Type**: Unescaped user-controlled data in generated HTML **Risk Level**: High The report generator inserts values from an input JSON document directly into HTML text, an HTML attribute, and inline CSS without contextual escaping or validation. ### Vulnerable Code ```python # Generate process flow HTML process_flow_items = "" for stage in process_stages: process_flow_items += f''' <div class="process-unit {stage['type']}"> <div>{stage['name']}</div> <div class="temp-display">{stage['temp']}°C</div> <div class="temp-label">{stage['label']}</div> </div>''' # Generate energy distribution table energy_table_rows = "" for item in energy_distribution: energy_table_rows += f''' <tr> <td>{item['item']}</td> <td>{item['percentage']}%</td> <td>{item['description']}</td> </tr>''' # Generate KPI cards kpi_cards = "" for kpi in kpis: kpi_cards += f''' <div class="kpi-card"> <div class="kpi-value">{kpi['value']}</div> <div class="kpi-label">{kpi['label']}</div> </div>''' ``` ### Technical Analysis The `process_stages`, `energy_distribution`, and `kpis` collections originate from JSON supplied through either `--input` or `--data`. Their fields are interpolated into the resulting HTML through Python f-strings without HTML encoding. Most values are inserted into HTML text contexts, where a payload containing closing tags and event-handler markup can terminate the intended element and introduce executable content. For example, a stage name could contain: ```html </div><img src=x onerror="alert(document.domain)"><div> ``` The `stage['type']` field is especially sensitive because it is inserted inside a quoted `class` attribute. An ...[truncated 1881 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape every value inserted into HTML text or attribute contexts: ```python from html import escape def html_text(value: Any) -> str: return escape(str(value), quote=True) ``` Apply this function to stage names, labels, energy-distribution text, KPI values, and all other text fields. 2. Do not treat `stage['type']` as arbitrary text. Validate it against an explicit allowlist: ```python allowed_stage_types = {"cold", "normal", "hot"} stage_type = str(stage.get("type", "normal")) if stage_type not in allowed_stage_types: stage_type = "normal" ``` 3. Enforce data types and reasonable ranges for numeric fields before rendering: ```python temperature = float(stage["temp"]) percentage = float(item["percentage"]) if not -273.15 <= temperature <= 3000: raise ValueError("Temperature is outside the permitted range") if not 0 <= percentage <= 100: raise ValueError("Percentage must be between 0 and 100") ``` 4. Prefer a mature template engine configured with automatic HTML escaping rather than assembling markup through raw f-strings. 5. Add regression tests containing characters such as `<`, `>`, `"`, `'`, and `&`, as well as complete event-handler payloads. Verify that the resulting report contains encoded text and no attacker-created elements or attributes. 6. If reports are hosted by a web application, apply a restrictive Content Security Policy that disallows inline scripts and event handlers. This should be defense in depth and must not replace output encoding and input validation. ]]>
