T09 · Insecure Skill Coding Practices
- Location
- scripts/url_fetch.py:67
- Finding
- Headless browser redirects and subresources bypass complete SSRF validation## Vulnerability Details **File Location**: `scripts/url_fetch.py:67-123` **Vulnerability Type**: Incomplete SSRF protection in the browser-rendering path **Risk Level**: High ### Vulnerable Code ```python def render_with_browser(url, browser, virtual_time=8000, allow_internal=False): # Defense-in-depth: re-verify the target INSIDE this function so the browser # is never launched for an internal/private URL even if an upstream guard # were skipped. --allow-internal (trusted local dev) still overrides. try: from url_security import _is_blocked_target except ImportError: # used standalone without the package; upstream guard applies _is_blocked_target = None if _is_blocked_target is not None: blocked, reason = _is_blocked_target(url, allow_internal) if blocked: print("[spa-fallback] in-function SSRF re-check refused target: %s" % reason, file=sys.stderr) return None # Pin the hostname to its validated address for the renderer as well, so a # rebinding resolver cannot send the browser to an internal address. The # mapping only covers this hostname (sub-resource hosts are untested by # design — see the documented limitation in SKILL.md). pin_rule = None try: from url_security import resolve_and_check _host = urllib.parse.urlparse(url).hostname or "" _blocked_dns, _reason_dns = resolve_and_check(_host, allow_internal) if _blocked_dns: print("[spa-fallback] in-function DNS re-check refused target: %s" % _reason_dns, file=sys.stderr) return None _ip = _resolve_first_ipv4(_host) if _ip and _host and _host != _ip: pin_rule = "MAP %s %s" % (_host, _ip) except Exception: # noqa: BLE001 - pinning is best effort, never fatal pin_rule = None html_path = _make_tem ...[truncated 3626 chars]
- Remediation
- ## Remediation Suggestions 1. Intercept every Chromium request through the Chrome DevTools Protocol or an equivalent browser automation API. 2. Validate all navigation, redirect, frame, worker, and subresource destinations before allowing them. 3. Resolve every destination hostname and reject any address that is loopback, private, link-local, reserved, multicast, CGNAT, or otherwise non-public. 4. Pin each approved hostname to the specific validated address, not only the original page hostname. 5. Prefer running browser traffic through a dedicated egress-filtering proxy that rejects private and metadata destinations at the network layer. 6. Place Chromium in an isolated network namespace or container that has no route to internal networks or metadata services. 7. Treat validation failures as fatal rather than continuing with best-effort pinning. 8. Restrict or remove `--allow-internal` from agent-facing execution; if retained for development, require an explicit trusted operator action. 9. Add regression tests for public-to-private redirects, DNS rebinding, nested frames, JavaScript fetches, and private subresource URLs.
