Skip to content

[SECURITY] Unrestricted redirect following in httpx calls enables potential SSRF #30

@FZ2000

Description

@FZ2000

Severity: P3

Summary

All httpx.get() calls in src/skills.py use follow_redirects=True without validating that redirect destinations remain on GitHub domains. This enables a Server-Side Request Forgery (SSRF) attack vector if any component in the redirect chain is attacker-controlled.

Affected Code

src/skills.py:

resp = httpx.get(url, follow_redirects=True, timeout=15)  # line 79
resp = httpx.get(url, follow_redirects=True, timeout=15)  # line 107

Attack Scenarios

  1. DNS rebinding: If an attacker controls a DNS entry for a subdomain of github.com or raw.githubusercontent.com (extremely unlikely but theoretically possible), redirects could point to internal IPs
  2. Compromised CDN/proxy: A MITM at the network layer issues a redirect to http://169.254.169.254/ (cloud metadata endpoint) or http://localhost:PORT/admin
  3. Future repo hosting: If support for non-GitHub registries is added, the same code runs with full redirect following

Impact

In cloud environments (AWS, GCP, Azure), an SSRF to the instance metadata endpoint (169.254.169.254) can leak IAM credentials. On developer machines, it can probe internal services.

Recommended Fix

Validate redirect destinations against an allowlist:

ALLOWED_HOSTS = {"api.github.com", "raw.githubusercontent.com"}

def _safe_get(url: str) -> httpx.Response:
    """httpx GET that validates redirects stay on allowed GitHub hosts."""
    resp = httpx.get(url, follow_redirects=False, timeout=15)
    while resp.is_redirect:
        location = resp.headers.get("location", "")
        parsed = httpx.URL(location)
        if parsed.host not in ALLOWED_HOSTS:
            raise httpx.HTTPError(f"Redirect to disallowed host: {parsed.host}")
        resp = httpx.get(location, follow_redirects=False, timeout=15)
    return resp

References

  • CWE-918: Server-Side Request Forgery (SSRF)
  • OWASP SSRF Prevention Cheat Sheet

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity vulnerability

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions