T09 · Insecure Skill Coding Practices
Error
- Location
- assets/template.html:294
- Finding
- Externally Sourced Travel Data Enables Stored HTML and JavaScript Injection<![CDATA[ ## Vulnerability Details **File Location**: `assets/template.html:294-309, 423-447, 461-468, 560` **Related Data-Handling Instructions**: `SKILL.md:178, 310-313` **Vulnerability Type**: Stored client-side HTML/JavaScript injection **Risk Level**: High ### Vulnerable Code ```javascript var LOADING_STEPS = {{LOADING_STEPS_JSON}}; var NODES_ORIGINAL = {{NODES_JSON}}; var NODES = JSON.parse(JSON.stringify(NODES_ORIGINAL)); ``` ```javascript var stepsHtml = ''; for (var i = 0; i < LOADING_STEPS.length; i++) { stepsHtml += '<div class="loading-step" id="ls' + i + '">' + LOADING_STEPS[i] + '</div>'; } document.getElementById('loadingSteps').innerHTML = stepsHtml; ``` ```javascript if (node.status === 'unlocked') { buttons = '<div class="btn-group"><a class="btn btn-book" href="' + node.jumpUrl + '" target="_blank" onclick="handleBook(' + i + ')">前往预订</a></div>'; } else if (node.status === 'in_progress') { buttons = '<div class="btn-group">' + '<a class="btn btn-book" href="' + node.jumpUrl + '" target="_blank" onclick="handleViewOrder(' + i + ')">查看订单</a>' + '<button class="btn btn-verify" onclick="handleVerify(' + i + ')">确认到店 (模拟核销)</button>' + '</div>'; } html += '<div class="node-card ' + statusClass + '" id="node-card-' + i + '">' + '<img class="node-img" src="' + node.picUrl + '" alt="' + node.title + '" onerror="this.style.background=\'#333\';this.style.height=\'80px\'">' + '<div class="node-body">' + '<span class="node-index">' + (i + 1) + '</span>' + '<span class="node-title">' + node.chapter + ': ' + node.title + '</span>' + '<span class="node-status-tag ' + tagClassMap[node.status] + '">' + statusTextMap[node.status] + '</span>' + '<p class="node-story">"' + node.story + '"</p>' + '<p class="node-meta">📍 ' + node.address + ' | 🎫 ' + node.skuName + '</p>' + buttons + '</div></div>'; container.innerHTML = html; ``` ```javascript for (var i = 0; i < gameState.rewards.length; i++ ...[truncated 3387 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace HTML-string concatenation with safe DOM construction: - Use `document.createElement`. - Insert textual fields with `textContent`. - Set URLs through DOM properties only after validation. - Avoid assigning externally derived strings to `innerHTML`. 2. Serialize embedded JSON with an HTML-safe serializer: - Escape `<` as `\u003c`. - Escape `>` as `\u003e`. - Escape `&` as `\u0026`. - Escape U+2028 and U+2029. - Ensure `</script>` cannot terminate the containing script element. - Prefer embedding data in an `application/json` element and parsing its `textContent`. 3. Apply strict URL validation: - Parse URLs with a standards-compliant URL parser. - Permit only `https:` for remote booking and image resources. - Reject embedded credentials, control characters, malformed hosts, and unexpected ports. - Where feasible, allowlist official FlyAI or Fliggy destination hosts. - Reject `javascript:`, `data:`, `file:`, `blob:`, and other unneeded schemes. 4. Add `rel="noopener noreferrer"` to every external link using `target="_blank"`. 5. Treat every external CLI response as untrusted, even if it is valid JSON. 6. Add a restrictive Content Security Policy that disallows inline event handlers and limits scripts, images, and navigation destinations. Refactoring inline handlers should precede enabling a strict policy. 7. Add security tests containing payloads with quotes, angle brackets, `</script>`, event handlers, malformed URLs, and Unicode separator characters. ]]>
