T09 · Insecure Skill Coding Practices
Error
- Location
- assets/smart-classroom-workbench.html:428
- Finding
- Stored DOM-Based Cross-Site Scripting Through Untrusted Classroom State<![CDATA[ ## Vulnerability Details **File Location**: `assets/smart-classroom-workbench.html`, lines 428-444; additional entry points at lines 471-483 and 573-584 **Vulnerability Type**: Stored DOM-based cross-site scripting **Risk Level**: High ### Vulnerable Code ```javascript function paint(){ const s = history[cursor]; if(!s) return; document.getElementById("topic").textContent = s.topic; document.getElementById("boardBody").innerHTML = renderBoard(s.board); document.getElementById("boardCaption").textContent = (s.board && s.board.caption) || ""; document.getElementById("formula").innerHTML = s.formula.main + "<small>" + s.formula.note + "</small>"; document.getElementById("mindmap").innerHTML = '<ul class="mindmap"><li class="root">' + s.mindmap.root + "</li>" + s.mindmap.items.map(i => "<li>" + i + "</li>").join("") + "</ul>"; document.getElementById("transcript").innerHTML = s.transcript.map(function(t){ if(Array.isArray(t)){ return '<span class="tag ' + t[0] + '">' + t[1] + "</span>"; } return t + "<br>"; }).join(""); document.getElementById("dialogue").innerHTML = (s.dialogue || []).map(function(m){ return '<div class="msg ' + m.r + '">' + m.t + "</div>"; }).join(""); document.getElementById("step").textContent = (cursor + 1) + "/" + history.length; document.getElementById("btnBack").disabled = cursor <= 0; document.getElementById("btnFwd").disabled = cursor >= history.length - 1; renderTimeline(); } ``` Untrusted JSON can reach these sinks through file import: ```javascript function importJSON(file){ if(!file) return; const reader = new FileReader(); reader.onload = function(e){ try{ const data = JSON.parse(e.target.result); const arr = Array.isArray(data) ? data : (data.history || []); if(!arr.length) throw new Error("No history data"); history = arr; cursor = 0; paint(); }catch(err){ showToast("Import failed", err.message); } }; reader.readAsText(file); ...[truncated 3100 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace `innerHTML` with safe DOM operations for textual content: - Assign text through `textContent`. - Create lists, dialogue bubbles, transcript entries, and timeline nodes with `document.createElement()`. - Apply classes only after validating values against strict allowlists. 2. Validate all state objects before storing or rendering them: - Require the documented top-level fields. - Enforce expected scalar, array, and object types. - Restrict `dialogue.r` to `t` or `s`. - Restrict board types to an explicit allowlist. - Reject unknown properties, excessive nesting, and oversized strings. 3. Do not permit unrestricted HTML: - Prefer a structured visualization format rendered by application-owned code. - If HTML or SVG support is essential, process it with a maintained, strict allowlist sanitizer. - Remove scripts, event-handler attributes, unsafe URL schemes, embedded documents, `foreignObject`, and other active content. - Apply SVG-specific sanitization rather than treating SVG as ordinary HTML. 4. Add a restrictive Content Security Policy: - Avoid inline event handlers and inline scripts. - Do not enable `unsafe-inline`. - Restrict scripts, connections, images, frames, objects, and base URLs to required sources. 5. Treat AI-generated markup and lesson-history files as untrusted: - Display a warning before importing externally obtained files. - Validate and sanitize every imported history entry. - Sanitize again when exporting or redistributing legacy state. 6. Add automated security tests using payloads in every rendered field to verify that imported and API-submitted content cannot execute JavaScript or inject active UI elements. ]]>
