FIX: Gracefully handle DNS issued from SSRF lookup when inline oneboxing (#19631)
There is an issue where chat message processing breaks due to unhandles `SocketError` exceptions originating in the SSRF check, specifically in `FinalDestination::Resolver`. This change gives `FinalDestination::SSRFDetector` a new error class to wrap the `SocketError` in, and haves the `RetrieveTitle` class handle that error gracefully.
This commit is contained in:
parent
462e14e279
commit
06db264f24
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
class FinalDestination
|
class FinalDestination
|
||||||
module SSRFDetector
|
module SSRFDetector
|
||||||
class DisallowedIpError < SocketError
|
class DisallowedIpError < SocketError; end
|
||||||
end
|
class LookupFailedError < SocketError; end
|
||||||
|
|
||||||
def self.standard_private_ranges
|
def self.standard_private_ranges
|
||||||
@private_ranges ||= [
|
@private_ranges ||= [
|
||||||
|
@ -61,7 +61,12 @@ class FinalDestination
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.lookup_and_filter_ips(name, timeout: nil)
|
def self.lookup_and_filter_ips(name, timeout: nil)
|
||||||
ips = lookup_ips(name, timeout: timeout)
|
begin
|
||||||
|
ips = lookup_ips(name, timeout: timeout)
|
||||||
|
rescue SocketError
|
||||||
|
raise LookupFailedError, "FinalDestination: lookup failed"
|
||||||
|
end
|
||||||
|
|
||||||
return ips if host_bypasses_checks?(name)
|
return ips if host_bypasses_checks?(name)
|
||||||
|
|
||||||
ips.filter! { |ip| FinalDestination::SSRFDetector.ip_allowed?(ip) }
|
ips.filter! { |ip| FinalDestination::SSRFDetector.ip_allowed?(ip) }
|
||||||
|
|
|
@ -9,7 +9,7 @@ module RetrieveTitle
|
||||||
max_redirects: max_redirects,
|
max_redirects: max_redirects,
|
||||||
initial_https_redirect_ignore_limit: initial_https_redirect_ignore_limit
|
initial_https_redirect_ignore_limit: initial_https_redirect_ignore_limit
|
||||||
)
|
)
|
||||||
rescue Net::ReadTimeout
|
rescue Net::ReadTimeout, FinalDestination::SSRFDetector::LookupFailedError
|
||||||
# do nothing for Net::ReadTimeout errors
|
# do nothing for Net::ReadTimeout errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,13 @@ describe FinalDestination::SSRFDetector do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "raises an exception if lookup fails" do
|
||||||
|
subject.stubs(:lookup_ips).raises(SocketError)
|
||||||
|
expect { subject.lookup_and_filter_ips("example.com") }.to raise_error(
|
||||||
|
subject::LookupFailedError,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "bypasses filtering for allowlisted hosts" do
|
it "bypasses filtering for allowlisted hosts" do
|
||||||
SiteSetting.allowed_internal_hosts = "example.com"
|
SiteSetting.allowed_internal_hosts = "example.com"
|
||||||
subject.stubs(:lookup_ips).returns(["127.0.0.1"])
|
subject.stubs(:lookup_ips).returns(["127.0.0.1"])
|
||||||
|
|
|
@ -162,7 +162,13 @@ RSpec.describe RetrieveTitle do
|
||||||
it "it ignores Net::ReadTimeout errors" do
|
it "it ignores Net::ReadTimeout errors" do
|
||||||
stub_request(:get, "https://example.com").to_raise(Net::ReadTimeout)
|
stub_request(:get, "https://example.com").to_raise(Net::ReadTimeout)
|
||||||
|
|
||||||
expect { RetrieveTitle.crawl("https://example.com") }.not_to raise_error
|
expect(RetrieveTitle.crawl("https://example.com")).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ignores SSRF lookup errors" do
|
||||||
|
subject.stubs(:fetch_title).raises(FinalDestination::SSRFDetector::LookupFailedError)
|
||||||
|
|
||||||
|
expect(RetrieveTitle.crawl("https://example.com")).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue