T09 · Insecure Skill Coding Practices
- Location
scripts/web_search.py:276- Finding
Server-Side Request Forgery Through Arbitrary URL Crawling
- Content
View full analysis
Dict[str, Any]: """Asynchronously crawl webpage content.""" if not HAS_CRAWL4AI: return { 'success': False, 'message': 'crawl4ai is not installed; unable to crawl webpages' } try: async with AsyncWebCrawler() as crawler: result = await crawler.arun(url=url) ``` The validation applied before reaching this sink is limited to the URL scheme and length: ```python def validate_url(url: str) -> tuple: """Validate URL.""" if not url: return False, 'URL cannot be empty' if not isinstance(url, str): return False, 'URL must be a string' url = url.strip() if len(url) == 0: return False, 'URL cannot be empty' if not url.startswith(('http://', 'https://')): return False, 'URL must begin with http:// or https://' if len(url) > 2000: return False, 'URL length cannot exceed 2000 characters' return True, '' ``` The crawl action passes the validated but otherwise unrestricted URL to the crawler: ```python elif action == 'crawl': url = kwargs.get('url', '') is_valid, error_msg = validate_url(url) if not is_valid: return {'success': False, 'message': error_msg} return crawl_page(url.strip()) ``` ### Technical Analysis The `crawl` action accepts an attacker-controlled HTTP or HTTPS URL and passes it to `AsyncWebCrawler.arun()`. Validation only confirms that the value is a string, uses an HTTP-based scheme, and is no longer than 2,000 characters. It does not reject: - IPv4 or IPv6 loopback destinations - RFC 1918 private networks - Lin ...[truncated 2008 chars]- Remediation
View remediation
