#{}
is for escaped string interpolation which automatically escapes the input and is thus more suitable for plain strings rather than JS objects:
script var data = #{JSON.stringify(data)}
<script>var data = {"foo":"bar"} </script>
!{}
is for unescaped code interpolation, which is more suitable for objects:
script var data = !{JSON.stringify(data)}
<script>var data = {"foo":"bar"} </script>
CAUTION: Unescaped code can be dangerous. You must be sure to sanitize any user inputs to avoid cross-site scripting (XSS).
E.g.:
{ foo: 'bar </script><script> alert("xss") //' }
will become:
<script>var data = {"foo":"bar </script><script> alert("xss") //"}</script>
Possible solution: Use .replace(/<\//g, '<\\/')
script var data = !{JSON.stringify(data).replace(/<\//g, '<\\/')}
<script>var data = {"foo":"bar<\/script><script>alert(\"xss\")//"}</script>
The idea is to prevent the attacker to:
JSON.stringify
escapes the quotes</script>
string, the replace statement will take care of ithttps://github.com/pugjs/pug/blob/355d3dae/examples/dynamicscript.pug