T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/enhanced_report_generator.py:408
- Finding
- Stored HTML Injection in Generated Usability Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/enhanced_report_generator.py:408-411` **Additional Affected Locations**: `scripts/enhanced_report_generator.py:555-558, 590-603, 660-677, 731-732` **Vulnerability Type**: Stored HTML injection caused by missing output encoding **Risk Level**: High ### Vulnerable Code ```python html += f""" <div class="page-analysis"> <h3>📄 Page Analysis: {page_analysis.get('title', 'Unknown')}</h3> <p><strong>Purpose:</strong> {page_analysis.get('purpose', 'Not analyzed')}</p> <p><strong>Navigation:</strong> {', '.join(page_analysis.get('navigation', ['None found']))}</p> """ ``` The same unsafe rendering pattern is used for Nova Act responses: ```python if observations_list: notes = '; '.join(str(o) for o in observations_list) elif raw_response: notes = f"Response: {raw_response}" elif error_msg: notes = f"Error: {error_msg}" else: notes = "No observations recorded" html += f""" <div class="observation-notes {notes_class}"> <strong>{"⚠️ " if is_issue else ""}Observation:</strong> {notes} </div> """ ``` ### Technical Analysis The report generator inserts website-derived and model-derived values directly into an HTML document without applying contextual HTML escaping. Affected values include: - Page titles, purposes, and navigation entries extracted from the tested website - Raw Nova Act responses - Persona names and test-case descriptions - Actions, expected outcomes, errors, and observations - Trace filenames and paths used in HTML attributes Because the tested website is an untrusted input source, an attacker can place HTML or script-bearing markup in visible page content. If Nova Act preserves that content in a title, navigation result, page description, or raw response, the report generator writes it verbatim into `nova_act_usability_report.html`. The vulnerability is stored rather than reflected: the malicious payload is first collecte ...[truncated 1782 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Apply HTML escaping to every untrusted text value before interpolation: ```python from html import escape title = escape(str(page_analysis.get("title", "Unknown")), quote=True) purpose = escape(str(page_analysis.get("purpose", "Not analyzed")), quote=True) navigation = escape( ", ".join(map(str, page_analysis.get("navigation", ["None found"]))), quote=True ) notes = escape(str(notes), quote=True) ``` 2. Escape attribute values separately with `quote=True`. Do not place raw trace paths or filenames into `href` attributes. 3. Validate trace links: - Resolve paths to canonical local paths. - Confirm they remain inside the expected log directory. - Convert them to file URIs with `Path.resolve().as_uri()`. - Reject unexpected schemes such as `javascript:`, `data:`, and remote HTTP URLs. 4. Prefer a template engine configured with automatic escaping instead of constructing HTML through f-strings. 5. Add a restrictive Content Security Policy to the generated report, for example: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data: file:"> ``` 6. Avoid enabling scripts in the report. If scripting is later required, use a nonce-based CSP and never permit inline event handlers. 7. Add regression tests using payloads in every dynamic field, including: - `<script>alert(1)</script>` - `<img src=x onerror=alert(1)>` - `" onmouseover="alert(1)` - `javascript:alert(1)` 8. Apply the correction consistently to all dynamic interpolation sites, not only the primary page-analysis section. ]]>
