Description

Context

packages/shared-utils/src/bot-utils/url-scraper.ts:178 validates URLs by checking the literal hostname string against an allowlist + isInternalHost check, then axios.get(url) re-resolves DNS independently. An attacker domain returning 203.0.113.5 on the first lookup and 127.0.0.1 on the second bypasses the check (DNS rebinding).

Also: isInternalHost blocks 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, but NOT 100.64.0.0/10 (CGNAT) — relevant because the bot itself runs on Tailscale at 100.87.20.88, so a rebound DNS could hit Tailscale-internal services (Authentik, GitLab, Outline, MinIO admin).

Acceptance criteria

  • [ ] Resolve DNS once before the fetch; validate the resolved IP against block ranges
  • [ ] Pin the fetch to that IP via axios custom httpAgent with lookup returning the validated IP, OR fetch with manually constructed http.request to the IP and the original Host header
  • [ ] Add 100.64.0.0/10 (RFC 6598 CGNAT, includes Tailscale) to the blocklist
  • [ ] Add IPv6 link-local (fe80::/10), IPv6 ULA (fc00::/7), ::1/128
  • [ ] Block AWS/GCP/Azure metadata IPs: 169.254.169.254, fd00:ec2::254
  • [ ] Cap redirects at 3 and revalidate the IP after each redirect (axios's default follows redirects without re-checking)
  • [ ] Test: confirm a domain that returns 8.8.8.8 then 127.0.0.1 across two lookups gets blocked

Technical hints

  • The dns.lookup() call gives you the IP; pass it via agent: new http.Agent({lookup: (host, opts, cb) => cb(null, validatedIp, 4)})
  • Tailscale CGNAT: 100.64.0.0/10 covers 100.64.0.0 to 100.127.255.255
  • Look at how the Q&A worker handles SSRF for reference (if it does)
  • Add a unit test for the IP-validation function so future config changes don't regress

--- *M-tier (100 pts + 40 merge_pr = 140). Touches a shared package — affects all bot URL-fetching paths.*

Relevant files

No relevant files linked yet.

Hints

No hints yet.

Activity

  • +100 Günther Van der Eecken earned M bounty Jun 14

Comments

No comments yet. Be the first to weigh in.

↑ Top