Allow ActionText tags in Rails 7.1 with new sanitizers
Asked Answered
M

3

5

In Rails 7.0 and earlier, we customized the Trix editor to embed YouTube videos. As is customary, the video will be embedded via an iframe tag. The Rails sanitizer removes this tag as it can be abused to embed malicious websites.

Allowing the tag in config/initializers/action_text.rb used to do the trick.

Rails.application.config.after_initialize do
  ActionText::ContentHelper.allowed_tags << "iframe"
end

Rails 7.1 has a new HTML5 sanitizer, but the old HTML4 one can still be used. This means ActionText::ContentHelper is not yet defined when the application boots, so the snippet above will crash the server. I expected the after_initialize to wait long enough for the sanitizer to be loaded, but no dice.

Unable to load application: NoMethodError: undefined method `<<' for nil:NilClass
config/initializers/action_text.rb:2:in `block in <top (required)>': undefined method `<<' for nil:NilClass (NoMethodError)

Any ideas on how to allow sanitized tags in Rails 7.1?

Marvamarve answered 26/10, 2023 at 10:7 Comment(0)
B
2

I was stuck with the same problem. According to ActionView 7.1.0 CHANGELOG it's nil until is set in the application.

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.

As a result of this change, the defaults for ActionText::ContentHelper.allowed_tags and .allowed_attributes are applied at runtime, so the value of these attributes is now 'nil' unless set by the application. You may call sanitizer_allowed_tags or sanitizer_allowed_attributes to inspect the tags and attributes being allowed by the sanitizer.

So, I decided to copy all tags from 'rails-html-sanitizer' gem to config/initializers/action_text.rb and also added my specific tags there

Rails.application.config.after_initialize do
  ActionText::ContentHelper.allowed_tags = Set.new([
    "a",
    "abbr",
    "acronym",
    "address",
    ...  # more tags go here
  ]).freeze
end

It did the job.

Benzene answered 26/10, 2023 at 18:22 Comment(4)
That's a... smart solution! I used ActionText::ContentHelper.allowed_tags = Loofah::HTML5::SafeList::ACCEPTABLE_ELEMENTS.add('iframe') instead of a hard coded list to be slightly more future proof. Thank you for helping me out!Marvamarve
Please do not copy allowlists. Use the sanitizer's allow list, that's what it's there for.Bradway
Why not? By manually listing the tags we can keep only those which can be used by Trix. I'm not sure if there any possibility to use tags like <kbd>, <dfn> ... in Trix editor.Benzene
@Marvamarve beware that the constant you're using does not include the "action-text-attachment" tag. I'm using this silly line of code instead: Class.new.include(ActionText::ContentHelper).new.sanitizer_allowed_tags. You can find the sanitizer_allowed_tags method here. It boils down to this: sanitizer.class.allowed_tags + [ ActionText::Attachment.tag_name, "figure", "figcaption" ]. That's what Rails actually uses as default for our allowed_tags setting.Mystic
C
2

We ran into this recently as well. I still feel like I'm missing something in the documentation ...

We took a hybrid of the other solutions and went with the following knowing that we're using the rails 7.1 defaults in our config:

# config/initializers/action_text.rb
Rails.application.config.after_initialize do
  default_allowed_attributes = Rails::HTML5::Sanitizer.safe_list_sanitizer.allowed_attributes + ActionText::Attachment::ATTRIBUTES.to_set
  custom_allowed_attributes = Set.new(%w[controls data-controller role style])
  ActionText::ContentHelper.allowed_attributes = (default_allowed_attributes + custom_allowed_attributes).freeze

  default_allowed_tags = Rails::HTML5::Sanitizer.safe_list_sanitizer.allowed_tags + Set.new([ActionText::Attachment.tag_name, "figure", "figcaption"])
  custom_allowed_tags = Set.new(%w[audio video source])
  ActionText::ContentHelper.allowed_tags = (default_allowed_tags + custom_allowed_tags).freeze
end

Basically we took the defaults we found via the HTML5 sanitizer and added them to our custom tags.

I'm sure there is a way to access the configured sanitizer, however we felt this made it very explicit what we were doing. I think this PR comment was along the right track for the dynamic approach: https://github.com/rails/rails/commit/e8137c527dd6bd43f695b472f440cab7466243a6#r128790711

Now everything works like a charm! <3 Rails 7.1

Configuration answered 20/12, 2023 at 14:41 Comment(0)
M
2

I was struggling with the same thing. I dug into the source and found out that our allowed_tags setting is wrapped by sanitizer_allowed_tags.

You can find that method here on Github. It boils down to this:

ActionText::ContentHelper.sanitizer.class.allowed_tags + [ ActionText::Attachment.tag_name, "figure", "figcaption" ]

That's what Rails actually uses as default for our allowed_tags setting.

So if you want to add "iframe" to that, you can do this in a initializer:

# initializers/whatever.rb
default_allowed_tags = Class.new.include(ActionText::ContentHelper).new.sanitizer_allowed_tags
ActionText::ContentHelper.allowed_tags = default_allowed_tags.add('iframe')

Hope that helps!

Mystic answered 26/2 at 19:10 Comment(1)
It's kinda wild that this is necessary, but your method appears to be the best way to do itGalbraith

© 2022 - 2024 — McMap. All rights reserved.