I ran into this exact same thing and discovered a safer solution than using html_safe
.
First, the method:
def full_title(page_title)
base_title = "".html_safe
base_title << "Ruby on Rails Tutorial Sample App"
if page_title.present?
base_title << " | "
base_title << page_title
end
base_title
end
This escapes page_title
if it is unsafe, but leaves it unescaped if it is safe.
This allows "Foo's"
to display properly, but also escapes "<script>alert('foo')</script>"
-- for instance, if some malicious user manages to enter custom text into a field that is displayed in a title tag, or if you ever accidentally use risky text yourself.
The explanation boils down to this -- 'foo'.html_safe
returns an ActiveSupport::SafeBuffer which acts like a String in every way except one: When you append a String to a SafeBuffer (by calling + or <<), that other String is HTML-escaped before it is appended to the SafeBuffer. When you append another SafeBuffer to a SafeBuffer, no escaping will occur. Rails is rendering all of your views under the hood using SafeBuffers, so the updated method above ends up providing Rails with a SafeBuffer that we've controlled to perform escaping on the page_title
"as-needed" rather than "always".
Now, the credit for this answer goes entirely to Henning Koch, and is explained in far more detail at Everything you know about html_safe is wrong -- my recap above attempts only to provide the essence of the explanation in the event that this link ever dies.