Help interpreting this bit of Rails code - ruby-on-rails

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.

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())

How to output html_safe within <%=%> block while concatenating strings?

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

Can't get regex captured group working in Ruby on Rails method

I need to parse and replace text using gsub and a regular expression. A simplified example appears below where I'm saving one of the captured groups -- \3 -- for use in my replacement string.
my_map.gsub(/(\shref=)(\")(\d+), ' href="/created_path/' + '\3' + '" ' + ' title="' + AnotherObject.find('\3')'"')
In the first use of the captured value, I'm simply displaying it to build the new path. In the second example, I am calling a find with the captured value. The second example will not work.
I've tried various ways of escaping the value ("\3", ''\3'', etc) and even built a method to test displaying the value (works) or using the value in a method (doesn't work).
Any advice is appreciated.
Use the block form of gsub and replace your \3 references with $3 globals:
my_map.gsub(/(\shref=)(\")(\d+)/) { %Q{ href="/created_path/#{$3}" title="#{AnotherObject.find($3)"} }
I also switched to %Q{} for quoting to avoid the confusing quote mixing and concatenation.
The second argument in the replacement-string form of gsub (i.e. the version of gsub that you're trying to use) will be evaluated and concatenated before gsub is called and **before **the \3 value is available so it won't work.
As an aside, is there a method you should be calling on what AnotherObject.find($3) returns?

Why is this query string invalid?

In my asp.net mvc page I create a link that renders as followed:
http://localhost:3035/Formula/OverView?colorId=349405&paintCode=744&name=BRILLANT%20SILVER&formulaId=570230
According to the W3C validator, this is not correct and it errors after the first ampersand. It complains about the & not being encoded and the entity &p not recognised etc.
AFAIK the & shouldn't be encoded because it is a separator for the key value pair.
For those who care: I send these pars as querystring and not as "/" seperated values because there is no decent way of passing on optional parameters that I know of.
To put all the bits together:
an anchor (<a>) tag's href attribute needs an encoded value
& encodes to &
to encode an '&' when it is part of your parameter's value, use %26
Wouldn't encoding the ampersand into & make it part of my parameter's value?
I need it to seperate the second variable from the first
Indeed, by encoding my href value, I do get rid of the errors. What I'm wondering now however is what to do if for example my colorId would be "123&456", where the ampersand is part of the value.
Since the separator has to be encoded, what to do with encoded ampersands. Do they need to be encoded twice so to speak?
So to get the url:
www.mySite.com/search?query=123&456&page=1
What should my href value be?
Also, I think I'm about the first person in the world to care about this.. go check the www and count the pages that get their query string validated in the W3C validator..
Entities which are part of the attributes should be encoded, generally. Thus you need & instead of just &
It works even if it doesn't validate because most browsers are very, very, very lenient in what to accept.
In addition, if you are outputting XHTML you have to encode every entity everywhere, not just inside the attributes.
All HTML attributes need to use character entities. You only don't need to change & into & within script blocks.
Whatever
Anywhere in an HTML document that you want an & to display directly next to something other than whitespace, you need to use the character entity &. If it is part of an attribute, the & will work as though it was an &. If the document is XHTML, you need to use character entities everywhere, even if you don't have something immediately next to the &. You can also use other character entities as part of attributes to treat them as though they were the actual characters.
If you want to use an ampersand as part of a URL in a way other than as a separator for parameters, you should use %26.
As an example...
Hello
Would send the user to http://localhost/Hello, with name=Bob and text=you & me "forever".
This is a slightly confusing concept to some people, I've found. When you put & in a HTML page, such as in <a href="abc?def=5&ghi=10">, the URL is actually abc?def=5&ghi=10. The HTML parser converts the entity to an ampersand.
Think of exactly the same as how you need to escape quotes in a string:
// though you define your string like this:
myString = "this is \"something\" you know?"
// the string is ACTUALLY: this is "something" you know?
// when you look at the HTML, you see:
<a href="foo?bar=1&baz=2">
// but the url is ACTUALLY: foo?bar=1&bar=2

Resources