T09 · Insecure Skill Coding Practices
- Location
- eval-viewer/generate_review.py:273
- Finding
- Stored JavaScript Injection Through Embedded Evaluation Output<![CDATA[ ## Vulnerability Details **File Location**: `eval-viewer/generate_review.py:273-275` **Related Sink**: `eval-viewer/viewer.html:648` **Vulnerability Type**: Stored JavaScript injection caused by unsafe JSON embedding **Risk Level**: High ### Vulnerable Code ```python data_json = json.dumps(embedded) return template.replace( "/*__EMBEDDED_DATA__*/", f"const EMBEDDED_DATA = {data_json};" ) ``` The generated data is inserted into this executable script context: ```html <script> // ---- Embedded data (injected by generate_review.py) ---- /*__EMBEDDED_DATA__*/ </script> ``` ### Technical Analysis `generate_review.py` recursively reads evaluation output files and includes their content in the `embedded` object. The resulting object is serialized with `json.dumps()` and inserted directly into an HTML `<script>` element. JSON string escaping alone is insufficient for safe insertion into an HTML script context. In particular, Python's default JSON encoder does not escape the `<` character. If an evaluation output contains a sequence such as: ```html </script><script> // Attacker-controlled JavaScript </script> ``` the HTML parser treats the first `</script>` as the end of the original script element, even though the sequence appears inside a JavaScript string. The following script element is then parsed and executed. This is a stored injection issue because the payload can be placed in an evaluation output file and later executed automatically when the generated review page is opened. Evaluation outputs may be influenced by untrusted prompts, generated artifacts, tested Skills, or compromised tools. The use of `textContent` when rendering ordinary text later in `viewer.html` does not mitigate this vulnerability because execution occurs while the browser initially parses the generated HTML. ### Attack Path 1. An attacker supplies a test prompt, input artifact, Skill, or generated output that causes a file under an evaluation run's ...[truncated 1774 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not interpolate serialized untrusted data directly into an executable `<script>` element. 2. Store the serialized data in a non-executable element and parse its text explicitly: ```html <script id="embedded-data" type="application/json"> <!-- Safely escaped JSON is inserted here --> </script> <script> const EMBEDDED_DATA = JSON.parse( document.getElementById("embedded-data").textContent ); </script> ``` 3. Before inserting JSON into HTML, escape characters significant to the HTML parser: ```python data_json = json.dumps(embedded) data_json = ( data_json .replace("&", "\\u0026") .replace("<", "\\u003c") .replace(">", "\\u003e") .replace("\u2028", "\\u2028") .replace("\u2029", "\\u2029") ) ``` Escaping `<` is essential because it prevents construction of `</script>`. 4. Prefer a well-reviewed HTML templating or serialization utility that explicitly supports safe JSON embedding. 5. Add regression tests using output content containing: - `</script><script>alert(1)</script>` - HTML event handlers - U+2028 and U+2029 - nested JSON and multiline source files 6. Add a restrictive Content Security Policy. Avoid inline scripts where possible and use hashes or nonces for trusted scripts. Restrict `connect-src`, `img-src`, `frame-src`, and `script-src` to the minimum required origins. 7. Treat all evaluation outputs as untrusted, even when they were generated locally. ]]>
