T09 · Insecure Skill Coding Practices
- Location
templates/sim_single_file_html_template.html:413- Finding
Untrusted SimSpec Strings Can Break Out of the Generated Script Block
- Content
View full analysis
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: HighVulnerable 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
textContentassignments, the vulnerable values are processed by the HTML parser before those safe DOM operations occur.The affected data flow is:
- An untrusted SimSpec supplies free-form text.
- The text passes schema validation.
- The value is serialized as JSON.
- The serialized JSON is inserted raw int ...[truncated 2233 chars]
- Remediation
View remediation
Remediation Suggestions
-
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.
-
Prefer inert JSON containers.
Store configuration in
<script type="application/json">elements and parse theirtextContent. Script-closing sequences must still be escaped because the HTML parser recognizes</script>regardless of the script type. -
Apply context-specific encoding to non-JSON placeholders.
HTML-escape values inserted into element text and attribute-encode
sim_id,domain,level, andrenderer_kindbefore placing them in attributes. -
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.
-
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.
-
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.
-
