Skip to content

Prevent SSRF via user-supplied feed URLs#1548

Merged
mockdeep merged 1 commit into
mainfrom
security/ssrf-feed-fetch
Jun 10, 2026
Merged

Prevent SSRF via user-supplied feed URLs#1548
mockdeep merged 1 commit into
mainfrom
security/ssrf-feed-fetch

Conversation

@mockdeep

Copy link
Copy Markdown
Collaborator

Summary

Fixes an authenticated Server-Side Request Forgery (SSRF) in feed fetching — advisory GHSA-496x-437q-h35q (CWE-918). User-supplied feed URLs were fetched with no scheme or host/IP validation and with redirects followed, allowing any authenticated user to make the server issue requests to loopback, private/link-local ranges (e.g. 169.254.169.254), and other internal hosts. The sink was reachable from POST /feeds, PUT /feeds/:id (via the next poll), and scheduled polling.

Fix

  • New app/utils/safe_fetch.rb chokepoint that restricts the URL scheme to http/https and runs every outbound request inside private_address_check's only_public_connections, which validates the actual connected IP on each redirect hop (so legitimate http→https and other redirects still work).
  • Routed all three sinks through it: the direct fetch and the Feedbag.find discovery request in FeedDiscovery, and the scheduled fetch in Feed::FetchOne (which also covers the feed_url update path at poll time).
  • The scheme allow-list also closes a file:// local-read reachable through Feedbag's open-uri.

Tests

  • New spec/utils/safe_fetch_spec.rb; added an unsafe-URL case to spec/utils/feed_discovery_spec.rb.
  • Updated spec/integration/feed_importing_spec.rb to permit its local (loopback) test server, which the new guard correctly blocks by default.
  • Full suite green (512 examples, 0 failures), RuboCop clean.

Notes

  • Adds one dependency: private_address_check.
  • Residual: the guard completes the TCP handshake before rejecting a private address, so the marginal blind-port-scan timing signal isn't perfectly eliminated; the credential-theft / metadata-read vector is fully closed.

🤖 Generated with Claude Code

Feed URLs were fetched with no scheme or host validation and with
redirects followed, letting any authenticated user make the server
issue requests to loopback, private/link-local ranges, and cloud
metadata endpoints (GHSA-496x-437q-h35q, CWE-918). The sink was
reachable from feed creation, feed update (via the next poll), and
scheduled polling.

Route all outbound feed fetches through a new SafeFetch helper that
restricts the scheme to http/https and runs the request inside
private_address_check's only_public_connections, which validates the
actual connected IP on every redirect hop. This covers the direct
HTTParty fetches in FeedDiscovery and Feed::FetchOne as well as the
Feedbag discovery request, while still following legitimate redirects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mockdeep mockdeep merged commit 75cb095 into main Jun 10, 2026
3 checks passed
@mockdeep mockdeep deleted the security/ssrf-feed-fetch branch June 10, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant