T09 · Insecure Skill Coding Practices
- Location
scripts/verify_facts.py:427- Finding
Server-Side Request Forgery in Source Verification
- Content
View full analysis
Vulnerability Details
File Location:
scripts/verify_facts.py, lines 427–438 and 454–459
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: MediumVulnerable code:
python def fetch_source_text(url: str, timeout: float = 20.0) -> tuple[str, str]: """Текст страницы источника. Возвращает (текст, причина отказа).""" timeout = min(timeout, time_left()) if timeout < 3: return "", "не хватило времени до дедлайна" try: req = urllib.request.Request(url, headers={ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36", "Accept-Language": "ru,en;q=0.8", }) with urllib.request.urlopen(req, timeout=timeout) as resp: raw = resp.read(1_500_000)python urls = [str((srcmap.get(str(n)) or {}).get("url") or "") for n in claim.get("sources") or []] urls = [u for u in urls if u.startswith("http")] if not urls: return "НЕ ПРОВЕРИТЬ", "у источника нет адреса" reasons = [] for url in urls[:2]: text, err = fetch_source_text(url)Technical Analysis
verify_facts.pyextracts source URLs from the dossier JSON and directly passes them tourllib.request.urlopen. The only URL restriction is a string-prefix check forhttp, which does not establish that the destination is a public HTTP or HTTPS service.There is no validation of:
- The URL scheme after parsing.
- Loopback addresses such as
127.0.0.1or::1. - Private network ranges.
- Link-local addresses and cloud metadata endpoints.
- Reserved, multicast, or otherwise non-public addresses.
- DNS results before connection.
- Redirect destinations followed by
urlopen. - DNS rebinding between validation and connection.
The source URL is part of dossier research data and can originate from externally sou ...[truncated 2013 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse URLs with
urllib.parse.urlsplitand allow only exacthttpandhttpsschemes. - Resolve the hostname before connecting and reject every resolved address in loopback, private, link-local, multicast, unspecified, reserved, and non-global ranges for both IPv4 and IPv6.
- Disable automatic redirects, or validate every redirect destination using the same scheme, hostname, DNS, and IP-address policy before following it.
- Address DNS rebinding by ensuring the validated IP is the address used for the connection, or by enforcing equivalent restrictions through a trusted egress proxy.
- Reject URLs containing ambiguous hostname encodings, embedded credentials, malformed ports, or unsupported address representations.
- Consider an allowlist of expected public source domains if the workflow permits it.
- Apply outbound firewall rules that prevent the Skill runtime from reaching internal and metadata networks.
- Return generic fetch failures so internal reachability cannot be distinguished through detailed network errors.
- Add tests covering direct and redirected requests to IPv4 and IPv6 loopback, RFC1918 private ranges, link-local ranges, integer or encoded IP representations, and DNS names resolving to non-public addresses.
- Parse URLs with
