flawopen.com/Cross-Site Scripting/Ruby
Imagine a guestbook where visitors write a public note. Stored XSS is like someone writing a note that isn't just text — it's a hidden trick that makes the guestbook page itself start doing things, like stealing the next visitor's login session, the moment anyone opens the page to read it.
Cross-site scripting happens when untrusted input reaches the rendered page without proper output encoding. Rails' ERB templates auto-escape by default, so this almost always traces back to .html_safe or the raw() helper being called on untrusted data.
In 2005, an 19-year-old user exploited a stored XSS flaw on MySpace to create the "Samy" worm — a script that added itself to every profile that viewed it, infecting over a million profiles within about 20 hours and forcing MySpace offline to contain it. It remains one of the most-cited demonstrations of how quickly stored XSS can self-propagate.
Source: widely documented in security industry retrospectives — see References below.<%# html_safe skips escaping &%>
<div>
<%= @comment.body.html_safe %>
</div>
<%# ERB output auto-escapes by default &%>
<div>
<%= @comment.body %>
</div>
Rails wraps ERB output in automatic HTML escaping by default — <%= @comment.body %> converts special characters to their entity equivalents before rendering. .html_safe doesn't sanitize anything; it just marks the string as "trust this as already-safe HTML," which disables escaping for that string entirely.
Its name suggests it makes a string safe. It does the opposite: it tells Rails to skip escaping that string, trusting it's already safe HTML. Called on untrusted user input, it reintroduces exactly the vulnerability Rails' default escaping exists to prevent.
raw(@comment.body) and @comment.body.html_safe both disable escaping for the value — same risk, different syntax, both need the same review scrutiny.
If a value is marked .html_safe at one point and later concatenated with untrusted input, Rails may not re-escape the combined string as expected — treat any .html_safe value as permanently sensitive, not just at its point of creation.
True for plain <%= %> output — false the moment .html_safe or raw() is involved.
Any reflected value counts — a search query echoed on a results page, a URL parameter shown in an error message, a filename displayed after upload.
grep -rn "\.html_safe\|raw(" app/views/
Same underlying shape — untrusted data mixed into a command's structure without encoding — but the target and damage differ: SQL injection targets the database, XSS targets other users' browsers.
Yes, when applied immediately before marking safe, using an allow-list of permitted tags/attributes — the risk is calling .html_safe without sanitizing first, not the sanitizer itself.