T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/hnews.py:243
- Finding
- Unvalidated URL Schemes in Generated HTML Links<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hnews.py`, lines 243–264 **Vulnerability Type**: Unvalidated URL scheme leading to unsafe client-side navigation **Risk Level**: Medium ### Complete Code Snippet ```python def build_card(item: dict) -> str: """生成单条新闻的 HTML 卡片。""" e = html_mod.escape title = e(item['title']) url = e(item['url']) rank = item['rank'] source_html = '' if item['source']: source_html = f'<span class="card-source">{e(item["source"])}</span>' meta_parts = [] if item['points']: meta_parts.append(f'<span class="points">{item["points"]} points</span>') if item['author']: meta_parts.append(f'<span class="author">@{item["author"]}</span>') if item['comments']: meta_parts.append(f'<span class="comments">{item["comments"]} 评论</span>') meta_html = '\n '.join(meta_parts) return f'''\ <a class="news-card" href="{url}" target="_blank" rel="noopener"> ``` The URL originates from Markdown input at line 43: ```python url = m.group(3) ``` ### Technical Analysis The converter copies a URL from an input Markdown document into an HTML `href` attribute. Although `html.escape()` prevents the URL from breaking out of the quoted attribute, it does not validate or restrict the URL scheme. Consequently, input containing a scheme such as `javascript:` can remain an executable or otherwise unsafe browser URL in the generated document. Other unwanted schemes, including crafted `data:` URLs, may facilitate deceptive navigation or delivery of attacker-controlled content, depending on browser restrictions. For example, an attacker-controlled entry could contain: ```markdown 1. [Malicious News](javascript:alert(document.domain)) (example.com) 10 points | attacker | 1 comment ``` The generated card would preserve the unsafe scheme: ```html <a class="news-card" href="javascript:alert(document.domain)" target="_blank" rel="noopener"> ``` `rel=" ...[truncated 1441 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Validate URLs before placing them into HTML: 1. Parse each URL with `urllib.parse.urlsplit`. 2. Permit only an explicit allowlist of required schemes, preferably `https` and, if necessary, `http`. 3. Require a valid network location for HTTP and HTTPS URLs. 4. Reject, omit, or replace links using `javascript:`, `data:`, `file:`, `vbscript:`, or unknown schemes. 5. Continue applying `html.escape(..., quote=True)` after validation. 6. Consider adding a restrictive Content Security Policy as defense in depth. Example hardening: ```python from urllib.parse import urlsplit def validate_news_url(raw_url: str) -> str: url = raw_url.strip() parsed = urlsplit(url) if parsed.scheme.lower() not in {'https', 'http'}: raise ValueError(f'Unsupported URL scheme: {parsed.scheme or "missing"}') if not parsed.netloc: raise ValueError('The URL must contain a valid host') return url ``` Apply validation when parsing each item: ```python try: url = validate_news_url(m.group(3)) except ValueError as exc: print(f'Warning: skipping unsafe URL: {exc}', file=sys.stderr) i += 1 continue ``` At the rendering boundary, retain contextual escaping: ```python url = html_mod.escape(item['url'], quote=True) ``` A defense-in-depth policy can also be emitted in the document head: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src https: data:; navigate-to https: http:"> ``` The exact policy should be tested against supported browsers and the intended standalone-file workflow. ]]>
