T09 · Insecure Skill Coding Practices
- Location
scripts/render_report.py:143- Finding
Untrusted API URLs Permit Active Content Injection in Generated HTML Reports
- Content
View full analysis
Vulnerability Details
File Location:
scripts/render_report.py, lines 143–144
Vulnerability Type: HTML attribute injection and unsafe URL scheme handling
Risk Level: MediumVulnerable Code
python def _link(text, url): return f'<a href="{esc(url)}" target="_blank">{esc(text)}</a>' if url else esc(text)The
esc()helper used here is based onxml.sax.saxutils.escape:python def esc(s) -> str: return _esc(str(s if s is not None else ""))Technical Analysis
The report renderer incorporates URLs from API-derived report records into an HTML
hrefattribute. The_link()function appliesxml.sax.saxutils.escape, which escapes&,<, and>by default but does not escape quotation marks. Consequently, a URL containing a double quote can terminate thehrefattribute and inject additional HTML attributes.For example, a value resembling:
text https://example.invalid/" onclick="alert(document.domain)would produce markup structurally equivalent to:
html <a href="https://example.invalid/" onclick="alert(document.domain)" target="_blank">...</a>The function also performs no URL-scheme validation. A
javascript:URL can therefore remain active and execute when the report viewer clicks the link.This sink is reachable from several report fields, including URLs in
top_picks,proposed,intent,expiring, andcitations.items. The documented workflow instructs the agent to preserve API-returned URLs unchanged when generating the HTML report, so a malicious or compromised upstream data record can reach this renderer without local user authorship.Attack Path
- An upstream API or data provider returns a procurement record whose
urlfield contains either:- a double quote followed by an injected event attribute; or
- an active scheme such as
javascript:.
- The agent copies the API-returned URL into the JSON report input as required by the Skill workflow.
- Full scan mo ...[truncated 1152 chars]
- An upstream API or data provider returns a procurement record whose
- Remediation
View remediation
Remediation Suggestions
- Escape attribute values with quote-aware HTML escaping:
python from html import escape def esc_text(value) -> str: return escape(str(value if value is not None else ""), quote=False) def esc_attr(value) -> str: return escape(str(value if value is not None else ""), quote=True)- Validate every URL before rendering it. Permit only
httpsURLs and, where compatible with the business requirement, restrict hosts to the expected platform domains:
python from urllib.parse import urlparse ALLOWED_HOSTS = { "www.zhiliaobiaoxun.com", "ai.zhiliaobiaoxun.com", "agent.zhiliaobiaoxun.com", } def safe_url(value) -> str | None: if not isinstance(value, str): return None value = value.strip() parsed = urlparse(value) if parsed.scheme != "https": return None if parsed.hostname not in ALLOWED_HOSTS: return None return value- Render rejected or malformed URLs as plain text rather than as clickable links:
python def _link(text, url): validated = safe_url(url) if not validated: return esc_text(text) return ( f'<a href="{esc_attr(validated)}" ' f'target="_blank" rel="noopener noreferrer">' f'{esc_text(text)}</a>' )-
Apply the same validation consistently to every URL-bearing collection, including citation entries.
-
Add regression tests for:
- double and single quotation marks;
javascript:,data:, andfile:schemes;- encoded or mixed-case active schemes;
- malformed URLs and user-info host confusion;
- valid signed HTTPS URLs containing ordinary query parameters.
