Package: rails / 2:6.0.3.7+dfsg-2+deb11u2

CVE-2021-22942-5.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
From 3cfcb1a5f515c799946bd858a1949ffefda90316 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?=
 <rafael@rubyonrails.org>
Date: Wed, 15 Dec 2021 16:56:48 -0500
Subject: [PATCH] Merge pull request #43882 from rails/rm-allow-ip-with-port

Allow IPs with port in the HostAuthorization middleware
---
 .../middleware/host_authorization.rb          | 36 +++++--
 .../test/dispatch/host_authorization_test.rb  | 96 +++++++++++++++++++
 2 files changed, 123 insertions(+), 9 deletions(-)

diff --git a/actionpack/lib/action_dispatch/middleware/host_authorization.rb b/actionpack/lib/action_dispatch/middleware/host_authorization.rb
index e26e6ddc97..ca68b0241a 100644
--- a/actionpack/lib/action_dispatch/middleware/host_authorization.rb
+++ b/actionpack/lib/action_dispatch/middleware/host_authorization.rb
@@ -11,7 +11,15 @@ module ActionDispatch
   # default one will run, which responds with +403 Forbidden+.
   class HostAuthorization
     ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
-    PORT_REGEX = /(?::\d+)?/.freeze
+    PORT_REGEX = /(?::\d+)/ # :nodoc:
+    IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
+    IPV6_HOSTNAME = /(?<host>[a-f0-9]*:[a-f0-9.:]+)/i # :nodoc:
+    IPV6_HOSTNAME_WITH_PORT = /\[#{IPV6_HOSTNAME}\]#{PORT_REGEX}/i # :nodoc:
+    VALID_IP_HOSTNAME = Regexp.union( # :nodoc:
+      /\A#{IPV4_HOSTNAME}\z/,
+      /\A#{IPV6_HOSTNAME}\z/,
+      /\A#{IPV6_HOSTNAME_WITH_PORT}\z/,
+    )
 
     class Permissions # :nodoc:
       def initialize(hosts)
@@ -24,11 +32,17 @@ def empty?
 
       def allows?(host)
         @hosts.any? do |allowed|
-          allowed === host
-        rescue
-          # IPAddr#=== raises an error if you give it a hostname instead of
-          # IP. Treat similar errors as blocked access.
-          false
+          if allowed.is_a?(IPAddr)
+            begin
+              allowed === extract_hostname(host)
+            rescue
+              # IPAddr#=== raises an error if you give it a hostname instead of
+              # IP. Treat similar errors as blocked access.
+              false
+            end
+          else
+            allowed === host
+          end
         end
       end
 
@@ -44,16 +58,20 @@ def sanitize_hosts(hosts)
         end
 
         def sanitize_regexp(host)
-          /\A#{host}#{PORT_REGEX}\z/
+          /\A#{host}#{PORT_REGEX}?\z/
         end
 
         def sanitize_string(host)
           if host.start_with?(".")
-            /\A([a-z0-9-]+\.)?#{Regexp.escape(host[1..-1])}#{PORT_REGEX}\z/i
+            /\A([a-z0-9-]+\.)?#{Regexp.escape(host[1..-1])}#{PORT_REGEX}?\z/i
           else
-            /\A#{Regexp.escape host}#{PORT_REGEX}\z/i
+            /\A#{Regexp.escape host}#{PORT_REGEX}?\z/i
           end
         end
+
+        def extract_hostname(host)
+          host.slice(VALID_IP_HOSTNAME, "host") || host
+        end
     end
 
     DEFAULT_RESPONSE_APP = -> env do
diff --git a/actionpack/test/dispatch/host_authorization_test.rb b/actionpack/test/dispatch/host_authorization_test.rb
index 0bacaeb1ea..57125078c7 100644
--- a/actionpack/test/dispatch/host_authorization_test.rb
+++ b/actionpack/test/dispatch/host_authorization_test.rb
@@ -155,6 +155,102 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest
     assert_match "Success", response.body
   end
 
+  test "localhost using IPV4 works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "127.0.0.1",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV4 with port works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "127.0.0.1:3000",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV4 binding in all addresses works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "0.0.0.0",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV4 with port binding in all addresses works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "0.0.0.0:3000",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV6 works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "::1",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV6 with port works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "[::1]:3000",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV6 binding in all addresses works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "::",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
+  test "localhost using IPV6 with port binding in all addresses works in dev" do
+    @app = ActionDispatch::HostAuthorization.new(App, ActionDispatch::HostAuthorization::ALLOWED_HOSTS_IN_DEVELOPMENT)
+
+    get "/", env: {
+      "HOST" => "[::]:3000",
+      "action_dispatch.show_detailed_exceptions" => true
+    }
+
+    assert_response :ok
+    assert_match "Success", response.body
+  end
+
   test "hosts with port works" do
     @app = ActionDispatch::HostAuthorization.new(App, ["host.test"])
 
-- 
2.39.2