aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md16
-rw-r--r--actionpack/lib/abstract_controller/base.rb4
-rw-r--r--actionpack/lib/abstract_controller/caching/fragments.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb2
-rw-r--r--actionpack/lib/action_controller/renderer.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb17
-rw-r--r--actionpack/lib/action_dispatch/request/utils.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb2
-rw-r--r--actionpack/test/dispatch/cookies_test.rb12
-rw-r--r--actionpack/test/dispatch/exception_wrapper_test.rb1
-rw-r--r--actionpack/test/dispatch/request_test.rb7
11 files changed, 44 insertions, 23 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index a5497aa055..a30f178190 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,19 @@
+* Purpose metadata for signed/encrypted cookies.
+
+ Rails can now thwart attacks that attempt to copy signed/encrypted value
+ of a cookie and use it as the value of another cookie.
+
+ It does so by stashing the cookie-name in the purpose field which is
+ then signed/encrypted along with the cookie value. Then, on a server-side
+ read, we verify the cookie-names and discard any attacked cookies.
+
+ Enable `action_dispatch.use_cookies_with_metadata` to use this feature, which
+ writes cookies with the new purpose and expiry metadata embedded.
+
+ Pull Request: #32937
+
+ *Assain Jaleel*
+
* Raises `ActionController::RespondToMismatchError` with confliciting `respond_to` invocations.
`respond_to` can match multiple types and lead to undefined behavior when
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index a312af6715..6e6786d0be 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -78,7 +78,9 @@ module AbstractController
# Except for public instance methods of Base and its ancestors
internal_methods +
# Be sure to include shadowed public instance methods of this class
- public_instance_methods(false)).uniq.map(&:to_s)
+ public_instance_methods(false))
+
+ methods.map!(&:to_s)
methods.to_set
end
diff --git a/actionpack/lib/abstract_controller/caching/fragments.rb b/actionpack/lib/abstract_controller/caching/fragments.rb
index f99b0830b2..febd8a67a6 100644
--- a/actionpack/lib/abstract_controller/caching/fragments.rb
+++ b/actionpack/lib/abstract_controller/caching/fragments.rb
@@ -82,7 +82,7 @@ module AbstractController
# Given a key (as described in +expire_fragment+), returns
# a key array suitable for use in reading, writing, or expiring a
# cached fragment. All keys begin with <tt>:views</tt>,
- # followed by ENV["RAILS_CACHE_ID"] or ENV["RAILS_APP_VERSION"] if set,
+ # followed by <tt>ENV["RAILS_CACHE_ID"]</tt> or <tt>ENV["RAILS_APP_VERSION"]</tt> if set,
# followed by any controller-wide key prefix values, ending
# with the specified +key+ value.
def combined_fragment_cache_key(key)
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index ea637c8150..7ed7b9d546 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -46,7 +46,7 @@ module ActionController #:nodoc:
# allowed via {CORS}[https://en.wikipedia.org/wiki/Cross-origin_resource_sharing]
# will also be able to create XHR requests. Be sure to check your
# CORS whitelist before disabling forgery protection for XHR.
- #
+ #
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method.
# By default <tt>protect_from_forgery</tt> protects your session with
# <tt>:null_session</tt> method, which provides an empty session
diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb
index 2d1523f0fc..2b4559c760 100644
--- a/actionpack/lib/action_controller/renderer.rb
+++ b/actionpack/lib/action_controller/renderer.rb
@@ -81,7 +81,7 @@ module ActionController
# * <tt>:html</tt> - Renders the provided HTML safe string, otherwise
# performs HTML escape on the string first. Sets the content type as <tt>text/html</tt>.
# * <tt>:json</tt> - Renders the provided hash or object in JSON. You don't
- # need to call <tt>.to_json<tt> on the object you want to render.
+ # need to call <tt>.to_json</tt> on the object you want to render.
# * <tt>:body</tt> - Renders provided text and sets content type of <tt>text/plain</tt>.
#
# If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, the default is
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index 1d58964862..09aab631ed 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/array/extract"
module ActionDispatch
module Http
@@ -38,8 +39,8 @@ module ActionDispatch
end
end
- deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
- deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
+ deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.".freeze) }
+ deep_strings = strings.extract! { |s| s.include?("\\.".freeze) }
regexps << Regexp.new(strings.join("|".freeze), true) unless strings.empty?
deep_regexps << Regexp.new(deep_strings.join("|".freeze), true) unless deep_strings.empty?
@@ -55,23 +56,23 @@ module ActionDispatch
@blocks = blocks
end
- def call(original_params, parents = [])
- filtered_params = original_params.class.new
+ def call(params, parents = [], original_params = params)
+ filtered_params = params.class.new
- original_params.each do |key, value|
+ params.each do |key, value|
parents.push(key) if deep_regexps
if regexps.any? { |r| key =~ r }
value = FILTERED
elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| joined =~ r }
value = FILTERED
elsif value.is_a?(Hash)
- value = call(value, parents)
+ value = call(value, parents, original_params)
elsif value.is_a?(Array)
- value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
elsif blocks.any?
key = key.dup if key.duplicable?
value = value.dup if value.duplicable?
- blocks.each { |b| b.call(key, value) }
+ blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
end
parents.pop if deep_regexps
diff --git a/actionpack/lib/action_dispatch/request/utils.rb b/actionpack/lib/action_dispatch/request/utils.rb
index 0ae464082d..fb0efb9a58 100644
--- a/actionpack/lib/action_dispatch/request/utils.rb
+++ b/actionpack/lib/action_dispatch/request/utils.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/core_ext/hash/indifferent_access"
+
module ActionDispatch
class Request
class Utils # :nodoc:
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index cba49d1a0b..413e524ef6 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -83,7 +83,7 @@ module ActionDispatch
private
def normalize_filter(filter)
if filter[:controller]
- { controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ }
+ { controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
elsif filter[:grep]
{ controller: /#{filter[:grep]}/, action: /#{filter[:grep]}/,
verb: /#{filter[:grep]}/, name: /#{filter[:grep]}/, path: /#{filter[:grep]}/ }
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 34ead0a4c0..6637c2cae9 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -1405,8 +1405,7 @@ class CookiesTest < ActionController::TestCase
assert_equal "5-2-Stable Chocolate Cookies", cookies.encrypted[:favorite]
- freeze_time do
- travel 1001.years
+ travel 1001.years do
assert_nil cookies.encrypted[:favorite]
end
@@ -1422,8 +1421,7 @@ class CookiesTest < ActionController::TestCase
assert_equal "5-2-Stable Choco Chip Cookie", cookies.signed[:favorite]
- freeze_time do
- travel 1001.years
+ travel 1001.years do
assert_nil cookies.signed[:favorite]
end
@@ -1439,8 +1437,7 @@ class CookiesTest < ActionController::TestCase
assert_equal "5-2-Stable Chocolate Cookies", cookies.encrypted[:favorite]
- freeze_time do
- travel 1001.years
+ travel 1001.years do
assert_nil cookies.encrypted[:favorite]
end
@@ -1456,8 +1453,7 @@ class CookiesTest < ActionController::TestCase
assert_equal "5-2-Stable Choco Chip Cookie", cookies.signed[:favorite]
- freeze_time do
- travel 1001.years
+ travel 1001.years do
assert_nil cookies.signed[:favorite]
end
diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb
index 600280d6b3..668469a01d 100644
--- a/actionpack/test/dispatch/exception_wrapper_test.rb
+++ b/actionpack/test/dispatch/exception_wrapper_test.rb
@@ -20,6 +20,7 @@ module ActionDispatch
setup do
@cleaner = ActiveSupport::BacktraceCleaner.new
+ @cleaner.remove_filters!
@cleaner.add_silencer { |line| line !~ /^lib/ }
end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 84a2d1f69e..0ac8713527 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -1078,10 +1078,13 @@ class RequestParameterFilter < BaseRequestTest
filter_words << lambda { |key, value|
value.reverse! if key =~ /bargain/
}
+ filter_words << lambda { |key, value, original_params|
+ value.replace("world!") if original_params["barg"]["blah"] == "bar" && key == "hello"
+ }
parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
- before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo" } } }
- after_filter["barg"] = { :bargain => "niag", "blah" => "[FILTERED]", "bar" => { "bargain" => { "blah" => "[FILTERED]" } } }
+ before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo", "hello" => "world" } } }
+ after_filter["barg"] = { :bargain => "niag", "blah" => "[FILTERED]", "bar" => { "bargain" => { "blah" => "[FILTERED]", "hello" => "world!" } } }
assert_equal after_filter, parameter_filter.filter(before_filter)
end