T09 · Insecure Skill Coding Practices
- Location
reference/visualization-template.html:226- Finding
DOM-Based Cross-Site Scripting in Generated Ontology Visualizations
- Content
View full analysis
Vulnerability Details
File Location:
reference/visualization-template.html, lines 226–235
Vulnerability Type: DOM-based cross-site scripting through unsafeinnerHTMLconstruction
Risk Level: HighVulnerable Code
javascript function showDetail(node) { const st = TYPE_STYLE[node.type]; document.getElementById("hint")?.remove(); document.getElementById("detail").innerHTML = ` <div id="detail-name">${node.label.replace(/\n/g, " ")}</div> <span id="detail-tag" style="background:${st.fill};color:${st.text};border:1px solid ${st.stroke}">${st.label}</span> <div id="detail-desc">${node.desc || ""}</div> <div>${(node.props || []).map(p => `<div class="prop-row">${p}</div>`).join("")}</div> `; }The associated generation workflow is described in
SKILL.md, where graph content can be extracted from local or Feishu documents and inserted into the visualization template.Technical Analysis
The function inserts
node.label,node.desc, and every entry innode.propsdirectly into an HTML string assigned toinnerHTML. These fields are not HTML-escaped or sanitized.Because the skill instructs the agent to derive ontology graph data from user-selected documents, these graph fields may contain document-controlled content. If a document contains an HTML payload, that payload can become active markup when the generated visualization renders a node's details.
For example, a node description containing an element with an event handler could execute JavaScript after the node is selected:
html <img src=x onerror="alert(document.domain)">There is also a related embedding risk when replacing the template's
GRAPHobject. If values are inserted into JavaScript source without robust JSON serialization, specially crafted quotes, backslashes, closing script tags, or Unicode line separators could break out of the intended string context. The reviewed files do not define a safe serialization procedure.Att
...[truncated 1487 chars]
- Remediation
View remediation
Remediation Suggestions
- Eliminate
innerHTMLfor all document-derived graph values. Construct elements usingdocument.createElement()and assign untrusted strings throughtextContent.
javascript function showDetail(node) { const st = TYPE_STYLE[node.type]; const detail = document.getElementById("detail"); detail.replaceChildren(); const name = document.createElement("div"); name.id = "detail-name"; name.textContent = String(node.label || "").replace(/\n/g, " "); detail.appendChild(name); const tag = document.createElement("span"); tag.id = "detail-tag"; tag.textContent = st.label; tag.style.background = st.fill; tag.style.color = st.text; tag.style.border = `1px solid ${st.stroke}`; detail.appendChild(tag); const description = document.createElement("div"); description.id = "detail-desc"; description.textContent = String(node.desc || ""); detail.appendChild(description); const properties = document.createElement("div"); for (const property of node.props || []) { const row = document.createElement("div"); row.className = "prop-row"; row.textContent = String(property); properties.appendChild(row); } detail.appendChild(properties); }-
If rich HTML is a strict requirement, sanitize it with a well-maintained allowlist sanitizer. Disallow scripts, event-handler attributes, dangerous URL schemes, SVG active content, and unsafe style constructs.
-
Generate
GRAPHusing a proper JSON serializer rather than manual string interpolation. Before embedding serialized JSON in a script element, escape at least<, U+2028, and U+2029 to prevent script-context breakout. -
Prefer storing graph data in an
application/jsonscript block and parse it withJSON.parse, while still safely escaping closing script sequences. -
Validate graph structure before rendering:
- Require known node types.
- Coerce labels, descriptions, and properties to strings.
- Reject unexpected object values ...[truncated 451 chars]
- Eliminate
