T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_dashboard.js:779
- Finding
- Stored HTML and JavaScript Injection Through Untrusted Dashboard Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_dashboard.js:19, 167, 692, 776-784, 870, 987-999` **Vulnerability Type**: Stored HTML/JavaScript injection **Risk Level**: High ### Vulnerable Code The generator accepts an arbitrary JSON file without schema validation or content sanitization: ```javascript const data = JSON.parse(fs.readFileSync(dataPath, 'utf8')); ``` Input values are interpolated directly into HTML contexts: ```javascript <title>Shadow AI Monitor - ${company}</title> ``` ```javascript <p class="subtitle">${company} - Employee AI Tool Usage Report</p> ``` ```javascript ${recentEvents.map(event => ` <tr class="${event.risk.toLowerCase()}-risk"> <td>${new Date(event.timestamp).toLocaleString()}</td> <td>${event.employee}</td> <td>${event.tool}</td> <td>${event.dataCategory}</td> <td><span class="risk-badge ${event.risk.toLowerCase()}">${event.risk}</span></td> </tr> `).join('')} ``` Input-derived data is also serialized directly into an inline script: ```javascript const employeeDrilldown = ${JSON.stringify(employeeDrilldown)}; ``` The browser-side modal then converts input-derived values into HTML and assigns them to `innerHTML`: ```javascript const eventsHTML = data.topRiskyEvents.map(event => ` <div class="event-item ${event.risk.toLowerCase()}"> <div><strong>${event.tool}</strong> - ${event.category}</div> <div class="event-meta"> <span>📅 ${event.date}</span> <span class="risk-badge ${event.risk.toLowerCase()}">${event.risk} Risk</span> </div> </div> `).join(''); document.getElementById('modalEvents').innerHTML = eventsHTML; ``` ### Technical Analysis The input file path is supplied through `process.argv[2]`, and all parsed fields are treated as trusted. No schema enforcement, type checking, HTML escaping, or JavaScript-context escaping is performed before input-derived values are inserted into the generated document. Values such as `company`, `event.em ...[truncated 2361 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Define and enforce a strict schema for the input document before generating HTML: - Require expected object and array structures. - Enforce primitive types and reasonable length limits. - Restrict `risk` to an explicit enumeration such as `Low`, `Medium`, or `High`. - Reject unexpected properties where practical. 2. Apply context-specific output encoding to every value inserted into HTML text, attributes, CSS, or JavaScript. A basic HTML text encoder should at least encode `&`, `<`, `>`, `"`, and `'`. 3. Do not construct the modal with `innerHTML`. Create DOM elements and assign untrusted values through `textContent`: ```javascript const tool = document.createElement('strong'); tool.textContent = event.tool; const category = document.createTextNode(` - ${event.category}`); container.append(tool, category); ``` 4. Avoid placing serialized user data inside an executable inline script. Prefer a separate JSON file fetched and parsed as data. If inline JSON is necessary, place it in a non-executable element such as: ```html <script id="dashboard-data" type="application/json">...</script> ``` The serialized content must still escape HTML parser-sensitive characters, including `<`, `>`, `&`, U+2028, and U+2029. In particular, replace `<` with `\u003c` so that `</script>` cannot terminate the element. 5. Add a restrictive Content Security Policy. After removing inline handlers and inline scripts, use a policy similar to: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'none'; base-uri 'none'; object-src 'none'"> ``` 6. Add regression tests containing payloads in every string field, including: - `<img src=x onerror=alert(1)>` - `</script><script>alert(1)</script>` - Quotes, ampersands, angle brackets, and Unicode line separators Verify that these value ...[truncated 80 chars]
