T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate_report.py:460
- Finding
- Generated HTML reports allow attacker-controlled markup and unsafe link schemes<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py`, lines 460-548 **Vulnerability Type**: Stored HTML injection and unsafe URL handling **Risk Level**: Medium ### Vulnerable Code ```python ROW_TEMPLATE = """ <tr> <td><span class="rank-badge {rank_class}">{rank}</span></td> <td><a href="{profile_url}" target="_blank" class="account-name" title="点击查看小红书主页">{account_name}</a></td> <td><span class="score">{score}</span></td> <td>{followers}</td> <td>{new_notes}</td> <td class="interaction">{new_fans}</td> <td class="interaction">{new_likes}</td> <td class="interaction">{new_comments}</td> <td class="interaction">{new_collects}</td> <td class="interaction">{new_shares}</td> </tr>""" def _fmt(val, fmt_fn=None) -> str: if val is None or val == "" or val == "-": return "-" if isinstance(val, str): return val if val.strip() else "-" try: n = int(val) if n == 0: return "-" return fmt_fn(n) if fmt_fn else str(n) except (TypeError, ValueError): return str(val) if val else "-" rows.append(ROW_TEMPLATE.format( rank=rank, rank_class=rank_class, account_name=html_utils.escape(item.get("accountName", "")), profile_url=html_utils.escape( item.get("accountLink") or item.get("profileUrl", "#") ), followers=_fmt(item.get("followers"), format_followers), new_notes=item.get("newNoteCount", "-") or "-", new_fans=_fmt(item.get("newFans"), format_interaction), new_likes=_fmt(item.get("newLikes"), format_interaction), new_comments=_fmt(item.get("newComments"), format_interaction), new_collects=_fmt(item.get("newCollects"), format_interaction), new_shares=_fmt(item.get("newShares"), format_interaction), score=int(item.get("comprehensiveScore")) if item.get("comprehensiveScore") else "-", )) ``` ### Technical Analysis The report generator inser ...[truncated 2503 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Escape every value inserted into HTML, not only account names: ```python def escape_text(value) -> str: return html_utils.escape(str(value), quote=True) followers = escape_text(_fmt(item.get("followers"), format_followers)) new_notes = escape_text(item.get("newNoteCount", "-") or "-") new_fans = escape_text(_fmt(item.get("newFans"), format_interaction)) new_likes = escape_text(_fmt(item.get("newLikes"), format_interaction)) new_comments = escape_text(_fmt(item.get("newComments"), format_interaction)) new_collects = escape_text(_fmt(item.get("newCollects"), format_interaction)) new_shares = escape_text(_fmt(item.get("newShares"), format_interaction)) ``` 2. Validate profile URLs structurally and allow only expected HTTPS destinations: ```python from urllib.parse import urlparse def safe_profile_url(value: str) -> str: try: parsed = urlparse(value) allowed_hosts = {"www.xiaohongshu.com", "xiaohongshu.com"} if parsed.scheme == "https" and parsed.hostname in allowed_hosts: return html_utils.escape(value, quote=True) except (TypeError, ValueError): pass return "#" ``` 3. Add `rel="noopener noreferrer"` to links opened with `target="_blank"`: ```html <a href="{profile_url}" target="_blank" rel="noopener noreferrer" class="account-name"> ``` 4. Validate API and JSON fields against an explicit schema. Numeric metrics should be accepted only as numbers or narrowly defined numeric strings such as `123`, `12.3w`, or `-`. 5. Add automated tests using payloads containing `<script>`, event-handler attributes, quotes, encoded markup, and `javascript:` URLs. 6. Consider using a templating engine with automatic HTML escaping enabled rather than manually formatting HTML strings. ]]>
