Raise `ActionController::Redirecting::UnsafeRedirectError` for unsafe `redirect_to` redirects.

This allows `rescue_from` to be used to add a default fallback route:

```ruby
rescue_from ActionController::Redirecting::UnsafeRedirectError do
  redirect_to root_url
end
```

Co-Authored-By: Chris Oliver <excid3@gmail.com>
This commit is contained in:
Kasper Timm Hansen 2021-11-05 03:13:15 +01:00
parent b36c9ca378
commit c3758a71af
3 changed files with 19 additions and 3 deletions

View File

@ -1,3 +1,15 @@
* Raise `ActionController::Redirecting::UnsafeRedirectError` for unsafe `redirect_to` redirects.
This allows `rescue_from` to be used to add a default fallback route:
```ruby
rescue_from ActionController::Redirecting::UnsafeRedirectError do
redirect_to root_url
end
```
*Kasper Timm Hansen*, *Chris Oliver*
* Add `url_from` to verify a redirect location is internal.
Takes the open redirect protection from `redirect_to` so users can wrap a

View File

@ -7,6 +7,8 @@ module ActionController
include AbstractController::Logger
include ActionController::UrlFor
class UnsafeRedirectError < StandardError; end
included do
mattr_accessor :raise_on_open_redirects, default: false
end
@ -70,6 +72,8 @@ module ActionController
#
# redirect_to params[:redirect_url]
#
# Raises <tt>ActionController::Redirecting::UnsafeRedirectError</tt> in the case of an unsafe redirect.
#
# To allow any external redirects pass `allow_other_host: true`, though using a user-provided param in that case is unsafe.
#
# redirect_to "https://rubyonrails.org", allow_other_host: true
@ -186,7 +190,7 @@ module ActionController
if allow_other_host || _url_host_allowed?(location)
location
else
raise ArgumentError, "Unsafe redirect to #{location.truncate(100).inspect}, pass allow_other_host: true to redirect anyway."
raise UnsafeRedirectError, "Unsafe redirect to #{location.truncate(100).inspect}, pass allow_other_host: true to redirect anyway."
end
end

View File

@ -482,7 +482,7 @@ class RedirectTest < ActionController::TestCase
def test_unsafe_redirect
with_raise_on_open_redirects do
error = assert_raise(ArgumentError) do
error = assert_raise(ActionController::Redirecting::UnsafeRedirectError) do
get :unsafe_redirect
end
@ -492,7 +492,7 @@ class RedirectTest < ActionController::TestCase
def test_unsafe_redirect_back
with_raise_on_open_redirects do
error = assert_raise(ArgumentError) do
error = assert_raise(ActionController::Redirecting::UnsafeRedirectError) do
get :unsafe_redirect_back
end