Forcing HTML Escaping in Rails 3
Asked Answered
C

3

12

I'm running into an issue with the rails auto-escaping. It currently thinks a string is html_safe (which it is), but for display purposes I need it to still escape the html. Here's the steps the string is taking.

my_string = render(:partial => "set_string", :locals => {:item => @item})
<%= my_string %>

and the partial is basically

<h2>Page Header</h2>
<strong><%= item.name %></strong>
<%= item.body %>
etc

My understanding is that because I'm displaying text in a view directly (the h2, etc) it assumes it is safe, and it also properly escapes the item outputs, which makes the whole my_string safe. So, when I try to display it with the

<%= my_string %>

It doesn't escape the remaining html. I tried adding h to force the escaping but that didn't work.

So my question is, is there anyway to force html escaping of a safe string other than calling something on the string that will make it unsafe?

Thanks a lot for your help.

Cadre answered 5/6, 2012 at 17:47 Comment(0)
C
11

Thanks to Sebastien for the suggestion, I wanted to get the real answer here and not buried in the comments:

I looks like this works:

<%= raw CGI::escapeHTML(my_string) %>

You need the "raw" call otherwise the escapeHTML makes the string unsafe in addition to escaping it so the auto escape double escapes it.

Cadre answered 10/6, 2012 at 12:55 Comment(1)
note, CGI::escapeHTML does not escape all entities, e.g. &acute; and &aacute;Constellate
F
23

Escape from ActiveSupport::SafeBuffer in Rails 3+

In this instance <%= my_string.to_str %> will double-escape as required.

SafeBuffer workings

When a string is escaped by Rails you get an ActiveSupport::SafeBuffer. From that point, extra escaping is skipped because the SafeBuffer is html_safe?. It's a clever solution! There are times though, that we wish to escape such cleverness.

Why double-escape?

I needed to re-escape content generated by tag helpers to pass generated markup to data- attributes. This has also come in handy for displaying template-generated code.

Force-escape for a String that's html_safe?

Call to_str on the SafeBuffer, which returns a String.

# Example html safe content
content = content_tag :code, 'codez<>'
content.html_safe? # true

# call .to_str
escaped = content.to_str
escaped.html_safe? # false

# The escaped String will now be re-escaped when used in a template

The to_s gotcha

The to_s method looks very much like the to_str method. Don't use to_s here, ActionView::SafeBuffer#to_s just returns self, where to_str is called above the SafeBuffer context, returning a naturally unsafe String.

Fissure answered 21/12, 2012 at 0:54 Comment(5)
The solution is then to use content.to_str to re-escape the contentImplantation
Thanks @SébastienGrosjean-ZenCocoon I've updated the answer to be more specific to this question :)Fissure
great way to mark it as unsafe again, I used it to render a partial inside an html attribute: <div data-content="<%= render(partial: 'my_partial').to_str %>"></div>Impel
This doesn't work if the string is an ActionView::OutputBuffer -- to_s keeps it as an OutputBuffer. @Jason-Logsdon's answer works in this case though.Triley
I totally agree to_s does not work :D however to_str still works as intended. I had to squint before I realised this. Updated the code comments to help prevent the gotcha occurring for others.Fissure
C
11

Thanks to Sebastien for the suggestion, I wanted to get the real answer here and not buried in the comments:

I looks like this works:

<%= raw CGI::escapeHTML(my_string) %>

You need the "raw" call otherwise the escapeHTML makes the string unsafe in addition to escaping it so the auto escape double escapes it.

Cadre answered 10/6, 2012 at 12:55 Comment(1)
note, CGI::escapeHTML does not escape all entities, e.g. &acute; and &aacute;Constellate
T
5

To interpret the html (it's what i understood you need), you have to use :

<%= raw my_string %>
Tatting answered 6/6, 2012 at 9:30 Comment(4)
I'm looking to have the HTML to be escaped, not interpreted. Thanks.Cadre
Did you tried this : CGI::escapeHTML('Usage: foo "bar" <baz>')Tatting
Looks like this works: <%= raw CGI::escapeHTML(my_string) %>, otherwise the escapeHTML makes it unsafe in addition to escaping it so the auto escape double escapes it.Cadre
Thanks for helping out, I was just doing my_string.gsub("","") to make it unsafe but I figured there had to be a better way. Just update your answer and I'll accept it.Cadre

© 2022 - 2024 — McMap. All rights reserved.