Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby on Rails: Allow less than sign '<' inside code block with sanitize helper

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.

like image 674
mansoor.khan Avatar asked Apr 02 '16 08:04

mansoor.khan


3 Answers

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 &lt; 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 %>
like image 66
Kieran Johnson Avatar answered Nov 15 '22 15:11

Kieran Johnson


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
like image 44
Alex Avatar answered Nov 15 '22 14:11

Alex


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 &lt; 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.

like image 23
mansoor.khan Avatar answered Nov 15 '22 15:11

mansoor.khan