I'm trying to escape user generated content in Rails. I have used raw with sanitize and raw helpers to filter content like this:
raw(sanitize(code, :tags => ['<', 'h2','h3','p','br','ul','ol','li','code','pre','a'] ))
The list of tags mentioned are allowed in the content.
The problem is when I try to test it with a sql query like this:
mysql -u sat -p -h localhost database < data.sql
inside pre and code blocks it removes everything after the less than (<) sign.
Please help me figure out a way to do this.
I don't believe this is possible using the default sanitize method within Rails.
Instead try using the Sanitize gem (https://github.com/rgrove/sanitize)
require 'sanitize'
allowed_elements = ['h2','h3','p','br','ul','ol','li','code','pre','a']
code = "<pre>mysql -u sat -p -h localhost database < data.sql</pre>"
Sanitize.fragment(code, elements: allowed_elements)
# => <pre>mysql -u sat -p -h localhost database < data.sql</pre>
To use this to save sanitized content to the database add a before_save
filter to you model that runs sanitize on the user generated content and stores the result, e.g.
class MyModel < ActiveRecord::Base
ALLOWED_ELEMENTS = ['h2','h3','p','br','ul','ol','li','code','pre','a']
before_save :sanitize_code
private
def sanitize_code
self.code = Sanitize.fragment(code, elements: ALLOWED_ELEMENTS)
end
end
When you output the content you just need to use the raw view helper e.g.
<%= raw @instance.code %>
Rails 3 added the html_safe
property for every String instance. Every string that is printed or inserted to the database will be escaped unless html_safe
is set to true (simplified). What raw
does, is actually set html_safe
to true
. So you should only pass a string that is already safe/escaped.
A possible solution could look something like this:
strip_tags(code).html_safe
You might have to add additional checks / string replacements depending on your use case.
According to your comment, you probably need a little more complex version. You could try to replace all chars that you would like to allow, sanitize the string, and then reverse the replacement in order to avoid that the sanitize method sanitizes more than you actually want. Try something like this:
code = "mysql -u sat -p -h localhost database < data.sql"
ALLOWED_SIGNS = {
:lower_than => "<".html_safe
}
s = code.dup
ALLOWED_SIGNS.each { |k, v| s.sub!(v, "%{#{k}}") }
sanitize(s) % ALLOWED_SIGNS
It seems like the whole issue was with the way data being stored in the database. Previously, a less than sign '<' was being saved as it is but now it is being escaped so a '<' would be saved as <
which seems to have solved the problem.
I was able to understand that accidentally while using tinymce-rails WYSIWYG editor which was escaping the '<' automatically.
@kieran-johnson's answer might have done the same but tinymce-rails solved it without installing an extra gem.
Thank you all of you who took out time to help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With