Class: RubyLLM::Toolbox::Safety::UrlGuard
- Inherits:
-
Object
- Object
- RubyLLM::Toolbox::Safety::UrlGuard
- Defined in:
- lib/ruby_llm/toolbox/safety/url_guard.rb
Overview
SSRF defense for tools that fetch arbitrary URLs. It:
- allows only http/https,
- rejects embedded credentials,
- enforces optional domain allow/deny lists,
- resolves the host and blocks the request if ANY resolved address is
private, loopback, link-local (incl. the cloud metadata IP), CGNAT,
unique-local IPv6, or otherwise reserved.
resolve! also returns a vetted IP so the caller can pin the socket to it (Net::HTTP#ipaddr=), closing the DNS-rebinding window: the address that was vetted is exactly the one connected to, while TLS/SNI/cert checks still use the hostname. Re-run resolve! on every redirect hop (the fetch helpers do).
Defined Under Namespace
Classes: Blocked, Resolution
Constant Summary collapse
- ALLOWED_SCHEMES =
%w[http https].freeze
- BLOCKED_RANGES =
%w[ 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 224.0.0.0/4 240.0.0.0/4 255.255.255.255/32 ::1/128 ::/128 fc00::/7 fe80::/10 ].map { |cidr| IPAddr.new(cidr) }.freeze
Instance Method Summary collapse
-
#check!(url) ⇒ Object
Returns a parsed URI if the URL is safe to fetch; raises Blocked otherwise.
-
#initialize(allowlist: [], denylist: []) ⇒ UrlGuard
constructor
A new instance of UrlGuard.
-
#resolve!(url) ⇒ Object
Like check!, but also returns a vetted IP address to pin the connection to (see Resolution).
Constructor Details
#initialize(allowlist: [], denylist: []) ⇒ UrlGuard
Returns a new instance of UrlGuard.
40 41 42 43 |
# File 'lib/ruby_llm/toolbox/safety/url_guard.rb', line 40 def initialize(allowlist: [], denylist: []) @allowlist = Array(allowlist).map { |d| d.to_s.downcase } @denylist = Array(denylist).map { |d| d.to_s.downcase } end |
Instance Method Details
#check!(url) ⇒ Object
Returns a parsed URI if the URL is safe to fetch; raises Blocked otherwise.
47 48 49 |
# File 'lib/ruby_llm/toolbox/safety/url_guard.rb', line 47 def check!(url) resolve!(url).uri end |
#resolve!(url) ⇒ Object
Like check!, but also returns a vetted IP address to pin the connection to (see Resolution). Raises Blocked otherwise.
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/ruby_llm/toolbox/safety/url_guard.rb', line 53 def resolve!(url) uri = parse(url) raise Blocked, "only http/https URLs are allowed" unless ALLOWED_SCHEMES.include?(uri.scheme) raise Blocked, "URL must include a host" if uri.host.nil? || uri.host.empty? raise Blocked, "URLs with embedded credentials are not allowed" if uri.userinfo host = uri.host.downcase enforce_domain_lists(host) address = vetted_address(host) Resolution.new(uri: uri, address: address) end |