T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_headlines.py:25- Finding
Unrestricted URL Fetching Enables SSRF and Local Resource Access
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch_headlines.py, lines 25–27, 61, and 89
Vulnerability Type: Server-Side Request Forgery (SSRF) and local resource access
Risk Level: MediumVulnerable Code
python def fetch_html(url: str, timeout: int) -> str: with request.urlopen(url, timeout=timeout) as response: # noqa: S310 (trusted URL) return response.read().decode("utf-8", errors="ignore")python parser.add_argument("--url", default=DEFAULT_URL, help="Ziel-URL (Standard: %(default)s)")python html_content = fetch_html(args.url, args.timeout)Technical Analysis
The
--urlcommand-line argument accepts an arbitrary URL and passes it directly tourllib.request.urlopen. Contrary to the inlinetrusted URLcomment, the value is not restricted to the declaredhttps://www.tageblatt.de/source.No validation is applied to:
- The URL scheme
- The destination hostname
- Redirect destinations
- Embedded URL credentials
- Resolved loopback, link-local, private, or reserved IP addresses
Consequently, an attacker who can control the script's arguments may direct the process to HTTP services on internal networks or localhost. Depending on the schemes supported by the runtime's URL handlers, local resources such as
file:URLs may also be accessed.The fetched content is processed by
extract_headlines. Content matching the expected heading pattern can then be printed to standard output and written to an attacker-selected output path. This makes response-data extraction possible when the targeted resource contains, or can be made to return, matching markup.Attack Path
- The attacker gains the ability to influence arguments supplied to
fetch_headlines.py, directly or through an automation workflow that incorporates untrusted input. - The attacker supplies a crafted
--urltargeting a loopback service, private-network endpoint, cloud metadata-like endpoint, or supported local-resource U ...[truncated 1219 chars]
- Remediation
View remediation
Remediation Suggestions
- Remove the
--urloption if alternate sources are not a required feature, and always use the fixedDEFAULT_URL. - If configurability is required, parse URLs with
urllib.parse.urlsplitand enforce:- The
httpsscheme only - An exact allowlist such as
www.tageblatt.de - No embedded username or password
- An expected port, normally
443
- The
- Resolve the hostname and reject loopback, private, link-local, multicast, unspecified, and reserved addresses using Python's
ipaddressmodule. - Disable automatic redirects or validate every redirect destination with the same scheme, host, and resolved-address policy.
- Reject non-HTTP schemes explicitly, including
file,ftp, anddata. - Apply response-size limits before reading the complete body to reduce denial-of-service risk.
- Replace the misleading
# noqa: S310 (trusted URL)suppression with validation that establishes the trust boundary beforeurlopenis called. - Add security tests covering localhost URLs, private IP addresses, alternative schemes, URL credentials, DNS resolution to private addresses, and redirects to prohibited destinations.
- Remove the
