From 68fb1e40d15145db23b0252bd2fe7b8e39a32385 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Wed, 28 Jun 2023 15:42:06 -0400 Subject: [PATCH 1/2] Backfill tests for config.action_view.sanitizer_vendor Related to #48293 --- .../test/application/configuration_test.rb | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 7acfa042493c9..cac2dcd1e58e7 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -23,6 +23,8 @@ def self.delivered_email(email); email; end class ::MyOtherMailObserver < ::MyMailObserver; end +class ::MySanitizerVendor < ::Rails::HTML::Sanitizer; end + class MyLogRecorder < Logger def initialize @io = StringIO.new @@ -4481,6 +4483,27 @@ def new(app); self; end assert_equal OpenSSL::Digest::SHA1, ActiveRecord::Encryption.config.hash_digest_class end + test "sanitizer_vendor is set to best supported vendor in new apps" do + app "development" + + assert_equal Rails::HTML::Sanitizer.best_supported_vendor, ActionView::Helpers::SanitizeHelper.sanitizer_vendor + end + + test "sanitizer_vendor is set to HTML4 in upgraded apps" do + remove_from_config '.*config\.load_defaults.*\n' + add_to_config 'config.load_defaults "7.0"' + app "development" + + assert_equal Rails::HTML4::Sanitizer, ActionView::Helpers::SanitizeHelper.sanitizer_vendor + end + + test "sanitizer_vendor is set to a specific vendor" do + add_to_config "config.action_view.sanitizer_vendor = ::MySanitizerVendor" + app "development" + + assert_equal ::MySanitizerVendor, ActionView::Helpers::SanitizeHelper.sanitizer_vendor + end + private def set_custom_config(contents, config_source = "custom".inspect) app_file "config/custom.yml", contents From 44d3b44d9d7f42658bda1037f6c7713d02d4b1c0 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 3 Jul 2023 17:27:43 -0400 Subject: [PATCH 2/2] Add config.action_text.sanitizer_vendor Rails 7.1 defaults to Rails::HTML::Sanitizer.best_supported_vendor, earlier configs will use Rails::HTML4::Sanitizer. Related to #48523 / 55bca6b8. --- actiontext/CHANGELOG.md | 13 +++++++ .../app/helpers/action_text/content_helper.rb | 2 +- actiontext/lib/action_text/engine.rb | 6 +++ guides/source/configuring.md | 12 ++++++ .../lib/rails/application/configuration.rb | 4 ++ .../new_framework_defaults_7_1.rb.tt | 16 ++++++-- .../test/application/configuration_test.rb | 38 ++++++++++++++++++- 7 files changed, 86 insertions(+), 5 deletions(-) diff --git a/actiontext/CHANGELOG.md b/actiontext/CHANGELOG.md index 08ea666644564..61158b4de46bd 100644 --- a/actiontext/CHANGELOG.md +++ b/actiontext/CHANGELOG.md @@ -1,3 +1,16 @@ +* Use `Rails::HTML5::SafeListSanitizer` by default in the Rails 7.1 configuration if it is + supported. + + Action Text's sanitizer can be configured by setting + `config.action_text.sanitizer_vendor`. Supported values are `Rails::HTML4::Sanitizer` or + `Rails::HTML5::Sanitizer`. + + The Rails 7.1 configuration will set this to `Rails::HTML5::Sanitizer` when it is supported, and + fall back to `Rails::HTML4::Sanitizer`. Previous configurations default to + `Rails::HTML4::Sanitizer`. + + *Mike Dalessio* + * Attachables now can override default attachment missing template. When rendering Action Text attachments where the underlying attachable model has diff --git a/actiontext/app/helpers/action_text/content_helper.rb b/actiontext/app/helpers/action_text/content_helper.rb index 0980bf0f00e31..28ca1f651f055 100644 --- a/actiontext/app/helpers/action_text/content_helper.rb +++ b/actiontext/app/helpers/action_text/content_helper.rb @@ -4,7 +4,7 @@ module ActionText module ContentHelper - mattr_accessor(:sanitizer) { Rails::Html::Sanitizer.best_supported_vendor.safe_list_sanitizer.new } + mattr_accessor(:sanitizer, default: Rails::HTML4::Sanitizer.safe_list_sanitizer.new) mattr_accessor(:allowed_tags) { sanitizer.class.allowed_tags + [ ActionText::Attachment.tag_name, "figure", "figcaption" ] } mattr_accessor(:allowed_attributes) { sanitizer.class.allowed_attributes + ActionText::Attachment::ATTRIBUTES } mattr_accessor(:scrubber) diff --git a/actiontext/lib/action_text/engine.rb b/actiontext/lib/action_text/engine.rb index de71c547f38c4..71de7b8c2692e 100644 --- a/actiontext/lib/action_text/engine.rb +++ b/actiontext/lib/action_text/engine.rb @@ -82,5 +82,11 @@ def to_trix_content_attachment_partial_path initializer "action_text.configure" do |app| ActionText::Attachment.tag_name = app.config.action_text.attachment_tag_name end + + initializer "action_text.sanitizer_vendor" do |app| + if klass = app.config.action_text.delete(:sanitizer_vendor) + ActionText::ContentHelper.sanitizer = klass.safe_list_sanitizer.new + end + end end end diff --git a/guides/source/configuring.md b/guides/source/configuring.md index d6954791c8d63..052ee58194ad6 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -63,6 +63,7 @@ Below are the default values associated with each target version. In cases of co - [`config.action_controller.allow_deprecated_parameters_hash_equality`](#config-action-controller-allow-deprecated-parameters-hash-equality): `false` - [`config.action_dispatch.debug_exception_log_level`](#config-action-dispatch-debug-exception-log-level): `:error` - [`config.action_dispatch.default_headers`](#config-action-dispatch-default-headers): `{ "X-Frame-Options" => "SAMEORIGIN", "X-XSS-Protection" => "0", "X-Content-Type-Options" => "nosniff", "X-Permitted-Cross-Domain-Policies" => "none", "Referrer-Policy" => "strict-origin-when-cross-origin" }` +- [`config.action_text.sanitizer_vendor`](#config-action-text-sanitizer-vendor): `Rails::HTML::Sanitizer.best_supported_vendor` - [`config.action_view.sanitizer_vendor`](#config-action-view-sanitizer-vendor): `Rails::HTML::Sanitizer.best_supported_vendor` - [`config.active_job.use_big_decimal_serializer`](#config-active-job-use-big-decimal-serializer): `true` - [`config.active_record.allow_deprecated_singular_associations_name`](#config-active-record-allow-deprecated-singular-associations-name): `false` @@ -2821,6 +2822,17 @@ has no effect if Sprockets is not used. The default value is `true`. Accepts a string for the HTML tag used to wrap attachments. Defaults to `"action-text-attachment"`. +#### `config.action_text.sanitizer_vendor` + +Configures the HTML sanitizer used by Action Text by setting `ActionText::ContentHelper.sanitizer` to an instance of the class returned from the vendor's `.safe_list_sanitizer` method. The default value depends on the `config.load_defaults` target version: + +| Starting with version | The default value is | Which parses markup as | +|-----------------------|--------------------------------------|------------------------| +| (original) | `Rails::HTML4::Sanitizer` | HTML4 | +| 7.1 | `Rails::HTML5::Sanitizer` (see NOTE) | HTML5 | + +NOTE: `Rails::HTML5::Sanitizer` is not supported on JRuby, so on JRuby platforms Rails will fall back to use `Rails::HTML4::Sanitizer`. + ### Configuring a Database Just about every Rails application will interact with a database. You can connect to the database by setting an environment variable `ENV['DATABASE_URL']` or by using a configuration file called `config/database.yml`. diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 8b2580ec10f66..16a0cce63fc41 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -319,6 +319,10 @@ def load_defaults(target_version) if respond_to?(:action_view) action_view.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor end + + if respond_to?(:action_text) + action_text.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor + end end else raise "Unknown version #{target_version.to_s.inspect}" diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt index e63851ecc732b..efd19cc5fed51 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt @@ -184,13 +184,23 @@ # Configure Action View to use HTML5 standards-compliant sanitizers when they are supported on your # platform. # -# `Rails::HTML::Sanitizer.best_supported_vendor` will return `Rails::HTML5::Sanitizer` if it's -# supported, else fall back to `Rails::HTML4::Sanitizer`. +# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action View to use HTML5-compliant +# sanitizers if they are supported, else fall back to HTML4 sanitizers. # -# In previous versions of Rails, Action View always used `Rails::HTML4::Sanitizer`. +# In previous versions of Rails, Action View always used `Rails::HTML4::Sanitizer` as its vendor. # # Rails.application.config.action_view.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor +# Configure Action Text to use an HTML5 standards-compliant sanitizer when it is supported on your +# platform. +# +# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action Text to use HTML5-compliant +# sanitizers if they are supported, else fall back to HTML4 sanitizers. +# +# In previous versions of Rails, Action Text always used `Rails::HTML4::Sanitizer` as its vendor. +# +# Rails.application.config.action_text.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor + # Configure the log level used by the DebugExceptions middleware when logging # uncaught exceptions during requests # Rails.application.config.action_dispatch.debug_exception_log_level = :error diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index cac2dcd1e58e7..698b87d144e32 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -23,7 +23,13 @@ def self.delivered_email(email); email; end class ::MyOtherMailObserver < ::MyMailObserver; end -class ::MySanitizerVendor < ::Rails::HTML::Sanitizer; end +class ::MySafeListSanitizer < Rails::HTML4::SafeListSanitizer; end + +class ::MySanitizerVendor < ::Rails::HTML::Sanitizer + def self.safe_list_sanitizer + ::MySafeListSanitizer + end +end class MyLogRecorder < Logger def initialize @@ -4504,6 +4510,36 @@ def new(app); self; end assert_equal ::MySanitizerVendor, ActionView::Helpers::SanitizeHelper.sanitizer_vendor end + test "Action Text uses the best supported safe list sanitizer in new apps" do + app "development" + + assert_kind_of( + Rails::HTML::Sanitizer.best_supported_vendor.safe_list_sanitizer, + ActionText::ContentHelper.sanitizer, + ) + end + + test "Action Text uses the HTML4 safe list sanitizer in upgraded apps" do + remove_from_config '.*config\.load_defaults.*\n' + add_to_config 'config.load_defaults "7.0"' + app "development" + + assert_kind_of( + Rails::HTML4::Sanitizer.safe_list_sanitizer, + ActionText::ContentHelper.sanitizer, + ) + end + + test "Action Text uses the specified vendor's safe list sanitizer" do + add_to_config "config.action_text.sanitizer_vendor = ::MySanitizerVendor" + app "development" + + assert_kind_of( + ::MySafeListSanitizer, + ActionText::ContentHelper.sanitizer, + ) + end + private def set_custom_config(contents, config_source = "custom".inspect) app_file "config/custom.yml", contents