Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception

rescue_from ActionController::Redirecting::UnsafeRedirectError, with: :unsafe_redirect_error
rescue_from ActionController::InvalidAuthenticityToken, with: :invalid_auth_token
rescue_from ActionController::UnknownFormat, with: :render_not_found
rescue_from ActionView::MissingTemplate, with: :render_not_acceptable
Expand Down Expand Up @@ -267,7 +268,24 @@ def invalid_auth_token(_exception)
user_signed_in: user_signed_in?,
)
flash[:error] = t('errors.general')
redirect_back fallback_location: new_user_session_url, allow_other_host: false
begin
redirect_back fallback_location: new_user_session_url, allow_other_host: false
rescue ActionController::Redirecting::UnsafeRedirectError => err
# Exceptions raised inside exception handlers are not propagated up, so we manually rescue
unsafe_redirect_error(err)
end
end

def unsafe_redirect_error(_exception)
controller_info = "#{controller_path}##{action_name}"
analytics.unsafe_redirect_error(
controller: controller_info,
user_signed_in: user_signed_in?,
referer: request.referer,
)

flash[:error] = t('errors.general')
redirect_to new_user_session_url
end

def user_fully_authenticated?
Expand Down
19 changes: 19 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,25 @@ def invalid_authenticity_token(
)
end

# @param [String] controller
# @param [String] referer
# @param [Boolean] user_signed_in
# Redirect was almost sent to an invalid external host unexpectedly
def unsafe_redirect_error(
controller:,
referer:,
user_signed_in: nil,
**extra
)
track_event(
'Unsafe Redirect',
controller: controller,
referer: referer,
user_signed_in: user_signed_in,
**extra,
)
end

# @param [Integer] acknowledged_event_count number of acknowledged events in the API call
# @param [Integer] rendered_event_count how many events were rendered in the API response
# @param [String] set_errors JSON encoded representation of SET errors from the client
Expand Down
42 changes: 42 additions & 0 deletions spec/controllers/application_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,48 @@ def index

expect(response).to redirect_to(new_user_session_url)
end

it 'redirects back to home page if present and referer is invalid' do
referer = '@@ABC'

request.env['HTTP_REFERER'] = referer

get :index

expect(response).to redirect_to(new_user_session_url)
end
end

describe 'handling UnsafeRedirectError exceptions' do
controller do
def index
raise ActionController::Redirecting::UnsafeRedirectError
end
end

it 'tracks the Unsafe Redirect event and does not sign the user out' do
referer = '@@ABC'
request.env['HTTP_REFERER'] = referer
sign_in_as_user
expect(subject.current_user).to be_present

stub_analytics
event_properties = { controller: 'anonymous#index', user_signed_in: true, referer: referer }
expect(@analytics).to receive(:track_event).
with('Unsafe Redirect', event_properties)

get :index

expect(flash[:error]).to eq t('errors.general')
expect(response).to redirect_to(root_url)
expect(subject.current_user).to be_present
end

it 'redirects back to home page' do
get :index

expect(response).to redirect_to(new_user_session_url)
end
end

describe '#append_info_to_payload' do
Expand Down