T09 · Insecure Skill Coding Practices
- Location
scripts/linz_transport.py:23- Finding
Unrestricted API Base URL Enables Server-Side Request Forgery
- Content
View full analysis
str: base = base_url.rstrip("/") return f"{base}/{path.lstrip('/')}" def http_get_json(url: str, timeout: int) -> Any: req = urllib.request.Request(url=url, method="GET") try: with urllib.request.urlopen(req, timeout=timeout) as resp: body = resp.read().decode("utf-8") return json.loads(body) except urllib.error.HTTPError as err: raise RuntimeError(f"HTTP {err.code} for {url}") from err except urllib.error.URLError as err: raise RuntimeError(f"Network error for {url}: {err.reason}") from err except json.JSONDecodeError as err: raise RuntimeError(f"Invalid JSON from {url}: {err}") from err def query_efa(base_url: str, endpoint: str, params: dict[str, Any], timeout: int) -> dict[str, Any]: query = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None}) url = build_url(base_url, f"/efa/{endpoint}?{query}") payload = http_get_json(url, timeout) if not isinstance(payload, dict): raise RuntimeError(f"Expected JSON object from {endpoint}") return payload ``` ```python parser.add_argument( "--base-url", default=os.environ.get("LINZ_TRANSPORT_API_BASE_URL", DEFAULT_BASE_URL), help=( "API base URL. Defaults to LINZ_TRANSPORT_API_BASE_URL or " f"{DEFAULT_BASE_URL}." ), ) ``` The corresponding behavior is explicitly documented in `SKILL.md:26-29, 35-38`, which permits the base URL to be supplied through either a command-line argument or an environment variable. ### Technical Analysis The script accepts a caller-controlled base URL and passes the resulting URL directly to `urllib.request.urlopen`. It does not ...[truncated 2338 chars]- Remediation
View remediation
