T09 · Insecure Skill Coding Practices
Error
- Location
- references/plugins/g6-plugin-tooltip.md:73
- Finding
- DOM XSS Through Unescaped Graph Data in Tooltip Content<![CDATA[ ## Vulnerability Details **File Location**: `references/plugins/g6-plugin-tooltip.md:73-85` and `references/plugins/g6-plugin-tooltip.md:108-124` **Vulnerability Type**: Unescaped HTML interpolation leading to DOM-based cross-site scripting **Risk Level**: High ### Vulnerable Code ```javascript getContent: (event, items) => { const item = items[0]; if (!item) return ''; const { data } = item; return ` <div style="padding: 8px 12px; min-width: 120px;"> <div style="font-weight: bold; margin-bottom: 4px;">${data.name || item.id}</div> ${data.age ? `<div>年龄:${data.age}</div>` : ''} ${data.dept ? `<div>部门:${data.dept}</div>` : ''} ${data.relation ? `<div>关系:${data.relation}</div>` : ''} </div> `; }, ``` The second documented variant exposes every property of the graph data: ```javascript getContent: (event, items) => { const [item] = items; const d = item.data; return ` <div style="background:#fff;border:1px solid #eee;padding:12px;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.1)"> <h4 style="margin:0 0 8px">${d.name}</h4> <table style="border-collapse:collapse"> ${Object.entries(d).map(([k, v]) => ` <tr> <td style="color:#999;padding:2px 8px 2px 0">${k}</td> <td style="font-weight:500">${v}</td> </tr> `).join('')} </table> </div> `; }, ``` ### Technical Analysis The examples directly interpolate graph-record fields into strings returned by the G6 tooltip `getContent` callback. These strings are intended to be interpreted as HTML. No HTML escaping, contextual output encoding, sanitization, or trusted-data requirement is applied. Graph data commonly originates from APIs, imported files, databases, or user-generated records. Consequently, properties such as `name`, `dept`, `relation`, object keys, and object values must be treated as untrusted. An attacker-controlled value such as the following can introduce a ...[truncated 1618 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Prefer returning an `HTMLElement` and assign all dynamic values through `textContent`. - Do not construct markup by interpolating graph-record properties into template literals. - If rich HTML must be accepted, sanitize it with a maintained HTML sanitizer configured to reject scripts, event-handler attributes, dangerous URLs, and unsafe elements. - Treat graph data as untrusted unless its provenance and validation are explicitly guaranteed. - Add centralized helpers for safely creating tooltip rows rather than repeating HTML construction. - Add test cases using payloads in every rendered property, including object keys. A safer implementation is: ```javascript getContent: (event, items) => { const item = items[0]; const container = document.createElement('div'); container.style.cssText = 'padding:8px 12px;min-width:120px'; const title = document.createElement('div'); title.style.cssText = 'font-weight:bold;margin-bottom:4px'; title.textContent = String(item?.data?.name ?? item?.id ?? ''); container.appendChild(title); for (const field of ['age', 'dept', 'relation']) { const value = item?.data?.[field]; if (value === undefined || value === null) continue; const row = document.createElement('div'); row.textContent = `${field}: ${String(value)}`; container.appendChild(row); } return container; }, ``` ]]>
