T09 · Insecure Skill Coding Practices
- Location
scripts/generate_report.py:104- Finding
Unescaped Markdown Content Enables HTML Injection and Server-Side Resource Requests
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_report.py:104-214, 290-296
Vulnerability Type: Unescaped HTML generation from externally sourced content
Risk Level: MediumVulnerable Code
python def c(s): return re.sub(r"\*\*(.+?)\*\*", r"\1", s).strip()python def r_app(rows): h="" for r in rows: if len(r)<5: continue n,ra,rv,ca,po=r[0],r[1],r[2],r[3],r[4] h+="<tr><td><strong>"+n+"</strong></td><td><span class=\"tg tg-b\">&#x2b50; "+ra+"</span></td><td>"+rv+"</td><td>"+ca+"</td><td>"+po+"</td></tr>\n" return hpython def r_ops(rows): h="" for r in rows: if len(r)<6: continue n,mau,rv,vl,dr=r[0],r[1],r[2],r[3],r[4] dyn=c(" ".join(r[5:]))[:50] cls="wr" if "\u672a\u516c\u5f00" in mau else "" h+="<tr><td><strong>"+n+"</strong></td><td class=\""+cls+"\">"+mau+"</td><td>"+rv+"</td><td>"+vl+"</td><td>"+dr+"</td><td style=\"font-size:10px;color:#64748b\">"+dyn+"...</td></tr>\n" return hpython html = build_html(d, template) with open(html_file, "w", encoding="utf-8") as f: f.write(html) WPHtml(string=html, base_url="/").write_pdf(pdf_file)Technical Analysis
The report generator parses Markdown containing market data collected from external websites and directly concatenates parsed fields into HTML. The helper function
c()only strips Markdown bold markers; it does not apply HTML entity encoding or sanitization.This unsafe pattern affects application names, ratings, categories, product descriptions, operational metrics, market descriptions, events, opportunities, risks, personas, and strategy text. Consequently, HTML elements supplied throu ...[truncated 2932 chars]
- Remediation
View remediation
Remediation Suggestions
-
Apply contextual HTML escaping to every untrusted scalar value before inserting it into markup:
python from html import escape def safe_text(value): return escape(str(value), quote=True)Use
safe_text()for every Markdown-derived field, including names, metrics, descriptions, events, opportunities, risks, personas, and strategy entries. -
Replace manual string concatenation with an auto-escaping template engine such as Jinja2:
python from jinja2 import Environment, FileSystemLoader, select_autoescape env = Environment( loader=FileSystemLoader(template_directory), autoescape=select_autoescape(["html", "xml"]), )Only mark fixed, application-generated components as safe after their constituent values have been escaped.
-
If limited inline formatting is required, parse and sanitize it with a strict allowlist. Reject resource-bearing and layout-breaking elements, including
img,link,style,iframe,object,embed, and SVG-related elements. Remove URL-bearing attributes unless explicitly required. -
Configure a custom WeasyPrint
url_fetcherthat:- Allows only explicitly approved HTTPS hosts.
- Rejects
file:,ftp:,data:, and other unnecessary schemes. - Rejects loopback, private, link-local, multicast, and reserved IP ranges.
- Revalidates the resolved address after DNS resolution and redirects.
- Applies strict response-size and timeout limits.
-
Bundle required fonts locally and remove remote stylesheet imports so normal report generation does not require external network access.
-
Run PDF generation in a sandbox with no access to credentials or sensitive files and with outbound network access disabled unless strictly necessary.
-
Add regression tests using payloads in every parsed field. Verify that angle brackets are emitted as entities and that PDF generation prod ...[truncated 36 chars]
-
