HTML safe and escaped text in a rails view

In my skilling up in Rails application I was wanting to add in the feature to allow a user’s comment/post, if it contained a URL, to make that URL clickable. A fairly simple change but one with a bit of a trick on Rails as it turns out.

My first attempt was fairly straight forward. In my view I was displaying the post (called a micropost from the tutorial) like this:

<span class="content">
  <%= micropost.content >
  <%= image_tag micropost.picture.url if micropost.picture? >

Which was fine but only printed out a URL as the raw text without being a link. So I wrote a little function with a regex from the web to find URLs in a given string and to sub in the URL wrapped in an anchor tag.

def parse_content(content)
  content.gsub(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/, '<a href="\0" target="_blank">\0</a>')

Then I took the micropost partial for the view and ran the micropost content through the function to sub in the linked version.

<span class="content">
  <%= parse_content(micropost.content) >
  <%= image_tag micropost.picture.url if micropost.picture? >

Then I ran my test of the helper function, and it didn’t work. Bit of investigating and I saw that the regex I used had ^ and $ in it so would only match a string that was JUST a URL. Removed them and the unit test passed. But it broke another integration minitest test which was trying to find a content post in a page as the post now had link tagged URL in it. So I edited the test to use parse_content on the text being looked for before comparing and it still didn’t pass.

Looking at the contents of the page being searched I saw the URL links, but they’d been escaped. Rails of course escapes strings being rendered to a view by default as a safety measure. To overcome this you need to mark a string being rendered as safe for HTML rendering using a couple of mechanisms. This is easily done in Rails using the string method s.html_safe.

So it would seem that the simple solution is to set the parsed output with html_safe by changing the view code to parse_content(micropost.content).html_safe. However this allows the whole post to bypass the html escaping process and would allow malicious code to get through. No so safe.

My solution would then be to escape the full contents of the post ahead of parsing, then parse for the URLs to make them links. This would still make any malicious code URLs visible and clickable but you’d be able to see what the URLs were, which would hopefully set off some alarm bells.

I found a discussion on StackOverflow about escaping text in Rails in which there were a few methods mentioned. The main ones seemed to be h() which is an alias for html_escape() and raw(). h() seemed fine so my final view code is:

<span class="content">
  <%= parse_content(h(micropost.content)).html_safe >
  <%= image_tag micropost.picture.url if micropost.picture? >

So for now this seems like my solution. A more upgraded solution might be to strip out any HTML code from either saving or displaying it rather than escaping it, but I’ll leave that for another update.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s