actionpack 6.0.3.6 → 6.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +243 -251
 - data/MIT-LICENSE +1 -1
 - data/lib/abstract_controller.rb +1 -0
 - data/lib/abstract_controller/base.rb +35 -2
 - data/lib/abstract_controller/callbacks.rb +2 -2
 - data/lib/abstract_controller/helpers.rb +105 -90
 - data/lib/abstract_controller/rendering.rb +9 -9
 - data/lib/abstract_controller/translation.rb +8 -2
 - data/lib/action_controller.rb +2 -3
 - data/lib/action_controller/api.rb +2 -2
 - data/lib/action_controller/base.rb +4 -2
 - data/lib/action_controller/caching.rb +0 -1
 - data/lib/action_controller/log_subscriber.rb +3 -3
 - data/lib/action_controller/metal.rb +2 -2
 - data/lib/action_controller/metal/conditional_get.rb +10 -2
 - data/lib/action_controller/metal/content_security_policy.rb +1 -1
 - data/lib/action_controller/metal/data_streaming.rb +1 -1
 - data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
 - data/lib/action_controller/metal/exceptions.rb +33 -0
 - data/lib/action_controller/metal/feature_policy.rb +46 -0
 - data/lib/action_controller/metal/head.rb +7 -4
 - data/lib/action_controller/metal/helpers.rb +11 -1
 - data/lib/action_controller/metal/http_authentication.rb +4 -2
 - data/lib/action_controller/metal/implicit_render.rb +1 -1
 - data/lib/action_controller/metal/instrumentation.rb +11 -9
 - data/lib/action_controller/metal/live.rb +1 -1
 - data/lib/action_controller/metal/logging.rb +20 -0
 - data/lib/action_controller/metal/mime_responds.rb +6 -2
 - data/lib/action_controller/metal/parameter_encoding.rb +35 -4
 - data/lib/action_controller/metal/params_wrapper.rb +14 -8
 - data/lib/action_controller/metal/redirecting.rb +1 -1
 - data/lib/action_controller/metal/rendering.rb +6 -0
 - data/lib/action_controller/metal/request_forgery_protection.rb +48 -24
 - data/lib/action_controller/metal/rescue.rb +1 -1
 - data/lib/action_controller/metal/strong_parameters.rb +103 -15
 - data/lib/action_controller/renderer.rb +24 -13
 - data/lib/action_controller/test_case.rb +62 -56
 - data/lib/action_dispatch.rb +3 -2
 - data/lib/action_dispatch/http/cache.rb +12 -10
 - data/lib/action_dispatch/http/content_disposition.rb +2 -2
 - data/lib/action_dispatch/http/content_security_policy.rb +5 -1
 - data/lib/action_dispatch/http/feature_policy.rb +168 -0
 - data/lib/action_dispatch/http/filter_parameters.rb +1 -1
 - data/lib/action_dispatch/http/filter_redirect.rb +1 -1
 - data/lib/action_dispatch/http/headers.rb +3 -2
 - data/lib/action_dispatch/http/mime_negotiation.rb +20 -8
 - data/lib/action_dispatch/http/mime_type.rb +28 -15
 - data/lib/action_dispatch/http/parameters.rb +1 -19
 - data/lib/action_dispatch/http/request.rb +26 -8
 - data/lib/action_dispatch/http/response.rb +17 -16
 - data/lib/action_dispatch/http/url.rb +3 -2
 - data/lib/action_dispatch/journey.rb +0 -2
 - data/lib/action_dispatch/journey/formatter.rb +53 -28
 - data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
 - data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
 - data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
 - data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
 - data/lib/action_dispatch/journey/nodes/node.rb +4 -3
 - data/lib/action_dispatch/journey/parser.rb +13 -13
 - data/lib/action_dispatch/journey/parser.y +1 -1
 - data/lib/action_dispatch/journey/path/pattern.rb +13 -18
 - data/lib/action_dispatch/journey/route.rb +7 -18
 - data/lib/action_dispatch/journey/router.rb +26 -30
 - data/lib/action_dispatch/journey/router/utils.rb +6 -4
 - data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -2
 - data/lib/action_dispatch/middleware/cookies.rb +74 -33
 - data/lib/action_dispatch/middleware/debug_exceptions.rb +10 -17
 - data/lib/action_dispatch/middleware/debug_view.rb +1 -1
 - data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -17
 - data/lib/action_dispatch/middleware/host_authorization.rb +28 -17
 - data/lib/action_dispatch/middleware/public_exceptions.rb +1 -1
 - data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
 - data/lib/action_dispatch/middleware/request_id.rb +4 -5
 - data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
 - data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
 - data/lib/action_dispatch/middleware/ssl.rb +9 -6
 - data/lib/action_dispatch/middleware/stack.rb +18 -0
 - data/lib/action_dispatch/middleware/static.rb +154 -93
 - data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +18 -0
 - data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
 - data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
 - data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
 - data/lib/action_dispatch/middleware/templates/rescues/layout.erb +88 -8
 - data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
 - data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
 - data/lib/action_dispatch/railtie.rb +3 -2
 - data/lib/action_dispatch/request/session.rb +2 -8
 - data/lib/action_dispatch/request/utils.rb +26 -2
 - data/lib/action_dispatch/routing/inspector.rb +8 -7
 - data/lib/action_dispatch/routing/mapper.rb +102 -71
 - data/lib/action_dispatch/routing/polymorphic_routes.rb +12 -11
 - data/lib/action_dispatch/routing/redirection.rb +3 -3
 - data/lib/action_dispatch/routing/route_set.rb +49 -41
 - data/lib/action_dispatch/routing/url_for.rb +1 -0
 - data/lib/action_dispatch/system_test_case.rb +29 -24
 - data/lib/action_dispatch/system_testing/browser.rb +33 -27
 - data/lib/action_dispatch/system_testing/driver.rb +6 -7
 - data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
 - data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
 - data/lib/action_dispatch/testing/assertions.rb +1 -1
 - data/lib/action_dispatch/testing/assertions/response.rb +2 -4
 - data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
 - data/lib/action_dispatch/testing/integration.rb +38 -27
 - data/lib/action_dispatch/testing/test_process.rb +29 -4
 - data/lib/action_dispatch/testing/test_request.rb +3 -3
 - data/lib/action_pack.rb +1 -1
 - data/lib/action_pack/gem_version.rb +3 -3
 - metadata +20 -21
 - data/lib/action_controller/metal/force_ssl.rb +0 -58
 - data/lib/action_dispatch/http/parameter_filter.rb +0 -12
 - data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
 - data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
 - data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
 
