From b1d04cf9d58ac461f70a2cbf4df617cc14c3de1c Mon Sep 17 00:00:00 2001
From: Stan Hu <stanhu@gmail.com>
Date: Wed, 5 Sep 2018 21:41:59 -0700
Subject: [PATCH] Block loopback addresses in UrlBlocker

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/51128
---
 .../unreleased/sh-block-other-localhost.yml   |  5 +++++
 lib/gitlab/url_blocker.rb                     |  7 ++++++
 spec/lib/gitlab/url_blocker_spec.rb           | 22 ++++++++++++++++++-
 3 files changed, 33 insertions(+), 1 deletion(-)
 create mode 100644 changelogs/unreleased/sh-block-other-localhost.yml

diff --git a/changelogs/unreleased/sh-block-other-localhost.yml b/changelogs/unreleased/sh-block-other-localhost.yml
new file mode 100644
index 00000000000..a6a41f0bd81
--- /dev/null
+++ b/changelogs/unreleased/sh-block-other-localhost.yml
@@ -0,0 +1,5 @@
+---
+title: Block loopback addresses in UrlBlocker
+merge_request:
+author:
+type: security
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
index 3b483f27e70..a2cce9151c3 100644
--- a/lib/gitlab/url_blocker.rb
+++ b/lib/gitlab/url_blocker.rb
@@ -30,6 +30,7 @@ module Gitlab
         end
 
         validate_localhost!(addrs_info) unless allow_localhost
+        validate_loopback!(addrs_info) unless allow_localhost
         validate_local_network!(addrs_info) unless allow_local_network
         validate_link_local!(addrs_info) unless allow_local_network
 
@@ -84,6 +85,12 @@ module Gitlab
         raise BlockedUrlError, "Requests to localhost are not allowed"
       end
 
+      def validate_loopback!(addrs_info)
+        return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? }
+
+        raise BlockedUrlError, "Requests to loopback addresses are not allowed"
+      end
+
       def validate_local_network!(addrs_info)
         return unless addrs_info.any? { |addr| addr.ipv4_private? || addr.ipv6_sitelocal? }
 
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 624e2add860..8df0facdab3 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -29,6 +29,16 @@ describe Gitlab::UrlBlocker do
       expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', protocols: ['http'])).to be true
     end
 
+    it 'returns true for localhost IPs' do
+      expect(described_class.blocked_url?('https://0.0.0.0/foo/foo.git')).to be true
+      expect(described_class.blocked_url?('https://[::1]/foo/foo.git')).to be true
+      expect(described_class.blocked_url?('https://127.0.0.1/foo/foo.git')).to be true
+    end
+
+    it 'returns true for loopback IP' do
+      expect(described_class.blocked_url?('https://127.0.0.2/foo/foo.git')).to be true
+    end
+
     it 'returns true for alternative version of 127.0.0.1 (0177.1)' do
       expect(described_class.blocked_url?('https://0177.1:65535/foo/foo.git')).to be true
     end
@@ -84,6 +94,16 @@ describe Gitlab::UrlBlocker do
           end
         end
 
+        it 'allows localhost endpoints' do
+          expect(described_class).not_to be_blocked_url('http://0.0.0.0', allow_localhost: true)
+          expect(described_class).not_to be_blocked_url('http://localhost', allow_localhost: true)
+          expect(described_class).not_to be_blocked_url('http://127.0.0.1', allow_localhost: true)
+        end
+
+        it 'allows loopback endpoints' do
+          expect(described_class).not_to be_blocked_url('http://127.0.0.2', allow_localhost: true)
+        end
+
         it 'allows IPv4 link-local endpoints' do
           expect(described_class).not_to be_blocked_url('http://169.254.169.254')
           expect(described_class).not_to be_blocked_url('http://169.254.168.100')
@@ -122,7 +142,7 @@ describe Gitlab::UrlBlocker do
       end
 
       def stub_domain_resolv(domain, ip)
-        allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false)])
+        allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false, ipv4_loopback?: false, ipv6_loopback?: false)])
       end
 
       def unstub_domain_resolv
-- 
2.30.9