T09 · Insecure Skill Coding Practices
- Location
templates/cluster-map.html:320- Finding
Stored HTML and JavaScript Injection in Generated Cluster Maps
- Content
View full analysis
` element: ```javascript // === CLUSTER DATA (replaced by skill at generation time) === const CLUSTER_DATA = { pillar: { title: "Sample Pillar Page", keyword: "sample keyword", volume: 5000, template: "ultimate-guide", wordCount: 3500, url: "/pillar" }, clusters: [ { name: "Sample Cluster", color: "#4E79A7", posts: [ { title: "Sample Post 1", keyword: "sample post 1", volume: 1200, template: "how-to", wordCount: 1500, url: "/post-1", status: "planned" }, { title: "Sample Post 2", keyword: "sample post 2", volume: 800, template: "listicle", wordCount: 1400, url: "/post-2", status: "planned" } ] } ], links: [ { from: "pillar", to: "cluster-0-post-0", type: "mandatory" }, { from: "cluster-0-post-0", to: "pillar", type: "mandatory" } ], meta: { totalPosts: 3, totalClusters: 1, totalLinks: 2, estimatedWords: 6400 } }; // = ...[truncated 5705 chars]- Remediation
View remediation
``` - Read the element through `textContent` and parse it with `JSON.parse()`. - When generating the HTML, use a script-safe serializer that escapes at least `<`, `>`, `&`, U+2028, and U+2029. For example, encode `<` as `\u003c` before insertion. 2. **Eliminate SVG string concatenation and `innerHTML`.** - Create SVG elements with `document.createElementNS("http://www.w3.org/2000/svg", ...)`. - Populate properties using `setAttribute()` only after validation. - Set labels with `textContent` rather than constructing markup strings. 3. **Validate the complete cluster-plan schema before rendering.** - Reject unknown properties and unexpected data types. - Restrict `link.type` to an allowlist such as `mandatory`, `recommended`, and `optional`. - Require `link.from` and `link.to` to match identifiers generated from the validated plan. - Restrict status values to `planned` and `written`. - Validate colors against a strict format such as `^#[0-9A-Fa-f]{6}$`. - Apply reasonable length limits to titles, keywords, URLs, cluster names, and anchors. 4. **Treat imported and existing files as untrusted input.** - Validate strategy imports and `cluster-plan.json` even when they are read from the current working directory. - Do not assume that locally stored plans were generated by a trusted process. 5. **Add defense-in-depth controls.** - Add a restrictive Content Security Policy that disallows inline scripts and event handlers. - Keep generated maps self-contained and avoid granting them access to privileged browser APIs. - If maps are hosted, serve them from an isolated origin rather than a sensitive application origin. 6. **Add security regression tests.** Test all rendered fie ...[truncated 376 chars]
