T09 · Insecure Skill Coding Practices
- Location
scripts/build_digest.py:100- Finding
Unrestricted Feed Fetching Enables Server-Side Request Forgery and Local File Access
- Content
View full analysis
Vulnerability Details
File Location:
scripts/parse_opml.py:11-17,scripts/build_digest.py:10-18, andscripts/build_digest.py:100-103
Vulnerability Type: Server-Side Request Forgery (SSRF) and unsafe URL scheme handling
Risk Level: HighVulnerable Code
scripts/parse_opml.py:11-17:python for o in root.findall('.//outline'): url = o.attrib.get('xmlUrl') or o.attrib.get('url') if not url: continue title = o.attrib.get('text') or o.attrib.get('title') or url feeds.append((title.strip(), url.strip()))scripts/build_digest.py:10-18:python def read_feeds(path): feeds = [] with open(path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if not line or '\t' not in line: continue title, url = line.split('\t', 1) feeds.append((title, url)) return feedsscripts/build_digest.py:100-103:python req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) with urllib.request.urlopen(req, timeout=12) as r: xml_text = r.read().decode('utf-8', errors='ignore') feed_ok += 1Technical Analysis
Feed URLs originate from user-supplied OPML or a feed-list file and are passed directly to
urllib.request.urlopen. The implementation does not validate the URL scheme, hostname, resolved IP address, port, or redirect destination.Consequently, an attacker can supply URLs targeting loopback interfaces, private networks, link-local services, or cloud metadata endpoints. Because
urllib.requestsupports URL schemes beyond HTTP and HTTPS, afile://URL may also read a locally accessible XML file. Automatic redirects can bypass a superficial hostname check unless every redirect target is independently validated.The 12-second timeout and
--max-feedslimit constrain request duration and count but do not prevent access to prohibited destinations. Any retrieved content that is valid RSS, A ...[truncated 1817 chars]- Remediation
View remediation
Remediation Suggestions
-
Restrict URL schemes
- Parse every URL with
urllib.parse.urlsplit. - Permit only
https, orhttpwhere explicitly required. - Reject
file,ftp,data, and all other schemes. - Reject URLs containing embedded usernames or passwords.
- Parse every URL with
-
Block prohibited destinations
- Resolve the hostname before connecting.
- Use Python's
ipaddressmodule to reject loopback, private, link-local, multicast, reserved, and unspecified addresses. - Validate every returned DNS address rather than accepting the first safe-looking result.
- Explicitly block known cloud metadata destinations.
-
Control redirects
- Disable automatic redirects or use a custom redirect handler.
- Reapply complete scheme, hostname, port, DNS, and IP validation to every redirect target.
- Set a low maximum redirect count.
-
Reduce DNS rebinding exposure
- Ensure the address validated is the address used for the connection.
- Where the deployment permits it, enforce outbound network policy at the container, firewall, or proxy layer.
-
Constrain response processing
- Enforce an allowlist of expected XML content types where practical.
- Stream responses and stop after a configured maximum byte count rather than calling unbounded
read(). - Apply strict connection and read timeouts.
-
Prefer an explicit trust model
- Require user confirmation for newly imported domains.
- Consider a domain allowlist for approved public feed providers.
- Treat OPML and feed-list files as untrusted input.
-
Add security tests
- Verify rejection of
file://, loopback, private, link-local, IPv6-local, encoded-IP, credential-bearing, and nonstandard-port URLs. - Test public-to-private redirects and hostnames resolving to mixed public/private addresses.
- Verify rejection of
-
