T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/utils/resolver.py:7
- Finding
- Unvalidated URL Classification Enables Blind SSRF and Local-Network Requests## Vulnerability Details **File Location**: `scripts/utils/resolver.py:7-22, 54-57` **Vulnerability Type**: Blind SSRF caused by unanchored platform URL matching **Risk Level**: Medium ### Vulnerable Code ```python PLATFORM_URL_PATTERNS: list[tuple[str, re.Pattern[str]]] = [ ("bilibili", re.compile(r"space\.bilibili\.com/(\d+)")), ("bilibili", re.compile(r"bilibili\.com/space/(\d+)")), ("youtube", re.compile(r"youtube\.com/(?:@|channel/|c/)([^/?&]+)")), ("douyin", re.compile(r"douyin\.com/user/([A-Za-z0-9_-]+)")), ("kuaishou", re.compile(r"kuaishou\.com/profile/([A-Za-z0-9_-]+)")), ("xiaohongshu", re.compile(r"xiaohongshu\.com/user/profile/([A-Za-z0-9]+)")), ("tiktok", re.compile(r"tiktok\.com/@([^/?&]+)")), ("instagram", re.compile(r"instagram\.com/([^/?&]+)")), ("toutiao", re.compile(r"toutiao\.com/c/user/token/([^/?&]+)")), ("baijiahao", re.compile(r"baijiahao\.baidu\.com/u\?app_id=(\d+)")), ("baijiahao", re.compile(r"author\.baidu\.com/home/(\d+)")), ("haokan", re.compile(r"haokan\.baidu\.com/author/(\d+)")), ("iqiyi", re.compile(r"iqiyi\.com/u/(\w+)")), ("iqiyi", re.compile(r"iqiyi\.com/creator/(\d+)")), ("wechat_video", re.compile(r"channels\.weixin\.qq\.com/([^/?&]+)")), ] def _resolve_url(url: str) -> ResolvedInput: for platform, pattern in PLATFORM_URL_PATTERNS: m = pattern.search(url) if m: return ResolvedInput(platform=platform, uid=m.group(1), url=url) parsed = urlparse(url) raise ValueError( f"Unsupported platform URL: {parsed.netloc}. " f"Supported platforms: bilibili, youtube, douyin, kuaishou, " f"xiaohongshu, tiktok, instagram, toutiao, baijiahao, haokan, iqiyi, wechat_video" ) ``` The resulting original URL is subsequently forwarded to a platform scraper. For example, YouTube navigation occurs at `scripts/platforms/youtube.py ...[truncated 3129 chars]
- Remediation
- ## Remediation Suggestions 1. Parse the URL with `urllib.parse.urlsplit()` before performing any platform matching. 2. Normalize the hostname by lowercasing it, removing a trailing dot, and converting internationalized names to a canonical IDNA representation. 3. Match the parsed hostname against an explicit allowlist. Accept either the exact domain or a controlled subdomain using a boundary-safe comparison such as: ```python def host_matches(host: str, allowed: str) -> bool: return host == allowed or host.endswith("." + allowed) ``` 4. Require HTTPS for supported public platform URLs unless a documented platform strictly requires another scheme. 5. Reject URLs containing user-information, unexpected ports, malformed hostnames, or non-HTTP(S) schemes. 6. Resolve destination addresses and reject loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 ranges. 7. Revalidate the destination after DNS resolution and on every redirect to mitigate open-redirect and DNS-rebinding scenarios. 8. Construct canonical platform URLs from validated identifiers instead of retaining and navigating to the original user-supplied URL. 9. Add regression tests covering deceptive URLs, including: - `http://127.0.0.1/youtube.com/@test` - `https://youtube.com.attacker.example/@test` - `https://attacker.example/?next=youtube.com/@test` - URLs using credentials, alternate ports, IPv6 loopback, and redirect chains. 10. Where deployment controls permit, enforce an outbound network allowlist so the Skill process can connect only to documented platform domains.
