T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/check_ssl.py:187
- Finding
- Stored HTML Injection in Generated Certificate Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_ssl.py`, lines 187–198 **Vulnerability Type**: Unescaped HTML injection **Risk Level**: Medium ### Vulnerable Code ```python issuer = r["issuer"].get("organizationName", r["issuer"].get("commonName", "")) if r["issuer"] else "-" subject = r["subject"].get("commonName", "") if r["subject"] else "-" days = r["days_remaining"] if r["days_remaining"] is not None else "-" expiry = r["not_after"][:10] if r["not_after"] else "-" error = r["error"] if r["error"] else "" error_row = f'<tr class="table-danger"><td colspan="8">{error}</td></tr>' if error else "" rows += f"""<tr> <td>{r['hostname']}:{r['port']}</td> <td>{subject}</td> <td><span class="badge bg-{status_class}">{status_icon} {r['status'].replace('_', ' ').title()}</span></td> <td>{days}</td> <td>{expiry}</td> <td>{issuer}</td> <td>{', '.join(r['san'][:5])}</td> </tr>{error_row}""" ``` ### Technical Analysis The HTML report generator inserts dynamic values directly into HTML without context-appropriate escaping. The affected values include: - User-supplied hostnames - Certificate subject common names - Certificate issuer names - Subject Alternative Name entries - Network and certificate error messages - Status and other report fields Because these values are interpolated as markup rather than encoded as text, an attacker-controlled value containing HTML elements or event-handler attributes can alter the generated document. For example, a crafted hostname containing an HTML payload remains present in the result object even if DNS resolution fails and is subsequently inserted into the report. The generated report does not apply HTML escaping or a restrictive Content Security Policy. Consequently, active HTML content such as an element with an inline event handler may execute when the user opens the report in a browser. ### Attack Path 1. An attacker convinces the user or an automated process to include a c ...[truncated 1458 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape every dynamic value before inserting it into HTML. Use `html.escape(value, quote=True)` for hostnames, subjects, issuers, SAN entries, errors, statuses, dates, and all other externally derived values. 2. Centralize conversion and escaping to ensure non-string values are handled consistently: ```python from html import escape def html_text(value): return escape(str(value), quote=True) ``` 3. Apply escaping at the final HTML rendering boundary: ```python safe_hostname = html_text(r["hostname"]) safe_subject = html_text(subject) safe_issuer = html_text(issuer) safe_error = html_text(error) safe_sans = ", ".join(html_text(value) for value in r["san"][:5]) ``` 4. Prefer a maintained template engine with automatic HTML escaping if report generation becomes more complex. 5. Add a restrictive Content Security Policy, preferably without allowing inline scripts or event handlers. For a standalone report, an appropriate starting point is: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self' https://cdn.jsdelivr.net; img-src 'self' data:"> ``` The policy should be tested against the report's actual resource requirements. HTML escaping remains mandatory even when a CSP is present. 6. Add regression tests covering hostile values in every rendered field, including payloads such as: ```text <img src=x onerror=alert(1)> "><svg onload=alert(1)> <script>alert(1)</script> ``` Tests should verify that these values appear as encoded text and are never interpreted as HTML elements. ]]>