| 
         @@ -13,6 +13,7 @@ module ActionDispatch 
     | 
|
| 
       13 
13 
     | 
    
         
             
                    VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
         
     | 
| 
       14 
14 
     | 
    
         
             
                    VERBS.each do |v|
         
     | 
| 
       15 
15 
     | 
    
         
             
                      class_eval <<-eoc, __FILE__, __LINE__ + 1
         
     | 
| 
      
 16 
     | 
    
         
            +
                        # frozen_string_literal: true
         
     | 
| 
       16 
17 
     | 
    
         
             
                        class #{v}
         
     | 
| 
       17 
18 
     | 
    
         
             
                          def self.verb; name.split("::").last; end
         
     | 
| 
       18 
19 
     | 
    
         
             
                          def self.call(req); req.#{v.downcase}?; end
         
     | 
| 
         @@ -27,7 +28,7 @@ module ActionDispatch 
     | 
|
| 
       27 
28 
     | 
    
         
             
                        @verb = verb
         
     | 
| 
       28 
29 
     | 
    
         
             
                      end
         
     | 
| 
       29 
30 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
                      def call(request); @verb  
     | 
| 
      
 31 
     | 
    
         
            +
                      def call(request); @verb == request.request_method; end
         
     | 
| 
       31 
32 
     | 
    
         
             
                    end
         
     | 
| 
       32 
33 
     | 
    
         | 
| 
       33 
34 
     | 
    
         
             
                    class All
         
     | 
| 
         @@ -49,15 +50,10 @@ module ActionDispatch 
     | 
|
| 
       49 
50 
     | 
    
         
             
                    end
         
     | 
| 
       50 
51 
     | 
    
         
             
                  end
         
     | 
| 
       51 
52 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
                  def self.build(name, app, path, constraints, required_defaults, defaults)
         
     | 
| 
       53 
     | 
    
         
            -
                    request_method_match = verb_matcher(constraints.delete(:request_method))
         
     | 
| 
       54 
     | 
    
         
            -
                    new name, app, path, constraints, required_defaults, defaults, request_method_match, 0, {}
         
     | 
| 
       55 
     | 
    
         
            -
                  end
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
53 
     | 
    
         
             
                  ##
         
     | 
| 
       58 
54 
     | 
    
         
             
                  # +path+ is a path constraint.
         
     | 
| 
       59 
55 
     | 
    
         
             
                  # +constraints+ is a hash of constraints to be applied to this route.
         
     | 
