T09 · Insecure Skill Coding Practices
- Location
scripts/utils/resolver.py:7- Finding
Unvalidated URL Classification Enables Blind SSRF and Local-Network Requests
- Content
View full analysis
Vulnerability Details
File Location:
scripts/utils/resolver.py:7-22, 54-57
Vulnerability Type: Blind SSRF caused by unanchored platform URL matching
Risk Level: MediumVulnerable 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
View remediation
Remediation Suggestions
- Parse the URL with
urllib.parse.urlsplit()before performing any platform matching. - Normalize the hostname by lowercasing it, removing a trailing dot, and converting internationalized names to a canonical IDNA representation.
- 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) - Require HTTPS for supported public platform URLs unless a documented platform strictly requires another scheme.
- Reject URLs containing user-information, unexpected ports, malformed hostnames, or non-HTTP(S) schemes.
- Resolve destination addresses and reject loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 ranges.
- Revalidate the destination after DNS resolution and on every redirect to mitigate open-redirect and DNS-rebinding scenarios.
- Construct canonical platform URLs from validated identifiers instead of retaining and navigating to the original user-supplied URL.
- Add regression tests covering deceptive URLs, including:
http://127.0.0.1/youtube.com/@testhttps://youtube.com.attacker.example/@testhttps://attacker.example/?next=youtube.com/@test- URLs using credentials, alternate ports, IPv6 loopback, and redirect chains.
- Where deployment controls permit, enforce an outbound network allowlist so the Skill process can connect only to documented platform domains.
- Parse the URL with
