T09 · Insecure Skill Coding Practices
Warning
- Location
- assets/index.html:947
- Finding
- DOM-Based Cross-Site Scripting Through the Divination Question<![CDATA[ ## Vulnerability Details **File Location**: `assets/index.html`, lines 947–959, 998–1000, and 1037–1058 **Vulnerability Type**: DOM-based cross-site scripting caused by unsafe HTML insertion **Risk Level**: Medium ### Vulnerable Code The user-controlled question is read without sanitization and passed to the result renderer: ```javascript const q=document.getElementById('questionInput').value.trim(); renderDiagram(info,q); ``` The value is subsequently interpolated into markup assigned to `innerHTML`: ```javascript document.getElementById('guaDiagram').innerHTML=` ... <div class="gua-center-label"> <div class="gua-full-name">${info.upper.name}${info.lower.name===info.upper.name?'为'+info.upper.nature:''}卦</div> ${q?`<div class="gua-question-sm">「${q}」</div>`:''} </div> ...`; ``` The offline and request-error paths contain another unsafe HTML sink: ```javascript if(!apiKey){ loading.style.display='none'; out.innerHTML=offlineFallback(info,question); return; } ``` ```javascript out.innerHTML=`<span style="color:#9a5050;font-size:0.82rem">✦ ${err.message}</span>\n\n` +offlineFallback(info,question); ``` `offlineFallback()` includes the untrusted question in its returned string: ```javascript function offlineFallback(info,question){ const q=question?`仙家所问「${question}」,`:''; ... return `这位仙家,${q}六爻落定如下:\n${ys}\n\n` ... } ``` ### Technical Analysis The value from `questionInput` is attacker-controlled. It is embedded into HTML strings and parsed using `innerHTML`, so the browser treats injected tags and event-handler attributes as executable markup rather than plain text. The password input type used for the API key only masks its visual display. JavaScript running in the page can still access its value through the DOM. Consequently, successful script injection can read the configured API key, endpoint, model, question, and generated divination state. The page already demonstrates the safe alternative ...[truncated 1612 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Never insert the question into the DOM through `innerHTML`. 2. Construct the result layout with `document.createElement()` and assign the question through `textContent`. 3. Render offline fallback output using: ```javascript out.textContent = offlineFallback(info, question); ``` 4. For the request-error path, create the styled error element separately and assign `err.message` through `textContent`. 5. If HTML formatting is indispensable, sanitize the complete generated markup with a maintained sanitizer configured to reject scripts, event attributes, dangerous URLs, and active embedded content. 6. Add automated tests using HTML tags, event attributes, SVG payloads, malformed markup, and encoded payloads to verify that all question text is rendered literally. 7. Consider clearing the API-key field after use to reduce the period during which injected or unrelated scripts could read it. ]]>