| 
       60 
     | 
    
         
            -
                  def initialize(name 
     | 
| 
      
 56 
     | 
    
         
            +
                  def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
         
     | 
| 
       61 
57 
     | 
    
         
             
                    @name        = name
         
     | 
| 
       62 
58 
     | 
    
         
             
                    @app         = app
         
     | 
| 
       63 
59 
     | 
    
         
             
                    @path        = path
         
     | 
| 
         @@ -92,7 +88,7 @@ module ActionDispatch 
     | 
|
| 
       92 
88 
     | 
    
         
             
                    end
         
     | 
| 
       93 
89 
     | 
    
         
             
                  end
         
     | 
| 
       94 
90 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
                  # Needed for `rails routes`. Picks up succinctly defined requirements
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # Needed for `bin/rails routes`. Picks up succinctly defined requirements
         
     | 
| 
       96 
92 
     | 
    
         
             
                  # for a route, for example route
         
     | 
| 
       97 
93 
     | 
    
         
             
                  #
         
     | 
| 
       98 
94 
     | 
    
         
             
                  #   get 'photo/:id', :controller => 'photos', :action => 'show',
         
     | 
| 
         @@ -115,18 +111,11 @@ module ActionDispatch 
     | 
|
| 
       115 
111 
     | 
    
         
             
                  end
         
     | 
| 
       116 
112 
     | 
    
         | 
| 
       117 
113 
     | 
    
         
             
                  def score(supplied_keys)
         
     | 
| 
       118 
     | 
    
         
            -
                     
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                    required_keys.each do |k|
         
     | 
| 
      
 114 
     | 
    
         
            +
                    path.required_names.each do |k|
         
     | 
| 
       121 
115 
     | 
    
         
             
                      return -1 unless supplied_keys.include?(k)
         
     | 
| 
       122 
116 
     | 
    
         
             
                    end
         
     | 
| 
       123 
117 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
                     
     | 
| 
       125 
     | 
    
         
            -
                    path.names.each do |k|
         
     | 
| 
       126 
     | 
    
         
            -
                      score += 1 if supplied_keys.include?(k)
         
     | 
| 
       127 
     | 
    
         
            -
                    end
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                    score + (required_defaults.length * 2)
         
     | 
| 
      
 118 
     | 
    
         
            +
                    (required_defaults.length * 2) + path.names.count { |k| supplied_keys.include?(k) }
         
     | 
| 
       130 
119 
     | 
    
         
             
                  end
         
     | 
| 
       131 
120 
     | 
    
         | 
| 
       132 
121 
     | 
    
         
             
                  def parts
         
     | 
| 
         @@ -153,7 +142,7 @@ module ActionDispatch 
     | 
|
| 
       153 
142 
     | 
    
         
             
                  end
         
     | 
| 
       154 
143 
     | 
    
         | 
| 
       155 
144 
     | 
    
         
             
                  def glob?
         
     | 
| 
       156 
     | 
    
         
            -
                     
     | 
| 
      
 145 
     | 
    
         
            +
                    path.spec.any?(Nodes::Star)
         
     | 
| 
       157 
146 
     | 
    
         
             
                  end
         
     | 
| 
       158 
147 
     | 
    
         | 
| 
       159 
148 
     | 
    
         
             
                  def dispatcher?
         
     | 
| 
         @@ -40,11 +40,12 @@ module ActionDispatch 
     | 
|
| 
       40 
40 
     | 
    
         
             
                        req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
         
     | 
| 
       41 
41 
     | 
    
         
             
                      end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
                       
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 43 
     | 
    
         
            +
                      tmp_params = set_params.merge route.defaults
         
     | 
| 
      
 44 
     | 
    
         
            +
                      parameters.each_pair { |key, val|
         
     | 
| 
      
 45 
     | 
    
         
            +
                        tmp_params[key] = val.force_encoding(::Encoding::UTF_8)
         
     | 
| 
       45 
46 
     | 
    
         
             
                      }
         
     | 
| 
       46 
47 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                      req.path_parameters =  
     | 
| 
      
 48 
     | 
    
         
            +
                      req.path_parameters = tmp_params
         
     | 
| 
       48 
49 
     | 
    
         | 
| 
       49 
50 
     | 
    
         
             
                      status, headers, body = route.app.serve(req)
         
     | 
| 
       50 
51 
     | 
    
         | 
| 
         @@ -65,7 +66,8 @@ module ActionDispatch 
     | 
|
| 
       65 
66 
     | 
    
         
             
                    find_routes(rails_req).each do |match, parameters, route|
         
     | 
| 
       66 
67 
     | 
    
         
             
                      unless route.path.anchored
         
     | 
| 
       67 
68 
     | 
    
         
             
                        rails_req.script_name = match.to_s
         
     | 
| 
       68 
     | 
    
         
            -
                        rails_req.path_info   = match.post_match 
     | 
| 
      
 69 
     | 
    
         
            +
                        rails_req.path_info   = match.post_match
         
     | 
| 
      
 70 
     | 
    
         
            +
                        rails_req.path_info   = "/" + rails_req.path_info unless rails_req.path_info.start_with? "/"
         
     | 
| 
       69 
71 
     | 
    
         
             
                      end
         
     | 
| 
       70 
72 
     | 
    
         | 
| 
       71 
73 
     | 
    
         
             
                      parameters = route.defaults.merge parameters
         
     | 
| 
         @@ -105,23 +107,24 @@ module ActionDispatch 
     | 
|
| 
       105 
107 
     | 
    
         
             
                    end
         
     | 
| 
       106 
108 
     | 
    
         | 
| 
       107 
109 
     | 
    
         
             
                    def find_routes(req)
         
     | 
| 
       108 
     | 
    
         
            -
                       
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
      
 110 
     | 
    
         
            +
                      path_info = req.path_info
         
     | 
| 
      
 111 
     | 
    
         
            +
                      routes = filter_routes(path_info).concat custom_routes.find_all { |r|
         
     | 
| 
      
 112 
     | 
    
         
            +
                        r.path.match?(path_info)
         
     | 
| 
       110 
113 
     | 
    
         
             
                      }
         
     | 
| 
       111 
114 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
                       
     | 
| 
       113 
     | 
    
         
            -
                         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
                        end
         
     | 
| 
      
 115 
     | 
    
         
            +
                      if req.head?
         
     | 
| 
      
 116 
     | 
    
         
            +
                        routes = match_head_routes(routes, req)
         
     | 
| 
      
 117 
     | 
    
         
            +
                      else
         
     | 
| 
      
 118 
     | 
    
         
            +
                        routes.select! { |r| r.matches?(req) }
         
     | 
| 
      
 119 
     | 
    
         
            +
                      end
         
     | 
| 
       118 
120 
     | 
    
         | 
| 
       119 
121 
     | 
    
         
             
                      routes.sort_by!(&:precedence)
         
     | 
| 
       120 
122 
     | 
    
         | 
| 
       121 
123 
     | 
    
         
             
                      routes.map! { |r|
         
     | 
| 
       122 
     | 
    
         
            -
                        match_data = r.path.match( 
     | 
| 
      
 124 
     | 
    
         
            +
                        match_data = r.path.match(path_info)
         
     | 
| 
       123 
125 
     | 
    
         
             
                        path_parameters = {}
         
     | 
| 
       124 
     | 
    
         
            -
                        match_data.names. 
     | 
| 
      
 126 
     | 
    
         
            +
                        match_data.names.each_with_index { |name, i|
         
     | 
| 
      
 127 
     | 
    
         
            +
                          val = match_data[i + 1]
         
     | 
| 
       125 
128 
     | 
    
         
             
                          path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
         
     | 
| 
       126 
129 
     | 
    
         
             
                        }
         
     | 
| 
       127 
130 
     | 
    
         
             
                        [match_data, path_parameters, r]
         
     | 
| 
         @@ -129,24 +132,17 @@ module ActionDispatch 
     | 
|
| 
       129 
132 
     | 
    
         
             
                    end
         
     | 
| 
       130 
133 
     | 
    
         | 
| 
       131 
134 
     | 
    
         
             
                    def match_head_routes(routes, req)
         
     | 
| 
       132 
     | 
    
         
            -
                       
     | 
| 
       133 
     | 
    
         
            -
                      head_routes  
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
                       
     | 
| 
       136 
     | 
    
         
            -
                         
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                        end
         
     | 
| 
       142 
     | 
    
         
            -
                      else
         
     | 
| 
       143 
     | 
    
         
            -
                        head_routes
         
     | 
| 
      
 135 
     | 
    
         
            +
                      head_routes = routes.select { |r| r.requires_matching_verb? && r.matches?(req) }
         
     | 
| 
      
 136 
     | 
    
         
            +
                      return head_routes unless head_routes.empty?
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 139 
     | 
    
         
            +
                        req.request_method = "GET"
         
     | 
| 
      
 140 
     | 
    
         
            +
                        routes.select! { |r| r.matches?(req) }
         
     | 
| 
      
 141 
     | 
    
         
            +
                        routes
         
     | 
| 
      
 142 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 143 
     | 
    
         
            +
                        req.request_method = "HEAD"
         
     | 
| 
       144 
144 
     | 
    
         
             
                      end
         
     | 
| 
       145 
145 
     | 
    
         
             
                    end
         
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                    def match_routes(routes, req)
         
     | 
| 
       148 
     | 
    
         
            -
                      routes.select { |r| r.matches?(req) }
         
     | 
| 
       149 
     | 
    
         
            -
                    end
         
     | 
| 
       150 
146 
     | 
    
         
             
                end
         
     | 
| 
       151 
147 
     | 
    
         
             
              end
         
     | 
| 
       152 
148 
     | 
    
         
             
            end
         
     | 
| 
         @@ -19,11 +19,13 @@ module ActionDispatch 
     | 
|
| 
       19 
19 
     | 
    
         
             
                      encoding = path.encoding
         
     | 
| 
       20 
20 
     | 
    
         
             
                      path = +"/#{path}"
         
     | 
| 
       21 
21 
     | 
    
         
             
                      path.squeeze!("/")
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                      path 
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      unless path == "/"
         
     | 
| 
      
 24 
     | 
    
         
            +
                        path.delete_suffix!("/")
         
     | 
| 
      
 25 
     | 
    
         
            +
                        path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
         
     | 
| 
      
 26 
     | 
    
         
            +
                      end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       25 
28 
     | 
    
         
             
                      path.force_encoding(encoding)
         
     | 
| 
       26 
     | 
    
         
            -
                      path
         
     | 
| 
       27 
29 
     | 
    
         
             
                    end
         
     | 
| 
       28 
30 
     | 
    
         | 
| 
       29 
31 
     | 
    
         
             
                    # URI path and fragment escaping
         
     | 
| 
         @@ -24,7 +24,7 @@ module ActionDispatch 
     | 
|
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
                private
         
     | 
| 
       26 
26 
     | 
    
         
             
                  def actionable_request?(request)
         
     | 
| 
       27 
     | 
    
         
            -
                    request.get_header("action_dispatch.show_detailed_exceptions") 
     | 
| 
      
 27 
     | 
    
         
            +
                    request.get_header("action_dispatch.show_detailed_exceptions") && request.post? && request.path == endpoint
         
     | 
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                  def redirect_to(location)
         
     | 
| 
         @@ -33,7 +33,7 @@ module ActionDispatch 
     | 
|
| 
       33 
33 
     | 
    
         
             
                    if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
         
     | 
| 
       34 
34 
     | 
    
         
             
                      body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
         
     | 
| 
       35 
35 
     | 
    
         
             
                    else
         
     | 
| 
       36 
     | 
    
         
            -
                      return [400, {"Content-Type" => "text/plain"}, ["Invalid redirection URI"]]
         
     | 
| 
      
 36 
     | 
    
         
            +
                      return [400, { "Content-Type" => "text/plain" }, ["Invalid redirection URI"]]
         
     | 
| 
       37 
37 
     | 
    
         
             
                    end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
                    [302, {
         
     | 
| 
         @@ -69,6 +69,10 @@ module ActionDispatch 
     | 
|
| 
       69 
69 
     | 
    
         
             
                  get_header Cookies::COOKIES_SERIALIZER
         
     | 
| 
       70 
70 
     | 
    
         
             
                end
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
      
 72 
     | 
    
         
            +
                def cookies_same_site_protection
         
     | 
| 
      
 73 
     | 
    
         
            +
                  get_header(Cookies::COOKIES_SAME_SITE_PROTECTION) || Proc.new { }
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
       72 
76 
     | 
    
         
             
                def cookies_digest
         
     | 
| 
       73 
77 
     | 
    
         
             
                  get_header Cookies::COOKIES_DIGEST
         
     | 
| 
       74 
78 
     | 
    
         
             
                end
         
     | 
| 
         @@ -84,11 +88,10 @@ module ActionDispatch 
     | 
|
| 
       84 
88 
     | 
    
         
             
                # :startdoc:
         
     | 
| 
       85 
89 
     | 
    
         
             
              end
         
     | 
| 
       86 
90 
     | 
    
         | 
| 
       87 
     | 
    
         
            -
              #  
     | 
| 
      
 91 
     | 
    
         
            +
              # Read and write data to cookies through ActionController#cookies.
         
     | 
| 
       88 
92 
     | 
    
         
             
              #
         
     | 
| 
       89 
     | 
    
         
            -
              #  
     | 
| 
       90 
     | 
    
         
            -
              #  
     | 
| 
       91 
     | 
    
         
            -
              # the cookie object itself back, just the value it holds.
         
     | 
| 
      
 93 
     | 
    
         
            +
              # When reading cookie data, the data is read from the HTTP request header, Cookie.
         
     | 
| 
      
 94 
     | 
    
         
            +
              # When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie.
         
     | 
| 
       92 
95 
     | 
    
         
             
              #
         
     | 
| 
       93 
96 
     | 
    
         
             
              # Examples of writing:
         
     | 
| 
       94 
97 
     | 
    
         
             
              #
         
     | 
| 
         @@ -150,8 +153,10 @@ module ActionDispatch 
     | 
|
| 
       150 
153 
     | 
    
         
             
              # * <tt>:domain</tt> - The domain for which this cookie applies so you can
         
     | 
| 
       151 
154 
     | 
    
         
             
              #   restrict to the domain level. If you use a schema like www.example.com
         
     | 
| 
       152 
155 
     | 
    
         
             
              #   and want to share session with user.example.com set <tt>:domain</tt>
         
     | 
| 
       153 
     | 
    
         
            -
              #   to <tt>:all</tt>.  
     | 
| 
       154 
     | 
    
         
            -
              #    
     | 
| 
      
 156 
     | 
    
         
            +
              #   to <tt>:all</tt>. To support multiple domains, provide an array, and
         
     | 
| 
      
 157 
     | 
    
         
            +
              #   the first domain matching <tt>request.host</tt> will be used. Make
         
     | 
| 
      
 158 
     | 
    
         
            +
              #   sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
         
     | 
| 
      
 159 
     | 
    
         
            +
              #   <tt>Array</tt> again when deleting cookies.
         
     | 
| 
       155 
160 
     | 
    
         
             
              #
         
     | 
| 
       156 
161 
     | 
    
         
             
              #     domain: nil  # Does not set cookie domain. (default)
         
     | 
| 
       157 
162 
     | 
    
         
             
              #     domain: :all # Allow the cookie for the top most level
         
     | 
| 
         @@ -181,6 +186,7 @@ module ActionDispatch 
     | 
|
| 
       181 
186 
     | 
    
         
             
                COOKIES_SERIALIZER = "action_dispatch.cookies_serializer"
         
     | 
| 
       182 
187 
     | 
    
         
             
                COOKIES_DIGEST = "action_dispatch.cookies_digest"
         
     | 
| 
       183 
188 
     | 
    
         
             
                COOKIES_ROTATIONS = "action_dispatch.cookies_rotations"
         
     | 
| 
      
 189 
     | 
    
         
            +
                COOKIES_SAME_SITE_PROTECTION = "action_dispatch.cookies_same_site_protection"
         
     | 
| 
       184 
190 
     | 
    
         
             
                USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata"
         
     | 
| 
       185 
191 
     | 
    
         | 
| 
       186 
192 
     | 
    
         
             
                # Cookies can typically store 4096 bytes.
         
     | 
| 
         @@ -259,6 +265,12 @@ module ActionDispatch 
     | 
|
| 
       259 
265 
     | 
    
         
             
                        request.use_authenticated_cookie_encryption
         
     | 
| 
       260 
266 
     | 
    
         
             
                    end
         
     | 
| 
       261 
267 
     | 
    
         | 
| 
      
 268 
     | 
    
         
            +
                    def prepare_upgrade_legacy_hmac_aes_cbc_cookies?
         
     | 
| 
      
 269 
     | 
    
         
            +
                      request.secret_key_base.present? &&
         
     | 
| 
      
 270 
     | 
    
         
            +
                        request.authenticated_encrypted_cookie_salt.present? &&
         
     | 
| 
      
 271 
     | 
    
         
            +
                        !request.use_authenticated_cookie_encryption
         
     | 
| 
      
 272 
     | 
    
         
            +
                    end
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
       262 
274 
     | 
    
         
             
                    def encrypted_cookie_cipher
         
     | 
| 
       263 
275 
     | 
    
         
             
                      request.encrypted_cookie_cipher || "aes-256-gcm"
         
     | 
| 
       264 
276 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -286,9 +298,9 @@ module ActionDispatch 
     | 
|
| 
       286 
298 
     | 
    
         
             
                  DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
         
     | 
| 
       287 
299 
     | 
    
         | 
| 
       288 
300 
     | 
    
         
             
                  def self.build(req, cookies)
         
     | 
| 
       289 
     | 
    
         
            -
                    new(req) 
     | 
| 
       290 
     | 
    
         
            -
             
     | 
| 
       291 
     | 
    
         
            -
                     
     | 
| 
      
 301 
     | 
    
         
            +
                    jar = new(req)
         
     | 
| 
      
 302 
     | 
    
         
            +
                    jar.update(cookies)
         
     | 
| 
      
 303 
     | 
    
         
            +
                    jar
         
     | 
| 
       292 
304 
     | 
    
         
             
                  end
         
     | 
| 
       293 
305 
     | 
    
         | 
| 
       294 
306 
     | 
    
         
             
                  attr_reader :request
         
     | 
| 
         @@ -346,28 +358,6 @@ module ActionDispatch 
     | 
|
| 
       346 
358 
     | 
    
         
             
                    @cookies.map { |k, v| "#{escape(k)}=#{escape(v)}" }.join "; "
         
     | 
| 
       347 
359 
     | 
    
         
             
                  end
         
     | 
| 
       348 
360 
     | 
    
         | 
| 
       349 
     | 
    
         
            -
                  def handle_options(options) # :nodoc:
         
     | 
| 
       350 
     | 
    
         
            -
                    if options[:expires].respond_to?(:from_now)
         
     | 
| 
       351 
     | 
    
         
            -
                      options[:expires] = options[:expires].from_now
         
     | 
| 
       352 
     | 
    
         
            -
                    end
         
     | 
| 
       353 
     | 
    
         
            -
             
     | 
| 
       354 
     | 
    
         
            -
                    options[:path] ||= "/"
         
     | 
| 
       355 
     | 
    
         
            -
             
     | 
| 
       356 
     | 
    
         
            -
                    if options[:domain] == :all || options[:domain] == "all"
         
     | 
| 
       357 
     | 
    
         
            -
                      # If there is a provided tld length then we use it otherwise default domain regexp.
         
     | 
| 
       358 
     | 
    
         
            -
                      domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
         
     | 
| 
       359 
     | 
    
         
            -
             
     | 
| 
       360 
     | 
    
         
            -
                      # If host is not ip and matches domain regexp.
         
     | 
| 
       361 
     | 
    
         
            -
                      # (ip confirms to domain regexp so we explicitly check for ip)
         
     | 
| 
       362 
     | 
    
         
            -
                      options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp)
         
     | 
| 
       363 
     | 
    
         
            -
                        ".#{$&}"
         
     | 
| 
       364 
     | 
    
         
            -
                      end
         
     | 
| 
       365 
     | 
    
         
            -
                    elsif options[:domain].is_a? Array
         
     | 
| 
       366 
     | 
    
         
            -
                      # If host matches one of the supplied domains without a dot in front of it.
         
     | 
| 
       367 
     | 
    
         
            -
                      options[:domain] = options[:domain].find { |domain| request.host.include? domain.sub(/^\./, "") }
         
     | 
| 
       368 
     | 
    
         
            -
                    end
         
     | 
| 
       369 
     | 
    
         
            -
                  end
         
     | 
| 
       370 
     | 
    
         
            -
             
     | 
| 
       371 
361 
     | 
    
         
             
                  # Sets the cookie named +name+. The second argument may be the cookie's
         
     | 
| 
       372 
362 
     | 
    
         
             
                  # value or a hash of options as documented above.
         
     | 
| 
       373 
363 
     | 
    
         
             
                  def []=(name, options)
         
     | 
| 
         @@ -447,6 +437,34 @@ module ActionDispatch 
     | 
|
| 
       447 
437 
     | 
    
         
             
                    def write_cookie?(cookie)
         
     | 
| 
       448 
438 
     | 
    
         
             
                      request.ssl? || !cookie[:secure] || always_write_cookie
         
     | 
| 
       449 
439 
     | 
    
         
             
                    end
         
     | 
| 
      
 440 
     | 
    
         
            +
             
     | 
| 
      
 441 
     | 
    
         
            +
                    def handle_options(options)
         
     | 
| 
      
 442 
     | 
    
         
            +
                      if options[:expires].respond_to?(:from_now)
         
     | 
| 
      
 443 
     | 
    
         
            +
                        options[:expires] = options[:expires].from_now
         
     | 
| 
      
 444 
     | 
    
         
            +
                      end
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
                      options[:path]      ||= "/"
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
      
 448 
     | 
    
         
            +
                      cookies_same_site_protection = request.cookies_same_site_protection
         
     | 
| 
      
 449 
     | 
    
         
            +
                      options[:same_site] ||= cookies_same_site_protection.call(request)
         
     | 
| 
      
 450 
     | 
    
         
            +
             
     | 
| 
      
 451 
     | 
    
         
            +
                      if options[:domain] == :all || options[:domain] == "all"
         
     | 
| 
      
 452 
     | 
    
         
            +
                        # If there is a provided tld length then we use it otherwise default domain regexp.
         
     | 
| 
      
 453 
     | 
    
         
            +
                        domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
         
     | 
| 
      
 454 
     | 
    
         
            +
             
     | 
| 
      
 455 
     | 
    
         
            +
                        # If host is not ip and matches domain regexp.
         
     | 
| 
      
 456 
     | 
    
         
            +
                        # (ip confirms to domain regexp so we explicitly check for ip)
         
     | 
| 
      
 457 
     | 
    
         
            +
                        options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp)
         
     | 
| 
      
 458 
     | 
    
         
            +
                          ".#{$&}"
         
     | 
| 
      
 459 
     | 
    
         
            +
                        end
         
     | 
| 
      
 460 
     | 
    
         
            +
                      elsif options[:domain].is_a? Array
         
     | 
| 
      
 461 
     | 
    
         
            +
                        # If host matches one of the supplied domains.
         
     | 
| 
      
 462 
     | 
    
         
            +
                        options[:domain] = options[:domain].find do |domain|
         
     | 
| 
      
 463 
     | 
    
         
            +
                          domain = domain.delete_prefix(".")
         
     | 
| 
      
 464 
     | 
    
         
            +
                          request.host == domain || request.host.end_with?(".#{domain}")
         
     | 
| 
      
 465 
     | 
    
         
            +
                        end
         
     | 
| 
      
 466 
     | 
    
         
            +
                      end
         
     | 
| 
      
 467 
     | 
    
         
            +
                    end
         
     | 
| 
       450 
468 
     | 
    
         
             
                end
         
     | 
| 
       451 
469 
     | 
    
         | 
| 
       452 
470 
     | 
    
         
             
                class AbstractCookieJar # :nodoc:
         
     | 
| 
         @@ -458,7 +476,13 @@ module ActionDispatch 
     | 
|
| 
       458 
476 
     | 
    
         | 
| 
       459 
477 
     | 
    
         
             
                  def [](name)
         
     | 
| 
       460 
478 
     | 
    
         
             
                    if data = @parent_jar[name.to_s]
         
     | 
| 
       461 
     | 
    
         
            -
                      parse(name, data, purpose: "cookie.#{name}") 
     | 
| 
      
 479 
     | 
    
         
            +
                      result = parse(name, data, purpose: "cookie.#{name}")
         
     | 
| 
      
 480 
     | 
    
         
            +
             
     | 
| 
      
 481 
     | 
    
         
            +
                      if result.nil?
         
     | 
| 
      
 482 
     | 
    
         
            +
                        parse(name, data)
         
     | 
| 
      
 483 
     | 
    
         
            +
                      else
         
     | 
| 
      
 484 
     | 
    
         
            +
                        result
         
     | 
| 
      
 485 
     | 
    
         
            +
                      end
         
     | 
| 
       462 
486 
     | 
    
         
             
                    end
         
     | 
| 
       463 
487 
     | 
    
         
             
                  end
         
     | 
| 
       464 
488 
     | 
    
         | 
| 
         @@ -502,6 +526,18 @@ module ActionDispatch 
     | 
|
| 
       502 
526 
     | 
    
         
             
                    end
         
     | 
| 
       503 
527 
     | 
    
         
             
                end
         
     | 
| 
       504 
528 
     | 
    
         | 
| 
      
 529 
     | 
    
         
            +
                class MarshalWithJsonFallback # :nodoc:
         
     | 
| 
      
 530 
     | 
    
         
            +
                  def self.load(value)
         
     | 
| 
      
 531 
     | 
    
         
            +
                    Marshal.load(value)
         
     | 
| 
      
 532 
     | 
    
         
            +
                  rescue TypeError => e
         
     | 
| 
      
 533 
     | 
    
         
            +
                    ActiveSupport::JSON.decode(value) rescue raise e
         
     | 
| 
      
 534 
     | 
    
         
            +
                  end
         
     | 
| 
      
 535 
     | 
    
         
            +
             
     | 
| 
      
 536 
     | 
    
         
            +
                  def self.dump(value)
         
     | 
| 
      
 537 
     | 
    
         
            +
                    Marshal.dump(value)
         
     | 
| 
      
 538 
     | 
    
         
            +
                  end
         
     | 
| 
      
 539 
     | 
    
         
            +
                end
         
     | 
| 
      
 540 
     | 
    
         
            +
             
     | 
| 
       505 
541 
     | 
    
         
             
                class JsonSerializer # :nodoc:
         
     | 
| 
       506 
542 
     | 
    
         
             
                  def self.load(value)
         
     | 
| 
       507 
543 
     | 
    
         
             
                    ActiveSupport::JSON.decode(value)
         
     | 
| 
         @@ -549,7 +585,7 @@ module ActionDispatch 
     | 
|
| 
       549 
585 
     | 
    
         
             
                      serializer = request.cookies_serializer || :marshal
         
     | 
| 
       550 
586 
     | 
    
         
             
                      case serializer
         
     | 
| 
       551 
587 
     | 
    
         
             
                      when :marshal
         
     | 
| 
       552 
     | 
    
         
            -
                         
     | 
| 
      
 588 
     | 
    
         
            +
                        MarshalWithJsonFallback
         
     | 
| 
       553 
589 
     | 
    
         
             
                      when :json, :hybrid
         
     | 
| 
       554 
590 
     | 
    
         
             
                        JsonSerializer
         
     | 
| 
       555 
591 
     | 
    
         
             
                      else
         
     | 
| 
         @@ -619,6 +655,11 @@ module ActionDispatch 
     | 
|
| 
       619 
655 
     | 
    
         
             
                      sign_secret = request.key_generator.generate_key(request.encrypted_signed_cookie_salt)
         
     | 
| 
       620 
656 
     | 
    
         | 
| 
       621 
657 
     | 
    
         
             
                      @encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER)
         
     | 
| 
      
 658 
     | 
    
         
            +
                    elsif prepare_upgrade_legacy_hmac_aes_cbc_cookies?
         
     | 
| 
      
 659 
     | 
    
         
            +
                      future_cipher = encrypted_cookie_cipher
         
     | 
| 
      
 660 
     | 
    
         
            +
                      secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len(future_cipher))
         
     | 
| 
      
 661 
     | 
    
         
            +
             
     | 
| 
      
 662 
     | 
    
         
            +
                      @encryptor.rotate(secret, nil, cipher: future_cipher, serializer: SERIALIZER)
         
     | 
| 
       622 
663 
     | 
    
         
             
                    end
         
     | 
| 
       623 
664 
     | 
    
         
             
                  end
         
     | 
| 
       624 
665 
     | 
    
         | 
| 
         @@ -4,10 +4,7 @@ require "action_dispatch/http/request" 
     | 
|
| 
       4 
4 
     | 
    
         
             
            require "action_dispatch/middleware/exception_wrapper"
         
     | 
| 
       5 
5 
     | 
    
         
             
            require "action_dispatch/routing/inspector"
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            require "active_support/actionable_error"
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
7 
     | 
    
         
             
            require "action_view"
         
     | 
| 
       10 
     | 
    
         
            -
            require "action_view/base"
         
     | 
| 
       11 
8 
     | 
    
         | 
| 
       12 
9 
     | 
    
         
             
            module ActionDispatch
         
     | 
| 
       13 
10 
     | 
    
         
             
              # This middleware is responsible for logging exceptions and
         
     | 
| 
         @@ -63,8 +60,8 @@ module ActionDispatch 
     | 
|
| 
       63 
60 
     | 
    
         
             
                    if request.get_header("action_dispatch.show_detailed_exceptions")
         
     | 
| 
       64 
61 
     | 
    
         
             
                      begin
         
     | 
| 
       65 
62 
     | 
    
         
             
                        content_type = request.formats.first
         
     | 
| 
       66 
     | 
    
         
            -
                      rescue  
     | 
| 
       67 
     | 
    
         
            -
                         
     | 
| 
      
 63 
     | 
    
         
            +
                      rescue ActionDispatch::Http::MimeNegotiation::InvalidType
         
     | 
| 
      
 64 
     | 
    
         
            +
                        content_type = Mime[:text]
         
     | 
| 
       68 
65 
     | 
    
         
             
                      end
         
     | 
| 
       69 
66 
     | 
    
         | 
| 
       70 
67 
     | 
    
         
             
                      if api_request?(content_type)
         
     | 
| 
         @@ -140,20 +137,16 @@ module ActionDispatch 
     | 
|
| 
       140 
137 
     | 
    
         
             
                    return unless logger
         
     | 
| 
       141 
138 
     | 
    
         | 
| 
       142 
139 
     | 
    
         
             
                    exception = wrapper.exception
         
     | 
| 
      
 140 
     | 
    
         
            +
                    trace = wrapper.exception_trace
         
     | 
| 
       143 
141 
     | 
    
         | 
| 
       144 
     | 
    
         
            -
                     
     | 
| 
       145 
     | 
    
         
            -
                     
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                     
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
                      message << "#{exception.class} (#{exception.message}):"
         
     | 
| 
       151 
     | 
    
         
            -
                      message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
         
     | 
| 
       152 
     | 
    
         
            -
                      message << "  "
         
     | 
| 
       153 
     | 
    
         
            -
                      message.concat(trace)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    message = []
         
     | 
| 
      
 143 
     | 
    
         
            +
                    message << "  "
         
     | 
| 
      
 144 
     | 
    
         
            +
                    message << "#{exception.class} (#{exception.message}):"
         
     | 
| 
      
 145 
     | 
    
         
            +
                    message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
         
     | 
| 
      
 146 
     | 
    
         
            +
                    message << "  "
         
     | 
| 
      
 147 
     | 
    
         
            +
                    message.concat(trace)
         
     | 
| 
       154 
148 
     | 
    
         | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                    end
         
     | 
| 
      
 149 
     | 
    
         
            +
                    log_array(logger, message)
         
     | 
| 
       157 
150 
     | 
    
         
             
                  end
         
     | 
| 
       158 
151 
     | 
    
         | 
| 
       159 
152 
     | 
    
         
             
                  def log_array(logger, array)
         
     |