T09 · Insecure Skill Coding Practices
- Location
scripts/daily_brief.py:122- Finding
Configurable RSS URLs Permit Server-Side Request Forgery
- Content
View full analysis
str: req = urllib.request.Request(url, headers={"User-Agent": UA, **(headers or {})}) with urllib.request.urlopen(req, timeout=20) as resp: return resp.read().decode("utf-8", errors="replace") ``` ```python def fetch_rss(cfg: dict[str, Any]) -> list[Item]: if not cfg.get("enabled"): return [] per_feed = int(cfg.get("per_feed", 20)) out: list[Item] = [] for feed in cfg.get("feeds", []): try: xml = http_text(feed) except Exception: continue ``` ### Technical Analysis RSS feed URLs are read directly from the configuration and passed to `urllib.request.urlopen` without validating the URL scheme, hostname, resolved IP address, or redirect destination. Consequently, anyone able to modify the Skill configuration can make the process issue requests to destinations beyond legitimate public RSS feeds, including: - Loopback services such as `127.0.0.1` or `[::1]` - Private network services - Link-local addresses and cloud instance metadata endpoints - Internal services exposed only to the host running the Skill - Public endpoints that redirect to an internal destination The timeout limits request duration but does not restrict the destination. No response-size limit is applied either, allowing a configured server to return an excessively large response before XML parsing. If an internal response is valid RSS or Atom XML, its titles, links, and descriptions can enter `latest.json` and `latest.md`. When report delivery is enabled, selected response content may also be forwarded to Telegram, Slack, or email. ### Attack Path 1. An attacker gains the ability to supply or modify the JSON configuration used by `- ...[truncated 1256 chars]- Remediation
View remediation
