T09 · Insecure Skill Coding Practices
- Location
assets/patrol_report_template.html:232- Finding
Stored HTML and JavaScript Injection in Generated Patrol Reports
- Content
View full analysis
Vulnerability Details
File Location:
assets/patrol_report_template.html:232-304
Related Data Flow:scripts/smart_store_inspection.py:768-769, 833-844
Vulnerability Type: Stored HTML injection / DOM-based cross-site scripting
Risk Level: MediumVulnerable Code
The report generator retrieves records from the remote API and embeds the resulting data in an executable HTML report:
python # scripts/smart_store_inspection.py:768-769 print(f"[info] Fetching AI patrol records ({args.begin} ~ {args.end}) ...") records = _fetch_records(body)python # scripts/smart_store_inspection.py:833-844 with open(template_path, "r", encoding="utf-8") as f: html = f.read() payload = json.dumps(report, ensure_ascii=False).replace("</", "<\\/") marker = "window.__REPORT__ = null;" if marker not in html: print("[error] Template is missing the data marker.", file=sys.stderr) sys.exit(1) html = html.replace(marker, f"window.__REPORT__ = {payload};") with open(args.output, "w", encoding="utf-8") as f: f.write(html)The report template subsequently interpolates API-returned values into HTML markup:
javascript // assets/patrol_report_template.html:232-236 function tableize(el, cols, rows) { if (!rows.length) { el.innerHTML = '<div class="empty">(无数据)</div>'; return; } const head = cols.map(c => `<th>${c.label}</th>`).join(''); const body = rows.map(r => '<tr>' + cols.map(c => `<td>${r[c.key] != null ? r[c.key] : '-'}</td>`).join('') + '</tr>').join(''); el.innerHTML = `<div class="table-wrap"><table class="detail"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table></div>`; }javascript // assets/patrol_report_template.html:252-263 (R.records||[]).forEach(r => { const tr = document.createElement('tr'); const pic = r.cloudPictureUrl ? `<a href="${r.cloudPictureUrl}" target="_blank" style="color:var(--primary);">查看</a>` : '<span style="color:#9ca3af;">无</span>'; const detail = r.detailDat ...[truncated 3640 chars]- Remediation
View remediation
Remediation Suggestions
-
Replace dynamic
innerHTMLconstruction with DOM APIs:- Create elements using
document.createElement. - Insert remote text using
textContent. - Set validated attributes using
setAttributeor corresponding DOM properties.
- Create elements using
-
Validate
cloudPictureUrlbefore use:- Parse it with
new URL(...). - Permit only expected
https:URLs. - Optionally restrict hosts to the expected image service.
- Reject active or unexpected schemes such as
javascript:,data:, andfile:.
- Parse it with
-
Apply context-aware escaping if HTML-string generation cannot be removed:
- Escape
&,<,>,", and'for text and attribute contexts. - Do not rely on the Python
</replacement, because it only protects the initial script embedding.
- Escape
-
Add a restrictive Content Security Policy to generated reports, for example by disallowing inline script and limiting image and connection destinations. Move the template’s inline JavaScript to a separately generated resource or use a nonce/hash if a self-contained report is required.
-
Add regression tests containing malicious values in every API-derived field, including:
- HTML elements with event handlers.
- Quotes that escape attributes.
- Closing tags.
javascript:anddata:URLs.- SVG-based payloads.
-
Treat every API response field as untrusted even if the backend normally validates it, because device metadata, integrations, or compromised upstream records may introduce attacker-controlled content.
-
