T09 · Insecure Skill Coding Practices
Error
- Location
- templates/sim_single_file_html_template.html:413
- Finding
- Untrusted SimSpec Strings Can Break Out of the Generated Script Block## Vulnerability Details **File Location**: `templates/sim_single_file_html_template.html:413-420` **Related Validation Locations**: `templates/sim_spec_schema.json:27-30, 132-140, 157-160, 183-186, 228-234` **Vulnerability Type**: Generated HTML script injection **Risk Level**: High ### Vulnerable Code ```html const stateDefs = {{{state_json}}}; const paramDefs = {{{params_json}}}; const initialState = {{{initial_json}}}; const equations = {{{equations_json}}}; const outputs = {{{outputs_json}}}; const worksheet = {{{worksheet_json}}}; const successCriteria = {{{success_criteria_json}}}; const readoutFields = {{{readout_fields_json}}}; ``` The schema allows unrestricted strings in fields that can reach these placeholders. For example: ```json "title": { "type": "string", "minLength": 1 } ``` The same lack of content restrictions applies to multiple embedded fields, including units, descriptions, output labels, worksheet prompts, and success criteria. ### Technical Analysis The template embeds serialized SimSpec data directly into an executable `<script>` element through raw triple-mustache placeholders. JSON serialization alone is not sufficient contextual output encoding for an HTML script element. A string containing the HTML parser terminator `</script>` can terminate the enclosing script even when the sequence appears inside a JavaScript string literal. An attacker can follow it with a new `<script>` element containing arbitrary JavaScript. The schema validates these values only as non-empty strings and does not reject HTML control sequences. Although the application later displays dynamic labels and worksheet content through safe `textContent` assignments, the vulnerable values are processed by the HTML parser before those safe DOM operations occur. The affected data flow is: 1. An untrusted SimSpec supplies free-form text. 2. The text passes schema validation. 3. The value is serialized as JSON. 4. The serialized JSON is inserted raw int ...[truncated 2233 chars]
- Remediation
- ## Remediation Suggestions 1. **Use script-safe serialization for every embedded JSON value.** After `JSON.stringify`, escape characters that are significant to the HTML parser: ```javascript function serializeForInlineScript(value) { return JSON.stringify(value) .replace(/</g, "\\u003c") .replace(/>/g, "\\u003e") .replace(/&/g, "\\u0026") .replace(/\u2028/g, "\\u2028") .replace(/\u2029/g, "\\u2029"); } ``` Apply this serializer to all JSON placeholders before template insertion. 2. **Prefer inert JSON containers.** Store configuration in `<script type="application/json">` elements and parse their `textContent`. Script-closing sequences must still be escaped because the HTML parser recognizes `</script>` regardless of the script type. 3. **Apply context-specific encoding to non-JSON placeholders.** HTML-escape values inserted into element text and attribute-encode `sim_id`, `domain`, `level`, and `renderer_kind` before placing them in attributes. 4. **Add defense-in-depth validation.** Reject or normalize dangerous HTML control sequences in all free-text fields. This should supplement, not replace, correct contextual encoding. 5. **Add regression tests for every free-text field.** Test titles, units, descriptions, labels, worksheet prompts, success criteria, and extension metadata with payloads containing: ```text </script><script>alert(1)</script> ``` Confirm that the resulting document contains no additional executable script element and that the payload is displayed only as inert text. 6. **Extend the validation checklist.** Require verification that all SimSpec-derived values are safely encoded for their exact HTML, attribute, or JavaScript context before returning `index.html`.
