From f75a5180a6ec2bcc6b41b6d9d48dc7826c6ab9a4 Mon Sep 17 00:00:00 2001 From: Augusto Xavier Date: Wed, 10 Jun 2026 09:03:41 -0300 Subject: [PATCH] Do not include the query string in PATH_INFO when recalling the original controller. --- CHANGELOG.md | 5 +++++ lib/devise/failure_app.rb | 8 ++++++-- test/failure_app_test.rb | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ccb0882a4..0008d2ee09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### unreleased + +* bug fixes + * Do not include the query string in `PATH_INFO` when the failure app recalls the original controller. [#5704](https://github.com/heartcombo/devise/issues/5704) + ### 5.0.4 - 2026-05-08 * security fixes diff --git a/lib/devise/failure_app.rb b/lib/devise/failure_app.rb index 70cf6d2f31..389e940a2f 100644 --- a/lib/devise/failure_app.rb +++ b/lib/devise/failure_app.rb @@ -57,14 +57,18 @@ def http_auth end def recall + # Rack does not allow the query string in PATH_INFO, and it is already + # available to the recalled action through QUERY_STRING. + path_info = attempted_path.to_s.split("?").first + header_info = if relative_url_root? base_path = Pathname.new(relative_url_root) - full_path = Pathname.new(attempted_path) + full_path = Pathname.new(path_info) { "SCRIPT_NAME" => relative_url_root, "PATH_INFO" => '/' + full_path.relative_path_from(base_path).to_s } else - { "PATH_INFO" => attempted_path } + { "PATH_INFO" => path_info } end header_info.each do | var, value| diff --git a/test/failure_app_test.rb b/test/failure_app_test.rb index c9e4a56ce1..3781328258 100644 --- a/test/failure_app_test.rb +++ b/test/failure_app_test.rb @@ -383,6 +383,17 @@ def call_failure(env_params = {}) assert_includes @response.third.body, 'Your account is not activated yet.' end + test 'calls the original controller without the query string in PATH_INFO' do + env = { + "warden.options" => { recall: "devise/sessions#new", attempted_path: "/users/sign_in?redirect_to=/dashboard" }, + "devise.mapping" => Devise.mappings[:user], + "warden" => stub_everything + } + call_failure(env) + assert_includes @response.third.body, '

Log in

' + assert_equal '/users/sign_in', @request.env["PATH_INFO"] + end + if Rails.application.config.respond_to?(:relative_url_root) test 'calls the original controller with the proper environment considering the relative url root' do swap Rails.application.config, relative_url_root: "/sample" do @@ -398,6 +409,20 @@ def call_failure(env_params = {}) assert_equal '/users/sign_in', @request.env["PATH_INFO"] end end + + test 'calls the original controller without the query string in PATH_INFO considering the relative url root' do + swap Rails.application.config, relative_url_root: "/sample" do + env = { + "warden.options" => { recall: "devise/sessions#new", attempted_path: "/sample/users/sign_in?redirect_to=/dashboard"}, + "devise.mapping" => Devise.mappings[:user], + "warden" => stub_everything + } + call_failure(env) + assert_includes @response.third.body, '

Log in

' + assert_equal '/sample', @request.env["SCRIPT_NAME"] + assert_equal '/users/sign_in', @request.env["PATH_INFO"] + end + end end test 'respects the i18n locale passed via warden options when recalling original controller' do