T09 · Insecure Skill Coding Practices
- Location
scripts/research_tools.py:149- Finding
Unsafe URL Substring Matching Allows Untrusted Sources to Be Classified as Authoritative
- Content
View full analysis
Tuple[bool, str]: """ 评估来源质量 Args: url: URL title: 标题 Returns: (是否高质量,质量等级) """ quality_indicators = { 'academic': ['.edu', '.ac.uk', 'nature.com', 'science.org', 'scholar.google.com'], 'authoritative': ['.gov', 'reuters.com', 'bbc.com', 'xinhuanet.com', 'people.com.cn'], 'media': ['caixin.com', 'thepaper.cn', '36kr.com', 'techcrunch.com'], 'social': ['bilibili.com', 'xiaohongshu.com', 'douyin.com'], } url_lower = url.lower() title_lower = title.lower() for quality, indicators in quality_indicators.items(): if any(indicator in url_lower for indicator in indicators): return True, quality # 检查标题质量 spam_keywords = ['广告', '点击这里', '免费领取', '震惊', '必看'] if any(keyword in title_lower for keyword in spam_keywords): return False, 'spam' return True, 'general' ``` ### Technical Analysis The source-quality check searches for trusted-domain indicators as substrings anywhere in the complete URL. It does not parse the URL, isolate and normalize the hostname, validate the scheme, or ensure that the hostname is the trusted domain or one of its legitimate subdomains. An attacker-controlled URL such as the following can therefore receive an authoritative classification: ```text https://reuters.com.attacker.example/fabricated-report https://fake-reuters.com/article https://attacker.example/news?source=reuters.com ``` The final `return True, 'general'` also accepts every source whose title does not contain one of five listed spam phrases. This is a permissive default and does not establish that an unknown source is trustworthy. ### Attack Path 1. An attacker publishes fabricate ...[truncated 1078 chars]- Remediation
View remediation
bool: host = host.lower().rstrip(".") trusted = trusted.lower().rstrip(".") return host == trusted or host.endswith("." + trusted) def classify_source(url: str): try: parsed = urlsplit(url) except ValueError: return False, "invalid" if parsed.scheme != "https" or not parsed.hostname: return False, "invalid" host = parsed.hostname.lower().rstrip(".") trusted_domains = { "academic": ["nature.com", "science.org", "scholar.google.com"], "authoritative": [ "reuters.com", "bbc.com", "xinhuanet.com", "people.com.cn", ], "media": ["caixin.com", "thepaper.cn", "36kr.com", "techcrunch.com"], "social": ["bilibili.com", "xiaohongshu.com", "douyin.com"], } for quality, domains in trusted_domains.items(): if any(is_domain_or_subdomain(host, domain) for domain in domains): return True, quality return False, "requires_manual_verification" ``` 5. Do not classify unknown sources as trusted by default. Return an explicit manual-verification state. 6. Validate redirect destinations before assigning a trust classification. 7. Add tests for deceptive hostnames, trusted names in paths or query strings, embedded credentials, malformed URLs, IP literals, mixed-case hosts, and trailing-dot hostnames. ]]>
