haml: Javascript escaping invalid, generates SyntaxError: expected expression, got '&'
The 5.0.0 release notes refers to:
HTML escape interpolated code in filters. #770 (Matt Wildig)
:javascript #{JSON.generate(foo: "bar")} Haml 4 output: {"foo":"bar"} Haml 5 output: {"foo":"bar"}
However, I have (Haml 5.0.1):
:javascript
var list1= ["a", 'b', 'c'];
var hash1= {a: "a", b: 'b'};
var list2= #{JSON.generate( [:a, :b, :c])};
var hash2= #{JSON.generate(foo: "bar")};
In the browser this results into:
<script>
var list1= ["a", 'b', 'c'];
var hash1= {a: "a", b: 'b'};
var list2= ["a","b","c"];
var hash2= {"foo":"bar"};
</script>
The browsers runs into an error on line 4 (list2) and 5 (hash2):
SyntaxError: expected expression, got ‘&’
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 6
- Comments: 29 (13 by maintainers)
Commits related to this issue
- Fix reference about filter HTML-escape See: https://github.com/haml/haml/issues/940#issuecomment-315695441 — committed to haml/haml by k0kubun 7 years ago
- Fix JSON escaping inside HAML/JS templates More info here: https://github.com/haml/haml/issues/940 — committed to ministryofjustice/Claim-for-Crown-Court-Defence by lostie 6 years ago
I second @Justin-Maxwell’s suggestion of documentation updates. This hit us with an update to
haml-railswhich happened to pull inhaml5.x and I didn’t even notice that there would be a change. In testing we never saw this until we pushed to production. (Thank you, @k0kubun for maintaining aCHANGELOG, whichhaml-railsdoesn’t have.)For Rails apps and JSON, using
raw (...)or adding.html_safeis just marking the content as “safe”. That assumes you’ve already cleared it of tainted data. This matches the old behavior but you can do better with#json_escape:New to Haml…
TL;DR - in ‘production’ haml/template.rb forces :escape_html = true, so setting it false in the initializer doesn’t work!
After hours of trying to work out how to get a Ruby hash into a JavaScript object within a :javascript block (because should be easy, right?), and then, with reluctance, setting the global :escape_html option (because, outside of a few places, like :javascript, encoding to entities makes sense, right?)
I finally had hashes in objects - or so I thought…
Deploy to the server, and all the
"I has been fighting suddenly came back to haunt me. Gone in development, but not in production. For some reason even …Haml::Template.options[:escape_html] = false… wasn’t working. I was not a happy hamler!So anyway, a long while later of running production locally with breakpoints to narrow down what was going on - it turns out that, during initialization, in production, there’s a line towards the end of
/haml-5.0.2/lib/haml/template.rbthat gets processed right afterinitializers/haml.rbthat says:Haml::Template.options[:escape_html] = true😕
But, fortunately, in this thread, (checking closed issues to see if it had been reported previously) I have learn’t of ‘html_safe’ - something not mentioned as far as I can tell anywhere in the docs? [I know it’s not a Haml feature]
And now I know that, in a
:javascriptblockvar = #{hash.to_json.html_safe}works fine. Still seems a bit verbose to have to put that in everywhere. I’d think a Rails-oriented HTML page generator would deal with Hash→JS-Object without requiring much prior-knowledge or thought. But if that’s how it’s to be……please add a mention of hash.to_json.html_safe in a couple of places, e.g.
This example in the Ruby Interpolation: #{} section of the reference:
… perhaps the FAQ, and also mention html_safe in the [Escaping HTML] section of the docs, to make it easier for the noobs who come after me… 😃
It seems that you use ActiveRecord and it’s likely that you can use
html_safeorrawas I said. Why don’t you use that? Did you check https://github.com/haml/haml/pull/770?This would also solve your problem if you’re using Rails.
That’s because there are possibly vulnerable cases in some string interpolation. Following code can generate unexpected code in Haml 4, but not in Haml 5 with escape_html: true.
Does it make sense?
I wrote a little script to help us find cases of interpolation inside of HAML
:javascriptfilters:run as something like
bundle exec ruby haml_check.rb $(find . -name '*haml')In our case, we wanted to make sure every interpolation ended with
to_json.html_safe; you may need to tweak the script for your own case.Okay, a pitty, but I can do this.
However, the code giving me this problem is like:
:javascript var my_var=Array( #{ruby_array});
In the HAML Reference file it says: “Currently, filters ignore the :escape_html http://haml.info/docs/yardoc/Haml/Options.html#escape_html-instance_method option. This means that #{} interpolation within filters is never HTML-escaped.”
Does this mean that the documentation is outdated?
For others still facing migration pains related to this issue, I’ve opened a PR #984 that adds an
:escape_interpolated_htmloption for backwards compatibility with v4. Using the PR, setHaml::Template.options[:escape_interpolated_html] = falsein an initializer to get the old version 4 behavior without needing to refactor the rest of your application code.@k0kubun is it possible to set the
escape_htmloption for a particular filter? For instance,Just looking to keep the same behavior as before.