T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/searxng.py:205
- Finding
- Server-Side Request Forgery Through Unrestricted Instance and Search Result URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/searxng.py:129-133`, `scripts/searxng.py:205-218`, `scripts/searxng.py:297-299`, `scripts/searxng.py:328-329`, `scripts/searxng.py:361-366` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python # Resolve instance URL: arg > skill-config.json > default if base_url: self.base_url = base_url.rstrip("/") else: cfg_url = cfg.get("default_instance", "").strip() self.base_url = cfg_url.rstrip("/") if cfg_url else self.DEFAULT_INSTANCE ``` ```python def _fetch_full_content(self, url: str, max_chars: int = 4000) -> str: """Fetch a URL and return stripped plain text, up to max_chars.""" if not url: return "" try: self._wait_for_rate_limit() resp = self.session.get(url, timeout=8, allow_redirects=True) resp.raise_for_status() ct = resp.headers.get("Content-Type", "") if "html" not in ct and "text" not in ct: return "" extractor = _TextExtractor() extractor.feed(resp.text) return extractor.get_text(max_chars) except Exception as e: print(f"Warning: could not fetch {url}: {e}", file=sys.stderr) return "" ``` ```python url = f"{self.base_url}/search" response = self.session.get(url, params=params, timeout=10) response.raise_for_status() data = response.json() ``` ```python for r in data.get("results", data.get("items", [])): url = r.get("url", r.get("link", "")) fetched = self._fetch_full_content(url) if full_content else "" ``` ```python parser.add_argument( "--instance", default=None, metavar="URL", help="SearXNG instance URL (overrides config)", ) ``` ### Technical Analysis The client accepts an arbitrary SearXNG instance through `--instance` or `skill-config.json` without validating the URL scheme, hostname, resolved IP address, or destination network. It then sends a request to the supplied destin ...[truncated 2124 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only `https` URLs by default and reject unsupported schemes, embedded credentials, malformed hosts, and ambiguous URL forms. 2. Resolve destination hostnames before every request and reject all loopback, private, link-local, multicast, unspecified, reserved, and documentation IP ranges for both IPv4 and IPv6. 3. Explicitly block known metadata destinations, including link-local metadata addresses and provider-specific metadata hostnames. 4. Disable automatic redirects or validate the scheme, hostname, and resolved address of every redirect target before following it. 5. Restrict `--instance` to an administrator-defined allowlist of trusted SearXNG hosts where possible. 6. Apply the same validation to every result URL before `_fetch_full_content()` is called. 7. Set strict response-size limits and stream responses instead of loading an unrestricted body through `resp.text`. 8. Do not cache content retrieved by `--full-content`, particularly when its destination is not explicitly trusted. 9. Consider isolating outbound requests in a sandbox with network policy that prevents access to internal and metadata networks. ]]>
