T09 · Insecure Skill Coding Practices
- Location
scripts/generate_spending_report.py:1238- Finding
Stored Script Injection Through Unescaped Transaction Data in the Generated HTML Report
- Content
View full analysis
const report = __PAYLOAD__; const poolKeys = ["all", "consumption", "habit"]; let currentPool = "habit"; ``` The placeholder is replaced with the result of `json.dumps()` without HTML-context escaping: ```python normalized = template.replace("{{", "{").replace("}}", "}") return normalized.replace("__PAYLOAD__", payload_json) def generate_report(alipay_path: Path, wechat_path: Path, output_path: Path) -> Path: transactions = [transaction for transaction in parse_alipay(alipay_path) + parse_wechat(wechat_path) if keep_spend(transaction)] report = build_report_payload(transactions, alipay_path, wechat_path) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(html_template(json.dumps(report, ensure_ascii=False)), encoding="utf-8") return output_path ``` ### Technical Analysis Merchant names, item descriptions, notes, payment methods, and other transaction-derived strings are attacker-influenced data. These values are serialized using `json.dumps()` and inserted directly into an HTML `< ...[truncated 2470 chars]- Remediation
View remediation
str: return ( json.dumps(value, ensure_ascii=False) .replace("&", "\\u0026") .replace("<", "\\u003c") .replace(">", "\\u003e") .replace("\u2028", "\\u2028") .replace("\u2029", "\\u2029") ) ``` Use it when generating the report: ```python payload_json = safe_json_for_html(report) output_path.write_text(html_template(payload_json), encoding="utf-8") ``` 2. Prefer separating data from executable JavaScript: ```html ``` The JSON must still escape `<` as `\u003c`, because the HTML parser recognizes `` regardless of the script element’s MIME type. 3. Avoid constructing report markup with `innerHTML` where practical. Create elements with `document.createElement()` and assign transaction-derived values through `textContent`. 4. Add a restrictive Content Security Policy. Move the dashboard JavaScript into a separately generated or static script and disallow inline scripts where feasible: ```html ``` For a fully self-contained report, use a nonce or hash-based policy instead of broadly allowing all inline scripts. 5. Add regression tests with transaction fields containing payloads such as: ```text <>& ``` Verify that the generated HTML contains `\u003c/script\u003e`, that no additional executable script element is create ...[truncated 71 chars]
