How to output html_safe within <%=%> block while concatenating strings? - ruby-on-rails

Consider this:
<%
str = "http://domain.com/?foo=1&bar=2"
%>
Now these cases:
<%=str%>
# output:http://domain.com/?foo=1&bar=2
<%=str.html_safe%>
# output:http://domain.com/?foo=1&bar=2
<%="#{str.html_safe}"%>
# output:http://domain.com/?foo=1&bar=2
<%=""+str.html_safe%>
# output:http://domain.com/?foo=1&bar=2
I need to output the URL with other strings. How can I guarantee that the ampersand will be unescaped? For reasons beyond my control I can't send &.
Please help! Pulling my hair here :\
EDIT: To clarify, I actually have an array like so:
#images = [{:id=>"fooid",:url=>"http://domain.com/?foo=1&bar=2"},...]
I am creating a JS array (the image_array var) to use in my app this way:
image_array.push(<%=#images.map{|x|"{id:'#{x[:id]}',url:'#{x[:url].html_safe}'}"}.join(",")%>);
This generates:
image_array.push({id:'fooid',url:'http://domain.com/?foo=1&bar=2'},...);
Which does not work in my specific case. I need the url without the amp; part.

When you write:
"#{foo.bar}"
this is ~equivalent to writing
foo.bar.to_s
So what you are actually doing is:
<%=str.html_safe.to_s%>
…which Rails no longer sees as being safe, and so it hits your resulting string with a round of HTML escaping.
I don't know the internals of Rails, but I assume that the html_safe method extends the string object with an instance variable flagging it as OK, but when you wrap that in another string via interpolation you are getting a new string without that flag.
Edit: To answer your needs, use raw or call html_safe on your final string:
<%=raw "foo#{str}"%>
<%="foo#{str}".html_safe%>
or in your case:
image_array.push(<%=raw #images.map{…}.join(',')%>);
image_array.push(<%=#images.map{…}.join(',').html_safe%>);
See also this question.

Use this
<%=str.html_safe.to_s%>
or
<%=raw(str)%>
give you better results

image_array.push(<%= #images.map{|x| "{id:'#{x[:id]}',url:'#{x[:url]}'}".html_safe }.join(",") %>);

what you would do to be safe is:
image_array.push(<%= #images.map { |image| image.as_json(only: [:id, :url]) }.to_json } %>)
this will escape the <, >, etc. properly like this:
[{"name":"\u003ch1\u003eAAAA\u003c/h1\u003e"}]
and for people coming here like me who want to concatenate strings, it's just not safe to do it, the best way is to concatenate tags, e.g.
content_tag(:p) do
content_tag(:span, "<script>alert(1)</script>") +
link_to("show", user)
end
will work fine and properly escape the first string

Related

Why does ActionView's "select_tag" escape the HTML in its "options" argument when called directly?

So I've been going through the very poor documentation that exists for ActionView, particularly a method called select_tag that just exists when called from view files. Supposedly, it's called like this:
select_tag name, option_tags, options
The documentation says nothing at all about the name, very little about the options, and describes the option_tags only through examples that treat it as an opaque value that must be obtained from other functions.
As always, the only way to learn anything about Rails is to reverse-engineer it.
So I tried running it directly from a Rails console, which is tricky because Ruby doesn't let you call methods that are defined in modules unless you create a class and an object first:
class H
include ActionView::Helpers::FormOptionsHelper
include ActionView::Helpers::FormTagHelper
end
H.new.options_for_select ["foo","bar"]
The above usage of options_for_select comes from actual code that somebody else wrote. The return value is a string:
"<option value=\"foo\">foo</option>\n<option value=\"bar\">bar</option>"
So apparently, you're supposed to pass the return value from option_for_select (or one of the many other related functions that introduce complications I don't want to talk about such as generating HTML tags from ActiveRecord objects) as the option_tag parameter of select_tag. Except if you copy that string to your clipboard and paste it directly into a function call, it doesn't do what you'd expect:
H.new.select_tag :my_name, "<option value=\"foo\">foo</option>\n<option value=\"bar\">bar</option>"
Return value:
"<select name=\"my_name\" id=\"my_name\"><option value="foo">foo</option>\n<option value="bar">bar</option></select>"
At least this reveals what the name parameter is for.
Even weirder, the text is not escaped if you pass the return value directly to select_tag without letting it print on the console:
H.new.select_tag :name, H.new.options_for_select(["foo","bar"])
Return value:
"<select name=\"name\" id=\"name\"><option value=\"foo\">foo</option>\n<option value=\"bar\">bar</option></select>"
WTF is going on here?
In the course of writing this question, I stumbled on its answer: Ruby has been lying to me (like it always does).
When you evaluate:
H.new.options_for_select ["foo","bar"]
Ruby tells you that the result was a String. But that's only because Pry and Irb both silently call .to_s on everything, and the thing that gets returned from options_for_select has a to_s. The truth:
(H.new.options_for_select ["foo","bar"]).class
=> ActiveSupport::SafeBuffer
ActiveSupport::SafeBuffer.new("<foo>")
=> "<foo>"
So whoever wrote these methods assumed that you want to incorporate raw, user-provided strings into your <select> tags, and those strings could contain attempts at HTML/JavaScript injection, so they must be escaped.
ActiveView treats all strings as suspect, but it is possible to mark certain strings as "safe" by wrapping them in an ActiveSupport::SafeBuffer.

Line break in string returned by a helper method

I have a helper method that returns a string after formatting it, which includes adding line breaks.
I am calling this helper method in a view and am trying to display this formatted string.
I am using "\n" or "\r\n" to introduce line breaks, but this shows up as mere spaces in the browser.
You should use <br> or create it like tag('br'):
your_string = "test string" + tag('br')
your_string.html_safe #return your string
As #max rightly pointed, from a security vulnerability (XSS) aspect you can use h() on user-provided text, which converts your string to a safe string and allows you to securely call html_safe on the full string.
One mistake i made was using raw(string) in the helper method instead of using it in view and it still was shown as 'smth smth' so be aware of that.
My code looked something like this
td= helper_method()
And when I changed it to the following it worked:
td= raw(helper_method())

Exclude part of a string when testing in rails

How would I take a test statement that tests for two equal strings, such as, if #current_lesson.code == #current_course.answer, and exclude some parts of the answer. So, if I want to make the user write a <p> tag with anything in it, how would I make it correct, as long as they have the <p> tag?
I would use Regexp to check if you have a <p> tag with text inside the string. If you use Rspec, i'd use the matches matcher.
The code would be something like this:
expect(#current_lesson.code).to match(/\<p\>.+\<\/p\>/)
Didnt check the regexp, it is just to prove a point :).
You can use the "include" method for string object:
Example :
if #current_course.answer.include? #current_lesson.code
#Your code logic here
end

Clean up & style characters from text

I am getting text from a feed that has alot of characters like:
Insignia&#153; 2.0 Stereo Computer Speaker System (2-Piece) - Black
4th-Generation Apple® iPod® touch
Is there an easy way to get rid of these, or do I have to anticipate which characters I want to delete and use the delete method to remove them? Also, when I try to remove
&
with
str.delete("&")
It leaves behind "amp;" Is there a better way to delete this type of character? Do I need to re-encode the text?
String#delete is certainly not what you want, as it works on characters, not the string as a whole.
Try
str.gsub /&/, ""
You may also want to try replacing the & with a literal ampersand, such as:
str.gsub /&/, "&"
If this is closer to what you really want, you may get the best results unescaping the HTML string. If so try this:
CGI::unescapeHTML(str)
Details of the unescapeHTML method are here.
If you are getting data from a 'feed', aka RSS XML, then you should be using an XML parser like Nokogiri to process the XML. This will automatically unescape HTML entities and allow you to get the proper string representation directly.
For removing try to use gsub method, something like this:
text = "foo&bar"
text.gsub /\b&\b/, "" #=> foobar

Help interpreting this bit of Rails code

What is this?
"#{h params[:chat_input]}"
I am referring to the hash # and the h.
Most likely this is inside a double-quoted string, such as "Ponies all love #{h params[:chat_input]}!" The #{stuff} expression causes the stuff expression to be interpreted and inserted into the string. For example "1 + 2 = #{1 + 2}" will result in the string "1 + 2 = 3".
The h is an alias to the html_escape method, which is pretty self-explanatory.
The code you paste, by itself, is just a comment. I assume the code is inside a string, though.
"hello, #{5 + 5}"
# => hello, 10
The statement inside the brackets will be evaluated as Ruby. This is called string interpolation.
The statement inside the interpolation in your code is a method that gets an argument.
h params[:chat_input]
h(params[:chat_input])
The h method is a shortcut for html_escape, which escapes HTML. For example, <span> is converted into <span>, so that the browser displays the actual contents of the string, instead of interpreting it as HTML.
html_escape(params[:chat_input])
You probably know what params is.
To sum up, you get a HTML escaped version of whatever params[:chat_input] contains.
"#{h params[:chat_input]}"
In ruby, double-quoted strings allow for expressions to be evaluated and automatically converted to strings.
I can do this:
years = 25
"John is " + years + " years old"
but I'll get an error because I can't add the number to a string.
I can do
"John is #{years} years old"
to get around that.
The h() method is a Rails helper function that removes HTML tags. It's a safety thing.
Finally, params() is a method in Rails that gives you access to GET and POST parameters. It's actually wrapping a hash GET and POST parameters are symbolized to reduce memory (symbols are only defined once, whereas a string like "foo" is a new object every time.)
So, params[:chat_input] retrieves the value from the previous request's GET or POST parameters, and in your case it looks like it's just displaying and sanitizing them.
Hope that helps!
It's just interpolating a value inside a string. The :chat_input is a symbol, it's used in place of a string because symbols are only created once.
h(something)
or
h something
since ruby does not force the use of (), is a function available in rails that converts the parameter to a "safe HTML" string avoiding interpreting the possible HTML code inside of the 'something' variable.
"#{x}"
in ruby means converting the x variable to a string and placing it in the new string for example:
"#{host}:#{port}"
will place the value of host and the value of port into the new string formed by the "", in a way that if host is "localhost" and port is 30 the result string will be "localhost:30"
params is a special rails hash that contains the post/get parameters passed to the controller method being executed
another detail is that in ruby a method always returns the last evaluated expression
so the method
def test
"#{h params[:chat_input]}"
end
will return a string that has the HTML-safe value of the post/get parameter chat_input
holy crap, is that from chat_sandbox by any chance?
if so, let me know if you need any help $)
I'm hoping to update that code here soon.

Resources