1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #43882 from rails/rm-allow-ip-with-port

Allow IPs with port in the HostAuthorization middleware
This commit is contained in:
Rafael Mendonça França 2021-12-15 16:56:48 -05:00 committed by GitHub
commit 334dfef1f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 9 deletions

View file

@ -17,7 +17,15 @@ module ActionDispatch
# if +config.consider_all_requests_local+ is set to true, otherwise the body is empty.
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)
@ -30,11 +38,17 @@ module ActionDispatch
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
@ -50,16 +64,20 @@ module ActionDispatch
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
class DefaultResponseApp # :nodoc:

View file

@ -166,6 +166,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"])