T09 · Insecure Skill Coding Practices
- Location
HTML-Tools/Graph-Overview-Generator.html:416- Finding
DOM-based cross-site scripting through unescaped graph fields
- Content
View full analysis
Vulnerability Details
File Location:
HTML-Tools/Graph-Overview-Generator.html, lines 416–447
Vulnerability Type: DOM-based cross-site scripting caused by unsafeinnerHTMLrendering
Risk Level: MediumVulnerable Code
javascript $('typeHist').innerHTML = typeSorted.slice(0,20).map(([t,c])=>{ const w = (c/maxType*100).toFixed(0); return `<div class="hist-row"><span class="hist-label" title="${t}">${t}</span><div class="hist-bar-wrap"><div class="hist-bar" style="width:${w}%"></div></div><span class="hist-count">${c}</span></div>`; }).join('') || '<span style="font-size:0.82rem;color:#888780">No types</span>'; $('predHist').innerHTML = predSorted.slice(0,12).map(([p,c])=>{ const w=(c/maxPred*100).toFixed(0); return `<div class="hist-row"><span class="hist-label" title="${p}">${p}</span><div class="hist-bar-wrap"><div class="hist-bar" style="width:${w}%;background:#0F6E56"></div></div><span class="hist-count">${c}</span></div>`; }).join('') || '<span style="font-size:0.82rem;color:#888780">No predicates</span>'; if(Object.keys(statusMap).length){ $('statusBox').innerHTML = Object.entries(statusMap).sort((a,b)=>b[1]-a[1]).map(([s,c])=>`<span class="badge b2" style="margin:3px">${s}: ${c}</span>`).join(''); } let hHtml = `<table class="table"><tr><th>#</th><th>ID</th><th>Type</th><th>Degree</th><th>Connected Types</th></tr>`; top20.forEach((h,i)=>{ hHtml+=`<tr><td>${i+1}</td><td class="mono">${h.id}</td><td>${h.type}</td><td><span class="badge b1">${h.total}</span> <span style="font-size:0.75rem;color:#5f5e5a">(${h.indegree}/${h.outdegree})</span></td><td style="font-size:0.76rem">${h.connectedTypes.slice(0,4).join(', ')}</td></tr>`; }); hHtml+=`</table>`; $('hubTableWrap').innerHTML = hHtml; if(bridgeCandidates.length){ $('bridgeBox').innerHTML = bridgeCandidates.slice(0,12).map(b=>`<span class="badge b1" style="margin:3px" title="${b.connectedTypes.join(', ')}">${b.id} (${b.type}) → ${b.connectedTypes.length} types</span> ...[truncated 3099 chars]- Remediation
View remediation
Remediation Suggestions
- Replace HTML-string construction with DOM APIs and
textContent:
javascript const label = document.createElement('span'); label.className = 'hist-label'; label.textContent = String(t); label.title = String(t);-
Avoid assigning graph-derived content through
innerHTML. UsereplaceChildren,append, andcreateElementfor tables, badges, histogram rows, and comment entries. -
Where static markup must be inserted, keep it separate from untrusted values. Assign untrusted values only through
textContentor safe DOM properties. -
If HTML templating cannot be removed, apply a well-reviewed sanitizer and context-appropriate escaping to every graph-controlled value, including values placed in attributes. Generic string replacement is not sufficient for all HTML contexts.
-
Apply the same hardening to all
innerHTMLsinks in the file, including type, predicate, status, hub, bridge, comment, and metadata rendering. -
Add regression tests using graph fields containing payloads such as HTML tags, quoted attribute escapes, SVG markup, and event-handler attributes. Verify that all payloads appear as inert text and that no network request or script execution occurs.
-
Consider a restrictive Content Security Policy as defense in depth, while retaining proper output encoding as the primary fix.
- Replace HTML-string construction with DOM APIs and
