T09 · Insecure Skill Coding Practices
- Location
scripts/year_report_api.py:232- Finding
Unrestricted URL Fetch Enables Server-Side Request Forgery
- Content
View full analysis
bytes: req_headers = {"User-Agent": DEFAULT_UA, **(headers or {})} req = urllib.request.Request(url, data=data, headers=req_headers, method=method) try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read() except urllib.error.HTTPError as exc: body = exc.read().decode("utf-8", errors="replace")[:1200] raise SystemExit(f"HTTP {exc.code} for {url}\n{body}") from exc except (urllib.error.URLError, TimeoutError) as exc: raise SystemExit(f"Request failed for {url}: {exc}") from exc ``` ```python def cmd_download(args: argparse.Namespace) -> None: body = request(args.url, headers={"User-Agent": DEFAULT_UA}, timeout=60) with open(args.output, "wb") as f: f.write(body) emit("Direct public file download", args.url, {"output": args.output, "bytes": len(body)}) ``` ```python p = sub.add_parser("download", help="Download a public annual-report PDF URL returned by a source.") p.add_argument("url") p.add_argument("--output", required=True) p.set_defaults(func=cmd_download) ``` ### Technical Analysis The `download` subcommand passes the CLI-provided URL directly to `urllib.request.urlopen`. The implementation does not validate: - The URL scheme. - The destination hostname. - The resolved IP address. - Whether the address belongs to a loopback, private, link-local, reserved, or cloud metadata network. - Redirect destinations. - Whether the destination belongs to an approved official disclosure service. - The response content type or maximum respo ...[truncated 2346 chars]- Remediation
View remediation
