A user reported a bug in Slava, a bot that syncs Strava activities to Slack, where they couldn’t subscribe to the paid version because their Slack team name contained a quote. That was a rookie mistake on my part in HTML escaping that almost cost me $9.99. Interestingly, it required a rather non-trivial fix.
The bot’s code extensively uses ERB, the standard Ruby templating system. The implementation attempts to render a team name in bold. The recommended way to do this is to combine .html_safe
with the displayed value.
Aside of being not very elegant, this almost works. Unfortunately, because we are trying to pass an argument into JavaScript the page will be broken if the value of name
contains a line break.
So how do we fix that?
First, we try to avoid using the cumbersome html_safe
by sending a value into a JavaScript variable directly, which lets us reuse it later without having to mix Ruby ERB markup.
This looks unsafe, but assuming it works, we can reuse this variable directly.
For the same reason as above the page will be broken when the name has a quote, a double quote, or a carriage return. This value must be encoded in a safe manner before rendering it.
The standard ERB way to make a value safe is to escape it with html_escape, abbreviated as h
.
This does fix the issue with single and double quotes.
However, it will still render a carriage return, causing the following invalid JavaScript with the team name is “line1\nline2”.
This is because ERB considers line breaks as safe.
We can fix this by converting the safe value to JSON. This will quote and escape it for us, works for nil
, and will prevent XSS.
The value can thus be rendered directly without extra quotes.
In Rails, something similar is available as "<%= j name %>"
, or escape_javascript.
See slack-strava@3a70e5f7 for a complete implementation with tests.