actionpack 5.0.0.beta3 → 5.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +101 -16
- data/lib/abstract_controller/base.rb +2 -4
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +2 -1
- data/lib/abstract_controller/rendering.rb +1 -0
- data/lib/action_controller/api.rb +20 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +52 -21
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +9 -10
- data/lib/action_controller/metal/force_ssl.rb +4 -4
- data/lib/action_controller/metal/http_authentication.rb +8 -3
- data/lib/action_controller/metal/implicit_render.rb +55 -17
- data/lib/action_controller/metal/instrumentation.rb +3 -2
- data/lib/action_controller/metal/live.rb +2 -2
- data/lib/action_controller/metal/mime_responds.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +1 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +3 -2
- data/lib/action_controller/metal/rescue.rb +6 -2
- data/lib/action_controller/metal/strong_parameters.rb +30 -3
- data/lib/action_controller/renderer.rb +1 -1
- data/lib/action_controller/test_case.rb +2 -2
- data/lib/action_dispatch.rb +1 -1
- data/lib/action_dispatch/http/cache.rb +49 -15
- data/lib/action_dispatch/http/filter_parameters.rb +9 -3
- data/lib/action_dispatch/http/headers.rb +2 -2
- data/lib/action_dispatch/http/mime_types.rb +1 -1
- data/lib/action_dispatch/http/request.rb +0 -1
- data/lib/action_dispatch/journey/formatter.rb +7 -2
- data/lib/action_dispatch/journey/route.rb +1 -1
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/exception_wrapper.rb +0 -1
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +5 -0
- data/lib/action_dispatch/middleware/params_parser.rb +1 -0
- data/lib/action_dispatch/middleware/reloader.rb +12 -54
- data/lib/action_dispatch/middleware/ssl.rb +19 -3
- data/lib/action_dispatch/railtie.rb +2 -0
- data/lib/action_dispatch/request/session.rb +16 -10
- data/lib/action_dispatch/routing.rb +12 -3
- data/lib/action_dispatch/routing/inspector.rb +3 -3
- data/lib/action_dispatch/routing/mapper.rb +6 -3
- data/lib/action_dispatch/routing/route_set.rb +16 -15
- data/lib/action_dispatch/routing/url_for.rb +1 -1
- data/lib/action_dispatch/testing/assertions/routing.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +43 -27
- data/lib/action_pack/gem_version.rb +1 -1
- metadata +12 -11
- data/lib/action_dispatch/middleware/load_interlock.rb +0 -21
@@ -4,9 +4,11 @@ module ActionDispatch
|
|
4
4
|
module Http
|
5
5
|
# Allows you to specify sensitive parameters which will be replaced from
|
6
6
|
# the request log by looking in the query string of the request and all
|
7
|
-
# sub-hashes of the params hash to filter.
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# sub-hashes of the params hash to filter. Filtering only certain sub-keys
|
8
|
+
# from a hash is possible by using the dot notation: 'credit_card.number'.
|
9
|
+
# If a block is given, each key and value of the params hash and all
|
10
|
+
# sub-hashes is passed to it, the value or key can be replaced using
|
11
|
+
# String#replace or similar method.
|
10
12
|
#
|
11
13
|
# env["action_dispatch.parameter_filter"] = [:password]
|
12
14
|
# => replaces the value to all keys matching /password/i with "[FILTERED]"
|
@@ -14,6 +16,10 @@ module ActionDispatch
|
|
14
16
|
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
|
15
17
|
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
16
18
|
#
|
19
|
+
# env["action_dispatch.parameter_filter"] = [ "credit_card.code" ]
|
20
|
+
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
21
|
+
# change { file: { code: "xxxx"} }
|
22
|
+
#
|
17
23
|
# env["action_dispatch.parameter_filter"] = -> (k, v) do
|
18
24
|
# v.reverse! if k =~ /secret/i
|
19
25
|
# end
|
@@ -5,7 +5,7 @@ module ActionDispatch
|
|
5
5
|
# env = { "CONTENT_TYPE" => "text/plain", "HTTP_USER_AGENT" => "curl/7.43.0" }
|
6
6
|
# headers = ActionDispatch::Http::Headers.new(env)
|
7
7
|
# headers["Content-Type"] # => "text/plain"
|
8
|
-
# headers["User-Agent"] # => "curl/7
|
8
|
+
# headers["User-Agent"] # => "curl/7.43.0"
|
9
9
|
#
|
10
10
|
# Also note that when headers are mapped to CGI-like variables by the Rack
|
11
11
|
# server, both dashes and underscores are converted to underscores. This
|
@@ -115,7 +115,7 @@ module ActionDispatch
|
|
115
115
|
|
116
116
|
private
|
117
117
|
|
118
|
-
# Converts
|
118
|
+
# Converts an HTTP header name to an environment variable name if it is
|
119
119
|
# not contained within the headers hash.
|
120
120
|
def env_name(key)
|
121
121
|
key = key.to_s
|
@@ -21,7 +21,7 @@ Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
|
|
21
21
|
Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
|
22
22
|
Mime::Type.register "application/rss+xml", :rss
|
23
23
|
Mime::Type.register "application/atom+xml", :atom
|
24
|
-
Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
|
24
|
+
Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml ), %w(yml yaml)
|
25
25
|
|
26
26
|
Mime::Type.register "multipart/form-data", :multipart_form
|
27
27
|
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
|
@@ -32,8 +32,13 @@ module ActionDispatch
|
|
32
32
|
|
33
33
|
defaults = route.defaults
|
34
34
|
required_parts = route.required_parts
|
35
|
-
|
36
|
-
|
35
|
+
|
36
|
+
route.parts.reverse_each do |key|
|
37
|
+
break if defaults[key].nil? && parameterized_parts[key].present?
|
38
|
+
break if parameterized_parts[key].to_s != defaults[key].to_s
|
39
|
+
break if required_parts.include?(key)
|
40
|
+
|
41
|
+
parameterized_parts.delete(key)
|
37
42
|
end
|
38
43
|
|
39
44
|
return [route.format(parameterized_parts), params]
|
@@ -7,7 +7,16 @@ module ActionDispatch
|
|
7
7
|
define_callbacks :call
|
8
8
|
|
9
9
|
class << self
|
10
|
-
|
10
|
+
def to_prepare(*args, &block)
|
11
|
+
ActiveSupport::Reloader.to_prepare(*args, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_cleanup(*args, &block)
|
15
|
+
ActiveSupport::Reloader.to_complete(*args, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
deprecate to_prepare: 'use ActiveSupport::Reloader.to_prepare instead',
|
19
|
+
to_cleanup: 'use ActiveSupport::Reloader.to_complete instead'
|
11
20
|
|
12
21
|
def before(*args, &block)
|
13
22
|
set_callback(:call, :before, *args, &block)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rack/body_proxy'
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class Executor
|
5
|
+
def initialize(app, executor)
|
6
|
+
@app, @executor = app, executor
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
state = @executor.run!
|
11
|
+
begin
|
12
|
+
response = @app.call(env)
|
13
|
+
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
14
|
+
ensure
|
15
|
+
state.complete! unless returned
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -37,6 +37,7 @@ module ActionDispatch
|
|
37
37
|
# The +parsers+ argument can take Hash of parsers where key is identifying
|
38
38
|
# content mime type, and value is a lambda that is going to process data.
|
39
39
|
def self.new(app, parsers = {})
|
40
|
+
ActiveSupport::Deprecation.warn('ActionDispatch::ParamsParser is deprecated and will be removed in Rails 5.1. Configure the parameter parsing in ActionDispatch::Request.parameter_parsers.')
|
40
41
|
parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
|
41
42
|
ActionDispatch::Request.parameter_parsers = ActionDispatch::Request::DEFAULT_PARSERS.merge(parsers)
|
42
43
|
app
|
@@ -23,74 +23,32 @@ module ActionDispatch
|
|
23
23
|
# middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
|
24
24
|
# or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
|
25
25
|
#
|
26
|
-
class Reloader
|
27
|
-
include ActiveSupport::Callbacks
|
28
|
-
include ActiveSupport::Deprecation::Reporting
|
29
|
-
|
30
|
-
define_callbacks :prepare
|
31
|
-
define_callbacks :cleanup
|
32
|
-
|
33
|
-
# Add a prepare callback. Prepare callbacks are run before each request, prior
|
34
|
-
# to ActionDispatch::Callback's before callbacks.
|
26
|
+
class Reloader < Executor
|
35
27
|
def self.to_prepare(*args, &block)
|
36
|
-
|
37
|
-
warn "to_prepare without a block is deprecated. Please use a block"
|
38
|
-
end
|
39
|
-
set_callback(:prepare, *args, &block)
|
28
|
+
ActiveSupport::Reloader.to_prepare(*args, &block)
|
40
29
|
end
|
41
30
|
|
42
|
-
# Add a cleanup callback. Cleanup callbacks are run after each request is
|
43
|
-
# complete (after #close is called on the response body).
|
44
31
|
def self.to_cleanup(*args, &block)
|
45
|
-
|
46
|
-
warn "to_cleanup without a block is deprecated. Please use a block"
|
47
|
-
end
|
48
|
-
set_callback(:cleanup, *args, &block)
|
32
|
+
ActiveSupport::Reloader.to_complete(*args, &block)
|
49
33
|
end
|
50
34
|
|
51
|
-
# Execute all prepare callbacks.
|
52
35
|
def self.prepare!
|
53
|
-
|
36
|
+
default_reloader.prepare!
|
54
37
|
end
|
55
38
|
|
56
|
-
# Execute all cleanup callbacks.
|
57
39
|
def self.cleanup!
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def initialize(app, condition=nil)
|
62
|
-
@app = app
|
63
|
-
@condition = condition || lambda { true }
|
64
|
-
@validated = true
|
40
|
+
default_reloader.reload!
|
65
41
|
end
|
66
42
|
|
67
|
-
|
68
|
-
|
69
|
-
prepare!
|
70
|
-
|
71
|
-
response = @app.call(env)
|
72
|
-
response[2] = ::Rack::BodyProxy.new(response[2]) { cleanup! }
|
43
|
+
class << self
|
44
|
+
attr_accessor :default_reloader # :nodoc:
|
73
45
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
46
|
+
deprecate to_prepare: 'use ActiveSupport::Reloader.to_prepare instead',
|
47
|
+
to_cleanup: 'use ActiveSupport::Reloader.to_complete instead',
|
48
|
+
prepare!: 'use Rails.application.reloader.prepare! instead',
|
49
|
+
cleanup!: 'use Rails.application.reloader.reload! instead of cleanup + prepare'
|
78
50
|
end
|
79
51
|
|
80
|
-
|
81
|
-
run_callbacks :prepare if validated?
|
82
|
-
end
|
83
|
-
|
84
|
-
def cleanup! #:nodoc:
|
85
|
-
run_callbacks :cleanup if validated?
|
86
|
-
ensure
|
87
|
-
@validated = true
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def validated? #:nodoc:
|
93
|
-
@validated
|
94
|
-
end
|
52
|
+
self.default_reloader = ActiveSupport::Reloader
|
95
53
|
end
|
96
54
|
end
|
@@ -23,7 +23,7 @@ module ActionDispatch
|
|
23
23
|
# preload lists is `18.weeks`.
|
24
24
|
# * `subdomains`: Set to `true` to tell the browser to apply these settings
|
25
25
|
# to all subdomains. This protects your cookies from interception by a
|
26
|
-
# vulnerable site on a subdomain. Defaults to `
|
26
|
+
# vulnerable site on a subdomain. Defaults to `true`.
|
27
27
|
# * `preload`: Advertise that this site may be included in browsers'
|
28
28
|
# preloaded HSTS lists. HSTS protects your site on every visit *except the
|
29
29
|
# first visit* since it hasn't seen your HSTS header yet. To close this
|
@@ -34,6 +34,10 @@ module ActionDispatch
|
|
34
34
|
# original HSTS directive until it expires. Instead, use the header to tell browsers to
|
35
35
|
# expire HSTS immediately. Setting `hsts: false` is a shortcut for
|
36
36
|
# `hsts: { expires: 0 }`.
|
37
|
+
#
|
38
|
+
# Requests can opt-out of redirection with `exclude`:
|
39
|
+
#
|
40
|
+
# config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
|
37
41
|
class SSL
|
38
42
|
# Default to 180 days, the low end for https://www.ssllabs.com/ssltest/
|
39
43
|
# and greater than the 18-week requirement for browser preload lists.
|
@@ -49,14 +53,26 @@ module ActionDispatch
|
|
49
53
|
if options[:host] || options[:port]
|
50
54
|
ActiveSupport::Deprecation.warn <<-end_warning.strip_heredoc
|
51
55
|
The `:host` and `:port` options are moving within `:redirect`:
|
52
|
-
`config.ssl_options = { redirect: { host: …, port: … }}`.
|
56
|
+
`config.ssl_options = { redirect: { host: …, port: … } }`.
|
53
57
|
end_warning
|
54
58
|
@redirect = options.slice(:host, :port)
|
55
59
|
else
|
56
60
|
@redirect = redirect
|
57
61
|
end
|
58
62
|
|
63
|
+
@exclude = @redirect && @redirect[:exclude] || proc { !@redirect }
|
59
64
|
@secure_cookies = secure_cookies
|
65
|
+
|
66
|
+
if hsts != true && hsts != false && hsts[:subdomains].nil?
|
67
|
+
hsts[:subdomains] = false
|
68
|
+
|
69
|
+
ActiveSupport::Deprecation.warn <<-end_warning.strip_heredoc
|
70
|
+
In Rails 5.1, The `:subdomains` option of HSTS config will be treated as true if
|
71
|
+
unspecified. Set `config.ssl_options = { hsts: { subdomains: false } }` to opt out
|
72
|
+
of this behavior.
|
73
|
+
end_warning
|
74
|
+
end
|
75
|
+
|
60
76
|
@hsts_header = build_hsts_header(normalize_hsts_options(hsts))
|
61
77
|
end
|
62
78
|
|
@@ -69,7 +85,7 @@ module ActionDispatch
|
|
69
85
|
flag_cookies_as_secure! headers if @secure_cookies
|
70
86
|
end
|
71
87
|
else
|
72
|
-
return redirect_to_https request
|
88
|
+
return redirect_to_https request unless @exclude.call(request)
|
73
89
|
@app.call(env)
|
74
90
|
end
|
75
91
|
end
|
@@ -39,6 +39,8 @@ module ActionDispatch
|
|
39
39
|
config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
|
40
40
|
ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
|
41
41
|
|
42
|
+
ActionDispatch::Reloader.default_reloader = app.reloader
|
43
|
+
|
42
44
|
ActionDispatch.test_app = app
|
43
45
|
end
|
44
46
|
end
|
@@ -9,7 +9,7 @@ module ActionDispatch
|
|
9
9
|
|
10
10
|
# Singleton object used to determine if an optional param wasn't specified
|
11
11
|
Unspecified = Object.new
|
12
|
-
|
12
|
+
|
13
13
|
# Creates a session hash, merging the properties of the previous session if any
|
14
14
|
def self.create(store, req, default_options)
|
15
15
|
session_was = find req
|
@@ -61,7 +61,7 @@ module ActionDispatch
|
|
61
61
|
def initialize(by, req)
|
62
62
|
@by = by
|
63
63
|
@req = req
|
64
|
-
@delegate = {}
|
64
|
+
@delegate = {}
|
65
65
|
@loaded = false
|
66
66
|
@exists = nil # we haven't checked yet
|
67
67
|
end
|
@@ -88,13 +88,13 @@ module ActionDispatch
|
|
88
88
|
# nil if the given key is not found in the session.
|
89
89
|
def [](key)
|
90
90
|
load_for_read!
|
91
|
-
@delegate[key]
|
91
|
+
@delegate[key.to_s]
|
92
92
|
end
|
93
93
|
|
94
94
|
# Returns true if the session has the given key or false.
|
95
95
|
def has_key?(key)
|
96
96
|
load_for_read!
|
97
|
-
@delegate.key?(key)
|
97
|
+
@delegate.key?(key.to_s)
|
98
98
|
end
|
99
99
|
alias :key? :has_key?
|
100
100
|
alias :include? :has_key?
|
@@ -112,7 +112,7 @@ module ActionDispatch
|
|
112
112
|
# Writes given value to given key of the session.
|
113
113
|
def []=(key, value)
|
114
114
|
load_for_write!
|
115
|
-
@delegate[key] = value
|
115
|
+
@delegate[key.to_s] = value
|
116
116
|
end
|
117
117
|
|
118
118
|
# Clears the session.
|
@@ -139,13 +139,13 @@ module ActionDispatch
|
|
139
139
|
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
|
140
140
|
def update(hash)
|
141
141
|
load_for_write!
|
142
|
-
@delegate.update hash
|
142
|
+
@delegate.update stringify_keys(hash)
|
143
143
|
end
|
144
144
|
|
145
145
|
# Deletes given key from the session.
|
146
146
|
def delete(key)
|
147
147
|
load_for_write!
|
148
|
-
@delegate.delete key
|
148
|
+
@delegate.delete key.to_s
|
149
149
|
end
|
150
150
|
|
151
151
|
# Returns value of the given key from the session, or raises +KeyError+
|
@@ -165,9 +165,9 @@ module ActionDispatch
|
|
165
165
|
def fetch(key, default=Unspecified, &block)
|
166
166
|
load_for_read!
|
167
167
|
if default == Unspecified
|
168
|
-
@delegate.fetch(key, &block)
|
168
|
+
@delegate.fetch(key.to_s, &block)
|
169
169
|
else
|
170
|
-
@delegate.fetch(key, default, &block)
|
170
|
+
@delegate.fetch(key.to_s, default, &block)
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
@@ -211,9 +211,15 @@ module ActionDispatch
|
|
211
211
|
def load!
|
212
212
|
id, session = @by.load_session @req
|
213
213
|
options[:id] = id
|
214
|
-
@delegate.replace(session)
|
214
|
+
@delegate.replace(stringify_keys(session))
|
215
215
|
@loaded = true
|
216
216
|
end
|
217
|
+
|
218
|
+
def stringify_keys(other)
|
219
|
+
other.each_with_object({}) { |(key, value), hash|
|
220
|
+
hash[key.to_s] = value
|
221
|
+
}
|
222
|
+
end
|
217
223
|
end
|
218
224
|
end
|
219
225
|
end
|
@@ -73,14 +73,14 @@ module ActionDispatch
|
|
73
73
|
# get 'post/:id' => 'posts#show'
|
74
74
|
# post 'post/:id' => 'posts#create_comment'
|
75
75
|
#
|
76
|
+
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
|
77
|
+
# URL will route to the <tt>show</tt> action.
|
78
|
+
#
|
76
79
|
# If your route needs to respond to more than one HTTP method (or all methods) then using the
|
77
80
|
# <tt>:via</tt> option on <tt>match</tt> is preferable.
|
78
81
|
#
|
79
82
|
# match 'post/:id' => 'posts#show', via: [:get, :post]
|
80
83
|
#
|
81
|
-
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
|
82
|
-
# URL will route to the <tt>show</tt> action.
|
83
|
-
#
|
84
84
|
# == Named routes
|
85
85
|
#
|
86
86
|
# Routes can be named by passing an <tt>:as</tt> option,
|
@@ -252,5 +252,14 @@ module ActionDispatch
|
|
252
252
|
|
253
253
|
SEPARATORS = %w( / . ? ) #:nodoc:
|
254
254
|
HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
|
255
|
+
|
256
|
+
#:stopdoc:
|
257
|
+
INSECURE_URL_PARAMETERS_MESSAGE = <<-MSG.squish
|
258
|
+
Attempting to generate a URL from non-sanitized request parameters!
|
259
|
+
|
260
|
+
An attacker can inject malicious data into the generated URL, such as
|
261
|
+
changing the host. Whitelist and sanitize passed parameters to be secure.
|
262
|
+
MSG
|
263
|
+
#:startdoc:
|
255
264
|
end
|
256
265
|
end
|
@@ -33,11 +33,11 @@ module ActionDispatch
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def controller
|
36
|
-
|
36
|
+
parts.include?(:controller) ? ':controller' : requirements[:controller]
|
37
37
|
end
|
38
38
|
|
39
39
|
def action
|
40
|
-
|
40
|
+
parts.include?(:action) ? ':action' : requirements[:action]
|
41
41
|
end
|
42
42
|
|
43
43
|
def internal?
|
@@ -51,7 +51,7 @@ module ActionDispatch
|
|
51
51
|
|
52
52
|
##
|
53
53
|
# This class is just used for displaying route information when someone
|
54
|
-
# executes `
|
54
|
+
# executes `rails routes` or looks at the RoutingError page.
|
55
55
|
# People should not use this class.
|
56
56
|
class RoutesInspector # :nodoc:
|
57
57
|
def initialize(routes)
|