T09 · Insecure Skill Coding Practices
- Location
scripts/ares_client.py:440- Finding
Unrestricted API Base URL Enables SSRF-Like Outbound Requests
- Content
View full analysis
Vulnerability Details
File Location:
scripts/ares_client.py, lines 272–273, 346–347, 353–367, and 440
Vulnerability Type: Unrestricted network destination / server-side request forgery
Risk Level: MediumThe client exposes an undocumented
--basecommand-line option whose value is used directly to construct outbound request URLs. The code does not require HTTPS, restrict the destination toares.gov.cz, reject private or loopback addresses, or validate redirect destinations.python def fetch_pravni_forma_from_ares(base: str) -> dict[int, str]: url = f"{base.rstrip('/')}/ciselniky-nazevniky/vyhledat" payload = http_json("POST", url, payload={"kodCiselniku": "PravniForma"}) mapping: dict[int, str] = {} extract_codelist_pairs(payload, mapping) if mapping: return mapping raise AresError("Failed to decode codelist PravniForma", status=200, details={"url": url})python def fetch_ico(base: str, ico: str) -> dict[str, Any]: return http_json("GET", f"{base.rstrip('/')}/ekonomicke-subjekty/{ico}")python def fetch_search( base: str, name: str | None, city: str | None, nace: list[str] | None, limit: int, offset: int, ) -> dict[str, Any]: body: dict[str, Any] = { "pocet": limit, "start": offset, "razeni": [DEFAULT_SORT], } if name: body["obchodniJmeno"] = name if city: body["sidlo"] = {"nazevObce": city} if nace: body["czNace"] = nace if not name and not nace: raise ValidationError("search requires at least --name or --nace") return http_json("POST", f"{base.rstrip('/')}/ekonomicke-subjekty/vyhledat", payload=body)python parser.add_argument("--base", default=DEFAULT_BASE, help=f"ARES base URL (default: {DEFAULT_BASE})")Technical Analysis
User-controlled
args.basereaches the URL ...[truncated 2716 chars]- Remediation
View remediation
Remediation Suggestions
- Remove the
--baseoption from production-facing commands if custom endpoints are not a genuine operational requirement. - If endpoint configurability is required, parse the URL with
urllib.parse.urlsplitand enforce all of the following:- The scheme must be
https. - The hostname must exactly equal
ares.gov.cz. - Embedded user information must be rejected.
- Unexpected ports must be rejected.
- URL fragments and ambiguous malformed URLs must be rejected.
- The scheme must be
- Disable automatic redirects or implement a redirect handler that validates every redirect destination against the same scheme, hostname, and port allowlist.
- Perform validation immediately before each network request so no alternative call path can bypass it.
- If non-production endpoints are necessary for tests, expose them only through a clearly gated development mechanism, such as a test-only dependency injection interface, rather than a general command-line option.
- Add automated negative tests covering HTTP URLs, loopback addresses, private-network addresses, embedded credentials, alternate ports, hostname confusion, and redirects to prohibited destinations.
- Document the permitted outbound destination and ensure deployment-level egress controls independently restrict this Skill to the official ARES host.
- Remove the
