T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_reports.py:583
- Finding
- Stored HTML and JavaScript Injection in Generated Dashboard<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_reports.py:583-597`; `assets/dashboard_template.html:481-488`; `assets/dashboard_template.html:602-616` **Vulnerability Type**: Stored cross-site scripting through unsafe JavaScript serialization and DOM insertion **Risk Level**: High ### Vulnerable Code ```python # scripts/generate_reports.py:583-597 chart_data = self._prepare_chart_data() content = content.replace('{{SALES_TREND_DATA}}', json.dumps(chart_data['sales_trend'], ensure_ascii=False)) content = content.replace('{{PRICE_TREND_DATA}}', json.dumps(chart_data['price_trend'], ensure_ascii=False)) content = content.replace('{{PRICE_DIST_DATA}}', json.dumps(chart_data['price_dist'], ensure_ascii=False)) content = content.replace('{{RATING_DIST_DATA}}', json.dumps(chart_data['rating_dist'], ensure_ascii=False)) content = content.replace('{{BRAND_SHARE_DATA}}', json.dumps(chart_data['brand_share'], ensure_ascii=False)) content = content.replace('{{SELLER_SOURCE_DATA}}', json.dumps(chart_data['seller_source'], ensure_ascii=False)) content = content.replace('{{BRAND_RATING_TREND_DATA}}', json.dumps(chart_data['brand_rating_trend'], ensure_ascii=False)) content = content.replace('{{TOP50_PRODUCTS}}', json.dumps(chart_data['top50_products'], ensure_ascii=False)) # Compatibility with legacy template variables content = content.replace('{{STATISTICS_JSON}}', json.dumps(self.statistics, ensure_ascii=False)) content = content.replace('{{PRODUCTS_JSON}}', json.dumps(self.products[:50], ensure_ascii=False)) content = content.replace('{{SCORES_JSON}}', json.dumps(self.scores, ensure_ascii=False)) ``` ```html <!-- assets/dashboard_template.html:481-488 --> <script> const salesTrendData = {{SALES_TREND_DATA}}; const priceTrendData = {{PRICE_TREND_DATA}}; const priceDistData = {{PRICE_DIST_DATA}}; const ratingDistData = {{RATING_DIST_DATA}}; const brandShareData = {{BRAND_SHARE_DATA}}; const sellerSourceData = {{SELLER_SO ...[truncated 3029 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not insert untrusted values through `innerHTML`. Create cells explicitly and assign their values with `textContent`: ```javascript function appendTextCell(row, value) { const cell = row.insertCell(); cell.textContent = String(value ?? ''); } top50Products.forEach((product, index) => { const row = tableBody.insertRow(); appendTextCell(row, index + 1); appendTextCell(row, product.asin); appendTextCell(row, product.title); appendTextCell(row, product.brand); appendTextCell(row, `$${product.price}`); appendTextCell(row, product.rating); appendTextCell(row, product.sales); appendTextCell(row, `${product.marketShare}%`); }); ``` 2. Store serialized data in a non-executable element: ```html <script id="report-data" type="application/json">{{REPORT_DATA}}</script> ``` Parse it using: ```javascript const reportData = JSON.parse( document.getElementById('report-data').textContent ); ``` 3. Before embedding JSON in HTML, escape HTML-significant and script-sensitive characters. At minimum, transform `<`, `>`, `&`, U+2028, and U+2029 into Unicode escape sequences. This prevents literal `</script>` sequences from appearing in the generated file. 4. Apply contextual HTML escaping to direct replacements such as category names, site names, ratings, and seller summaries. 5. Validate API response fields against a strict schema, including expected types and maximum lengths. 6. Add a restrictive Content Security Policy. Prefer a locally bundled script and avoid inline JavaScript so that `script-src 'self'` can be enforced without `unsafe-inline`. 7. Add regression tests using payloads containing `</script>`, event-handler attributes, HTML entities, quotes, backticks, and Unicode line separators. ]]>
