-1

In my config/initializers I added the following to the String class:

class String
  def sanitize(options={ tags: %w(div p span strong b em i br ol ul li) })
    ActionController::Base.helpers.sanitize(self, options)
  end
end

On my local development site, this converts all disallowed tags to encoded html, so

"<span><img src=\"nonexistent.png\" onerror=\"alert('This alert should not be shown');\"></span><p>Build something</p>"

becomes

"<span>&lt;img src=\"nonexistent.png\" onerror=\"alert('This alert should not be shown');\"/&gt;</span><p>Build something</p> "

But in rspec, calling the same method on the same string results in:

"<span></span><p>Build something</p>"

It is not encoding the image tag anymore; it is just stripping the tag out altogether. What is the cause of this different behavior in a model spec than in a model?

Ando
  • 165
  • 1
  • 14
  • Show the code you're talking about that's calling this `String#sanitize` method, in both your own code and in rspec (or indicate if you meant that your same app code when called from rspec results in the whole element being removed). – smathy Jun 01 '23 at 16:37
  • 1
    It's the same code obviously: `str.sanitize`. But the reason for it was ultimately because a front end rich text editor was implementing html encoding before sending it to the api. – Ando Jun 03 '23 at 19:17

2 Answers2

2

Hard to know. Easy to find out. Mostly: don't do that.

Hard to know: Something else is clobbering your sanitize method or you're clobbering someone else's?

Easy to find out: Write a test. Set a breakpoint before calling sanitize. Step into it and keep stepping. You'll probably see what's going on pretty quickly.

Don't do that:

  1. Other things either add that method or you can call sanitize from something else. I'm not sure what the right solution is for you, but probably a controller or helper method sanitize(string, options) is better than doing what you've done. After all, that's what you're calling.
  2. If you have to add a method, don't open the class and clobber like you've done:
module StringSanitizer
  def sanitize(...)
    super # if appropriate - see if it already exists in the class or if something else has already added it
    ActionController::Base.helpers.sanitize(self, options)
  end
end
String.prepend(StringSanitizer)
kwerle
  • 2,225
  • 22
  • 25
  • There is no sanitize method on String: `String.instance_methods.include? :sanitize # => false` – smathy Jun 01 '23 at 16:36
  • 1
    The rest still stands: Don't do that. Debug it if you really have to do that. – kwerle Jun 01 '23 at 17:46
  • Thanks @kwerle. I commented the cause above, but I appreciate your answer as it ultimately led me to a better implementation: a module with a `before_save` that receives attr args from the model. – Ando Jun 03 '23 at 19:25
  • @Ando That's great! But as the saying goes: "Pics or it didn't happen." Answer your own question here with the right solution. – kwerle Jun 05 '23 at 17:25
0

Here's the solution I came with following @kwerle 's recommendations:

module Sanitizable
  extend ActiveSupport::Concern

  class_methods do
    def sanitizable(*attributes)
      before_save do |record|
        attributes.each do |attribute|
          record[attribute] = ActionController::Base.helpers.sanitize(record[attribute], tags: %w(p span strong b em i br ol ul li))
        end
      end
    end
  end
end

and this can be used in a model with:

include Sanitizable
sanitizable :subtitle, :description
Ando
  • 165
  • 1
  • 14