mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
55e0723846
[CVE-2021-22903]
251 lines
6.3 KiB
Ruby
251 lines
6.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "abstract_unit"
|
|
require "ipaddr"
|
|
|
|
class HostAuthorizationTest < ActionDispatch::IntegrationTest
|
|
App = -> env { [200, {}, %w(Success)] }
|
|
|
|
test "blocks requests to unallowed host" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, %w(only.com))
|
|
|
|
get "/"
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: www.example.com", response.body
|
|
end
|
|
|
|
test "allows all requests if hosts is empty" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, nil)
|
|
|
|
get "/"
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "hosts can be a single element array" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, %w(www.example.com))
|
|
|
|
get "/"
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "hosts can be a string" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "www.example.com")
|
|
|
|
get "/"
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "hosts are matched case insensitive" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "Example.local")
|
|
|
|
get "/", env: {
|
|
"HOST" => "example.local",
|
|
}
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "hosts are matched case insensitive with titlecased host" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "example.local")
|
|
|
|
get "/", env: {
|
|
"HOST" => "Example.local",
|
|
}
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "hosts are matched case insensitive with hosts array" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ["Example.local"])
|
|
|
|
get "/", env: {
|
|
"HOST" => "example.local",
|
|
}
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "regex matches are not title cased" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, [/www.Example.local/])
|
|
|
|
get "/", env: {
|
|
"HOST" => "www.example.local",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: www.example.local", response.body
|
|
end
|
|
|
|
test "passes requests to allowed hosts with domain name notation" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ".example.com")
|
|
|
|
get "/"
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "does not allow domain name notation in the HOST header itself" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ".example.com")
|
|
|
|
get "/", env: {
|
|
"HOST" => ".example.com",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: .example.com", response.body
|
|
end
|
|
|
|
test "checks for requests with #=== to support wider range of host checks" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, [-> input { input == "www.example.com" }])
|
|
|
|
get "/"
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "mark the host when authorized" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ".example.com")
|
|
|
|
get "/"
|
|
|
|
assert_equal "www.example.com", request.get_header("action_dispatch.authorized_host")
|
|
end
|
|
|
|
test "sanitizes regular expressions to prevent accidental matches" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, [/w.example.co/])
|
|
|
|
get "/"
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: www.example.com", response.body
|
|
end
|
|
|
|
test "blocks requests to unallowed host supporting custom responses" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ["w.example.co"], response_app: -> env do
|
|
[401, {}, %w(Custom)]
|
|
end)
|
|
|
|
get "/"
|
|
|
|
assert_response :unauthorized
|
|
assert_equal "Custom", body
|
|
end
|
|
|
|
test "blocks requests with spoofed X-FORWARDED-HOST" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, [IPAddr.new("127.0.0.1")])
|
|
|
|
get "/", env: {
|
|
"HTTP_X_FORWARDED_HOST" => "127.0.0.1",
|
|
"HOST" => "www.example.com",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: 127.0.0.1", response.body
|
|
end
|
|
|
|
test "does not consider IP addresses in X-FORWARDED-HOST spoofed when disabled" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, nil)
|
|
|
|
get "/", env: {
|
|
"HTTP_X_FORWARDED_HOST" => "127.0.0.1",
|
|
"HOST" => "www.example.com",
|
|
}
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "detects localhost domain spoofing" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "localhost")
|
|
|
|
get "/", env: {
|
|
"HTTP_X_FORWARDED_HOST" => "localhost",
|
|
"HOST" => "www.example.com",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: localhost", response.body
|
|
end
|
|
|
|
test "forwarded hosts should be permitted" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "domain.com")
|
|
|
|
get "/", env: {
|
|
"HTTP_X_FORWARDED_HOST" => "sub.domain.com",
|
|
"HOST" => "domain.com",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: sub.domain.com", response.body
|
|
end
|
|
|
|
test "forwarded hosts are allowed when permitted" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ".domain.com")
|
|
|
|
get "/", env: {
|
|
"HTTP_X_FORWARDED_HOST" => "sub.domain.com",
|
|
"HOST" => "domain.com",
|
|
}
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "exclude matches allow any host" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "only.com", exclude: ->(req) { req.path == "/foo" })
|
|
|
|
get "/foo"
|
|
|
|
assert_response :ok
|
|
assert_equal "Success", body
|
|
end
|
|
|
|
test "exclude misses block unallowed hosts" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "only.com", exclude: ->(req) { req.path == "/bar" })
|
|
|
|
get "/foo"
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: www.example.com", response.body
|
|
end
|
|
|
|
test "blocks requests with invalid hostnames" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, ".example.com")
|
|
|
|
get "/", env: {
|
|
"HOST" => "attacker.com#x.example.com",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: attacker.com#x.example.com", response.body
|
|
end
|
|
|
|
test "blocks requests to similar host" do
|
|
@app = ActionDispatch::HostAuthorization.new(App, "sub.example.com")
|
|
|
|
get "/", env: {
|
|
"HOST" => "sub-example.com",
|
|
}
|
|
|
|
assert_response :forbidden
|
|
assert_match "Blocked host: sub-example.com", response.body
|
|
end
|
|
|
|
test "config setting action_dispatch.hosts_response_app is deprecated" do
|
|
assert_deprecated do
|
|
ActionDispatch::HostAuthorization.new(App, "example.com", ->(env) { true })
|
|
end
|
|
end
|
|
end
|