mansoor.khan
mansoor.khan

Reputation: 2616

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.

Upvotes: 9

Views: 1981

Answers (6)

Kirill
Kirill

Reputation: 3737

Consider replacing "<" with its ASCII character &#60; before running it through the sanitize method. It should get converted into &lt; and then render as "<" character, instead of the html.

Upvotes: 0

shashwat srivastava
shashwat srivastava

Reputation: 151

nokogiri gem solves the problem:

gem 'nokogiri'

Nokogiri::HTML::DocumentFragment.parse('<b>hi</b> x > 5').text
 => "hi x > 5" 

Upvotes: 0

mansoor.khan
mansoor.khan

Reputation: 2616

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.

Upvotes: 3

Alex
Alex

Reputation: 2518

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

Upvotes: 2

Kieran Johnson
Kieran Johnson

Reputation: 1353

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 %>

Upvotes: 4

Satishakumar Awati
Satishakumar Awati

Reputation: 3798

This might help, sanitizer has options to provide white list of tags and attributes needs to ignored during sanitization

ActionView::Base.full_sanitizer.sanitize(html_string) #Basic Syntax

White list of tags and attributes can be specified as bellow

ActionView::Base.full_sanitizer.sanitize(html_string, :tags => %w(img br p), :attributes => %w(src style))

Above statement allows tags: img, br and p and attributes : src and style.

Upvotes: 1

Related Questions