|
1 | 1 | # frozen_string_literal: true
|
2 | 2 |
|
3 | 3 | require "abstract_unit"
|
| 4 | +require "active_support/log_subscriber/test_helper" |
4 | 5 |
|
5 | 6 | class Workshop
|
6 | 7 | extend ActiveModel::Naming
|
@@ -154,6 +155,14 @@ def redirect_to_url_with_network_path_reference
|
154 | 155 | redirect_to "//www.rubyonrails.org/"
|
155 | 156 | end
|
156 | 157 |
|
| 158 | + def redirect_to_path_relative_url |
| 159 | + redirect_to "example.com" |
| 160 | + end |
| 161 | + |
| 162 | + def redirect_to_path_relative_url_starting_with_an_at |
| 163 | + redirect_to "@example.com" |
| 164 | + end |
| 165 | + |
157 | 166 | def redirect_to_existing_record
|
158 | 167 | redirect_to Workshop.new(5)
|
159 | 168 | end
|
@@ -335,6 +344,18 @@ def test_redirect_to_url_with_complex_scheme
|
335 | 344 | assert_equal "x-test+scheme.complex:redirect", redirect_to_url
|
336 | 345 | end
|
337 | 346 |
|
| 347 | + def test_redirect_to_path_relative_url |
| 348 | + get :redirect_to_path_relative_url |
| 349 | + assert_response :redirect |
| 350 | + assert_equal "http://test.hostexample.com", redirect_to_url |
| 351 | + end |
| 352 | + |
| 353 | + def test_redirect_to_url_with_path_relative_url_starting_with_an_at |
| 354 | + get :redirect_to_path_relative_url_starting_with_an_at |
| 355 | + assert_response :redirect |
| 356 | + assert_equal "http://[email protected]", redirect_to_url |
| 357 | + end |
| 358 | + |
338 | 359 | def test_redirect_to_url_with_network_path_reference
|
339 | 360 | get :redirect_to_url_with_network_path_reference
|
340 | 361 | assert_response :redirect
|
@@ -620,6 +641,180 @@ def test_redirect_to_external_with_rescue
|
620 | 641 | assert_response :ok
|
621 | 642 | end
|
622 | 643 |
|
| 644 | + def test_redirect_to_path_relative_url_with_log |
| 645 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 646 | + ActionController::Base.action_on_path_relative_redirect = :log |
| 647 | + |
| 648 | + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new |
| 649 | + old_logger = ActionController::Base.logger |
| 650 | + ActionController::Base.logger = logger |
| 651 | + |
| 652 | + get :redirect_to_path_relative_url |
| 653 | + assert_response :redirect |
| 654 | + assert_equal "http://test.hostexample.com", redirect_to_url |
| 655 | + assert_match(/Path relative URL redirect detected: "example.com"/, logger.logged(:warn).last) |
| 656 | + ensure |
| 657 | + ActionController::Base.logger = old_logger |
| 658 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 659 | + end |
| 660 | + |
| 661 | + def test_redirect_to_path_relative_url_starting_with_an_at_with_log |
| 662 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 663 | + ActionController::Base.action_on_path_relative_redirect = :log |
| 664 | + |
| 665 | + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new |
| 666 | + old_logger = ActionController::Base.logger |
| 667 | + ActionController::Base.logger = logger |
| 668 | + |
| 669 | + get :redirect_to_path_relative_url_starting_with_an_at |
| 670 | + assert_response :redirect |
| 671 | + assert_equal "http://[email protected]", redirect_to_url |
| 672 | + assert_match(/Path relative URL redirect detected: "@example.com"/, logger.logged(:warn).last) |
| 673 | + ensure |
| 674 | + ActionController::Base.logger = old_logger |
| 675 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 676 | + end |
| 677 | + |
| 678 | + def test_redirect_to_path_relative_url_starting_with_an_at_with_notify |
| 679 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 680 | + ActionController::Base.action_on_path_relative_redirect = :notify |
| 681 | + |
| 682 | + events = [] |
| 683 | + ActiveSupport::Notifications.subscribe("unsafe_redirect.action_controller") do |*args| |
| 684 | + events << ActiveSupport::Notifications::Event.new(*args) |
| 685 | + end |
| 686 | + |
| 687 | + get :redirect_to_path_relative_url_starting_with_an_at |
| 688 | + |
| 689 | + assert_response :redirect |
| 690 | + assert_equal "http://[email protected]", redirect_to_url |
| 691 | + |
| 692 | + assert_equal 1, events.size |
| 693 | + event = events.first |
| 694 | + assert_equal "@example.com", event.payload[:url] |
| 695 | + assert_equal 'Path relative URL redirect detected: "@example.com"', event.payload[:message] |
| 696 | + assert_kind_of Array, event.payload[:stack_trace] |
| 697 | + assert event.payload[:stack_trace].any? { |line| line.include?("redirect_to_path_relative_url_starting_with_an_at") } |
| 698 | + ensure |
| 699 | + ActiveSupport::Notifications.unsubscribe("unsafe_redirect.action_controller") |
| 700 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 701 | + end |
| 702 | + |
| 703 | + def test_redirect_to_path_relative_url_with_notify |
| 704 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 705 | + ActionController::Base.action_on_path_relative_redirect = :notify |
| 706 | + |
| 707 | + events = [] |
| 708 | + ActiveSupport::Notifications.subscribe("unsafe_redirect.action_controller") do |*args| |
| 709 | + events << ActiveSupport::Notifications::Event.new(*args) |
| 710 | + end |
| 711 | + |
| 712 | + get :redirect_to_path_relative_url |
| 713 | + |
| 714 | + assert_response :redirect |
| 715 | + assert_equal "http://test.hostexample.com", redirect_to_url |
| 716 | + |
| 717 | + assert_equal 1, events.size |
| 718 | + event = events.first |
| 719 | + assert_equal "example.com", event.payload[:url] |
| 720 | + assert_equal 'Path relative URL redirect detected: "example.com"', event.payload[:message] |
| 721 | + assert_kind_of Array, event.payload[:stack_trace] |
| 722 | + assert event.payload[:stack_trace].any? { |line| line.include?("redirect_to_path_relative_url") } |
| 723 | + ensure |
| 724 | + ActiveSupport::Notifications.unsubscribe("unsafe_redirect.action_controller") |
| 725 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 726 | + end |
| 727 | + |
| 728 | + def test_redirect_to_path_relative_url_with_raise |
| 729 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 730 | + ActionController::Base.action_on_path_relative_redirect = :raise |
| 731 | + |
| 732 | + error = assert_raise(ActionController::Redirecting::UnsafeRedirectError) do |
| 733 | + get :redirect_to_path_relative_url |
| 734 | + end |
| 735 | + |
| 736 | + assert_equal 'Path relative URL redirect detected: "example.com"', error.message |
| 737 | + ensure |
| 738 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 739 | + end |
| 740 | + |
| 741 | + def test_redirect_to_path_relative_url_starting_with_an_at_with_raise |
| 742 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 743 | + ActionController::Base.action_on_path_relative_redirect = :raise |
| 744 | + |
| 745 | + error = assert_raise(ActionController::Redirecting::UnsafeRedirectError) do |
| 746 | + get :redirect_to_path_relative_url_starting_with_an_at |
| 747 | + end |
| 748 | + |
| 749 | + assert_equal 'Path relative URL redirect detected: "@example.com"', error.message |
| 750 | + ensure |
| 751 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 752 | + end |
| 753 | + |
| 754 | + def test_redirect_to_absolute_url_does_not_log |
| 755 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 756 | + ActionController::Base.action_on_path_relative_redirect = :log |
| 757 | + |
| 758 | + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new |
| 759 | + old_logger = ActionController::Base.logger |
| 760 | + ActionController::Base.logger = logger |
| 761 | + |
| 762 | + get :redirect_to_url |
| 763 | + assert_response :redirect |
| 764 | + assert_equal "http://www.rubyonrails.org/", redirect_to_url |
| 765 | + assert_empty logger.logged(:warn) |
| 766 | + |
| 767 | + get :relative_url_redirect_with_status |
| 768 | + assert_response :redirect |
| 769 | + assert_equal "http://test.host/things/stuff", redirect_to_url |
| 770 | + assert_empty logger.logged(:warn) |
| 771 | + ensure |
| 772 | + ActionController::Base.logger = old_logger |
| 773 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 774 | + end |
| 775 | + |
| 776 | + def test_redirect_to_absolute_url_does_not_notify |
| 777 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 778 | + ActionController::Base.action_on_path_relative_redirect = :notify |
| 779 | + |
| 780 | + events = [] |
| 781 | + ActiveSupport::Notifications.subscribe("unsafe_redirect.action_controller") do |*args| |
| 782 | + events << ActiveSupport::Notifications::Event.new(*args) |
| 783 | + end |
| 784 | + |
| 785 | + get :redirect_to_url |
| 786 | + assert_response :redirect |
| 787 | + assert_equal "http://www.rubyonrails.org/", redirect_to_url |
| 788 | + assert_empty events |
| 789 | + |
| 790 | + get :relative_url_redirect_with_status |
| 791 | + assert_response :redirect |
| 792 | + assert_equal "http://test.host/things/stuff", redirect_to_url |
| 793 | + assert_empty events |
| 794 | + ensure |
| 795 | + ActiveSupport::Notifications.unsubscribe("unsafe_redirect.action_controller") |
| 796 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 797 | + end |
| 798 | + |
| 799 | + def test_redirect_to_absolute_url_does_not_raise |
| 800 | + old_config = ActionController::Base.action_on_path_relative_redirect |
| 801 | + ActionController::Base.action_on_path_relative_redirect = :raise |
| 802 | + |
| 803 | + get :redirect_to_url |
| 804 | + assert_response :redirect |
| 805 | + assert_equal "http://www.rubyonrails.org/", redirect_to_url |
| 806 | + |
| 807 | + get :relative_url_redirect_with_status |
| 808 | + assert_response :redirect |
| 809 | + assert_equal "http://test.host/things/stuff", redirect_to_url |
| 810 | + |
| 811 | + get :redirect_to_url_with_network_path_reference |
| 812 | + assert_response :redirect |
| 813 | + assert_equal "//www.rubyonrails.org/", redirect_to_url |
| 814 | + ensure |
| 815 | + ActionController::Base.action_on_path_relative_redirect = old_config |
| 816 | + end |
| 817 | + |
623 | 818 | private
|
624 | 819 | def with_raise_on_open_redirects
|
625 | 820 | old_raise_on_open_redirects = ActionController::Base.raise_on_open_redirects
|
|
0 commit comments