diff options
12 files changed, 94 insertions, 30 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 6e7a68cdf8..533925ebe1 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -630,7 +630,7 @@ module ActionDispatch secret = key_generator.generate_key(request.encrypted_cookie_salt || "")[0, ActiveSupport::MessageEncryptor.key_len] sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || "") - @legacy_encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) + @legacy_encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end def decrypt_and_verify_legacy_encrypted_message(name, signed_message) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 122c42c5bd..ceaea40d18 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,11 @@ +* Generate field ids in `collection_check_boxes` and `collection_radio_buttons`. + + This makes sure that the labels are linked up with the fields. + + Fixes #29014. + + *Yuji Yaginuma* + * Add `:json` type to `auto_discovery_link_tag` to support [JSON Feeds](https://jsonfeed.org/version/1) *Mike Gunderloy* diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb index 7252d4f2d9..e02b7bdb2e 100644 --- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -10,6 +10,7 @@ module ActionView def check_box(extra_html_options = {}) html_options = extra_html_options.merge(@input_html_options) html_options[:multiple] = true + html_options[:skip_default_ids] = false @template_object.check_box(@object_name, @method_name, html_options, @value, nil) end end diff --git a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb index a5f72af9ff..f085a5fb73 100644 --- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb +++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -9,6 +9,7 @@ module ActionView class RadioButtonBuilder < Builder # :nodoc: def radio_button(extra_html_options = {}) html_options = extra_html_options.merge(@input_html_options) + html_options[:skip_default_ids] = false @template_object.radio_button(@object_name, @method_name, @value, html_options) end end diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb index df580f0369..ecdd5ce672 100644 --- a/actionview/test/template/form_helper/form_with_test.rb +++ b/actionview/test/template/form_helper/form_with_test.rb @@ -403,9 +403,9 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input type='hidden' name='post[active]' value='' />" \ - "<input name='post[active]' type='radio' value='true' />" \ + "<input name='post[active]' type='radio' value='true' id='post_active_true' />" \ "<label for='post_active_true'>true</label>" \ - "<input checked='checked' name='post[active]' type='radio' value='false' />" \ + "<input checked='checked' name='post[active]' type='radio' value='false' id='post_active_false' />" \ "<label for='post_active_false'>false</label>" end @@ -426,10 +426,10 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input type='hidden' name='post[active]' value='' />" \ "<label for='post_active_true'>" \ - "<input name='post[active]' type='radio' value='true' />" \ + "<input name='post[active]' type='radio' value='true' id='post_active_true' />" \ "true</label>" \ "<label for='post_active_false'>" \ - "<input checked='checked' name='post[active]' type='radio' value='false' />" \ + "<input checked='checked' name='post[active]' type='radio' value='false' id='post_active_false' />" \ "false</label>" end @@ -452,10 +452,10 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input type='hidden' name='post[active]' value='' />" \ "<label for='post_active_true'>" \ - "<input name='post[active]' type='radio' value='true' />" \ + "<input name='post[active]' type='radio' value='true' id='post_active_true' />" \ "true</label>" \ "<label for='post_active_false'>" \ - "<input checked='checked' name='post[active]' type='radio' value='false' />" \ + "<input checked='checked' name='post[active]' type='radio' value='false' id='post_active_false' />" \ "false</label>" \ "<input name='post[id]' type='hidden' value='1' />" end @@ -473,9 +473,9 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input type='hidden' name='post[1][active]' value='' />" \ - "<input name='post[1][active]' type='radio' value='true' />" \ + "<input name='post[1][active]' type='radio' value='true' id='post_1_active_true' />" \ "<label for='post_1_active_true'>true</label>" \ - "<input checked='checked' name='post[1][active]' type='radio' value='false' />" \ + "<input checked='checked' name='post[1][active]' type='radio' value='false' id='post_1_active_false' />" \ "<label for='post_1_active_false'>false</label>" end @@ -492,11 +492,11 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input name='post[tag_ids][]' type='hidden' value='' />" \ - "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" \ + "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' id='post_tag_ids_1' />" \ "<label for='post_tag_ids_1'>Tag 1</label>" \ - "<input name='post[tag_ids][]' type='checkbox' value='2' />" \ + "<input name='post[tag_ids][]' type='checkbox' value='2' id='post_tag_ids_2' />" \ "<label for='post_tag_ids_2'>Tag 2</label>" \ - "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" \ + "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' id='post_tag_ids_3' />" \ "<label for='post_tag_ids_3'>Tag 3</label>" end @@ -517,13 +517,13 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input name='post[tag_ids][]' type='hidden' value='' />" \ "<label for='post_tag_ids_1'>" \ - "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" \ + "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' id='post_tag_ids_1' />" \ "Tag 1</label>" \ "<label for='post_tag_ids_2'>" \ - "<input name='post[tag_ids][]' type='checkbox' value='2' />" \ + "<input name='post[tag_ids][]' type='checkbox' value='2' id='post_tag_ids_2' />" \ "Tag 2</label>" \ "<label for='post_tag_ids_3'>" \ - "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" \ + "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' id='post_tag_ids_3' />" \ "Tag 3</label>" end @@ -547,13 +547,13 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input name='post[tag_ids][]' type='hidden' value='' />" \ "<label for='post_tag_ids_1'>" \ - "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" \ + "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' id='post_tag_ids_1' />" \ "Tag 1</label>" \ "<label for='post_tag_ids_2'>" \ - "<input name='post[tag_ids][]' type='checkbox' value='2' />" \ + "<input name='post[tag_ids][]' type='checkbox' value='2' id='post_tag_ids_2' />" \ "Tag 2</label>" \ "<label for='post_tag_ids_3'>" \ - "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" \ + "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' id='post_tag_ids_3' />" \ "Tag 3</label>" \ "<input name='post[id]' type='hidden' value='1' />" end @@ -572,7 +572,7 @@ class FormWithActsLikeFormForTest < FormWithTest expected = whole_form("/posts") do "<input name='post[1][tag_ids][]' type='hidden' value='' />" \ - "<input checked='checked' name='post[1][tag_ids][]' type='checkbox' value='1' />" \ + "<input checked='checked' name='post[1][tag_ids][]' type='checkbox' value='1' id='post_1_tag_ids_1' />" \ "<label for='post_1_tag_ids_1'>Tag 1</label>" end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index d1d61ac8d7..ab5237488a 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,10 @@ +* Default `ActiveSupport::MessageEncryptor` to use AES 256 GCM encryption. + + On for new Rails 5.2 apps. Upgrading apps can find the config as a new + framework default. + + *Assain Jaleel* + * Cache: `write_multi` Rails.cache.write_multi foo: 'bar', baz: 'qux' diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 726e1464ad..e576766c64 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -19,7 +19,17 @@ module ActiveSupport # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..." # crypt.decrypt_and_verify(encrypted_data) # => "my secret data" class MessageEncryptor - DEFAULT_CIPHER = "aes-256-cbc" + class << self + attr_accessor :use_authenticated_message_encryption #:nodoc: + + def default_cipher #:nodoc: + if use_authenticated_message_encryption + "aes-256-gcm" + else + "aes-256-cbc" + end + end + end module NullSerializer #:nodoc: def self.load(value) @@ -45,7 +55,7 @@ module ActiveSupport OpenSSLCipherError = OpenSSL::Cipher::CipherError # Initialize a new MessageEncryptor. +secret+ must be at least as long as - # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256 + # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256 # bits. If you are using a user-entered secret, you can generate a suitable # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key # derivation function. @@ -57,7 +67,7 @@ module ActiveSupport # # Options: # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by - # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'. + # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-gcm'. # * <tt>:digest</tt> - String of digest to use for signing. Default is # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'. # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+. @@ -66,7 +76,7 @@ module ActiveSupport sign_secret = signature_key_or_options.first @secret = secret @sign_secret = sign_secret - @cipher = options[:cipher] || DEFAULT_CIPHER + @cipher = options[:cipher] || self.class.default_cipher @digest = options[:digest] || "SHA1" unless aead_mode? @verifier = resolve_verifier @serializer = options[:serializer] || Marshal @@ -85,7 +95,7 @@ module ActiveSupport end # Given a cipher, returns the key length of the cipher to help generate the key of desired size - def self.key_len(cipher = DEFAULT_CIPHER) + def self.key_len(cipher = default_cipher) OpenSSL::Cipher.new(cipher).key_len end diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index af1d5bd615..45bc51311b 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -7,6 +7,13 @@ module ActiveSupport config.eager_load_namespaces << ActiveSupport + initializer "active_support.set_authenticated_message_encryption" do |app| + if app.config.active_support.respond_to?(:use_authenticated_message_encryption) + ActiveSupport::MessageEncryptor.use_authenticated_message_encryption = + app.config.active_support.use_authenticated_message_encryption + end + end + initializer "active_support.reset_all_current_attributes_instances" do |app| app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all } app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all } diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index e34af8aa8d..23f53ac084 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -755,6 +755,8 @@ NOTE: Defined in `active_support/core_ext/module/anonymous.rb`. ### Method Delegation +#### `delegate` + The macro `delegate` offers an easy way to forward methods. Let's imagine that users in some application have login information in the `User` model but name and other data in a separate `Profile` model: @@ -837,13 +839,32 @@ In the previous example the macro generates `avatar_size` rather than `size`. NOTE: Defined in `active_support/core_ext/module/delegation.rb` +#### `delegate_missing_to` + +Imagine you would like to delegate everything missing from the `User` object, +to the `Profile` one. The `delegate_missing_to` macro lets you implement this +in a breeze: + +```ruby +class User < ApplicationRecord + has_one :profile + + delegate_missing_to :profile +end +``` + +The target can be anything callable within the object, e.g. instance variables, +methods, constants, etc. Only the public methods of the target are delegated. + +NOTE: Defined in `active_support/core_ext/module/delegation.rb`. + ### Redefining Methods There are cases where you need to define a method with `define_method`, but don't know whether a method with that name already exists. If it does, a warning is issued if they are enabled. No big deal, but not clean either. The method `redefine_method` prevents such a potential warning, removing the existing method before if needed. -NOTE: Defined in `active_support/core_ext/module/remove_method.rb` +NOTE: Defined in `active_support/core_ext/module/remove_method.rb`. Extensions to `Class` --------------------- @@ -931,7 +952,7 @@ When `:instance_reader` is `false`, the instance predicate returns a `NoMethodEr If you do not want the instance predicate, pass `instance_predicate: false` and it will not be defined. -NOTE: Defined in `active_support/core_ext/class/attribute.rb` +NOTE: Defined in `active_support/core_ext/class/attribute.rb`. #### `cattr_reader`, `cattr_writer`, and `cattr_accessor` @@ -1827,7 +1848,7 @@ as well as adding or subtracting their results from a Time object. For example: (4.months + 5.years).from_now ``` -NOTE: Defined in `active_support/core_ext/numeric/time.rb` +NOTE: Defined in `active_support/core_ext/numeric/time.rb`. ### Formatting diff --git a/guides/source/security.md b/guides/source/security.md index f69a0c72b0..9b1f28a283 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1060,6 +1060,7 @@ Additional Resources The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here: -* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security) -* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too) -* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet) +* Subscribe to the Rails security [mailing list.](http://groups.google.com/group/rubyonrails-security) +* [Brakeman - Rails Security Scanner](http://brakemanscanner.org/)- To perform static security analysis for Rails applications. +* [Keep up to date on the other application layers.](http://secunia.com/) (they have a weekly newsletter, too) +* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet.](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet) diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 4ffde6198a..fb635c6ae8 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -92,6 +92,10 @@ module Rails action_dispatch.use_authenticated_cookie_encryption = true end + if respond_to?(:active_support) + active_support.use_authenticated_message_encryption = true + end + else raise "Unknown version #{target_version.to_s.inspect}" end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt index 900baa607a..3809936f9f 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt @@ -13,3 +13,7 @@ # Use AES 256 GCM authenticated encryption for encrypted cookies. # Existing cookies will be converted on read then written with the new scheme. # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true + +# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages +# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. +# Rails.application.config.active_support.use_authenticated_message_encryption = true |