如何使用 Nunjucks 安全地将 JSON 渲染为内联 <script>?

How to safely render JSON into an inline <script> using Nunjucks?

我们有一个服务器端呈现的 HTML 页面,其中包含一个外部 JavaScript 文件。要触发该文件中的代码,我们要调用一个函数并向其传递一些动态数据(以 JSON 的形式):

<script src="/static/foo/bar.js"></script>
<script>
  foo.bar.init({biz: 42, qux: "quux"});
</script>

我们从 Nunjucks 模板渲染它,并在上下文中将 JSON 对象作为值 data 传递。这可以包含任意数据,包括用户提供的内容。

这是安全的但不起作用,因为 <>& 正在被转义(感谢 Nunjucks 自动转义):

foo.bar.init({{ data | dump }});

这可行,但不安全,因为 JSON 中的字符串可能包含文本 </script>:

foo.bar.init({{ data | dump | safe }});

如何说服 Nunjucks 渲染这个 JSON 以便安全正确地解释它?听起来这应该是一个已解决的问题,但我在任何地方都找不到预制的解决方案。

暂时这样做:

  /**
   * Returns a JSON stringified version of the value, safe for inclusion in an
   * inline <script> tag. The optional argument 'spaces' can be used for
   * pretty-printing.
   *
   * Output is NOT safe for inclusion in HTML! If that's what you need, use the
   * built-in 'dump' filter instead.
   */
  env.addFilter('json', function (value, spaces) {
    if (value instanceof nunjucks.runtime.SafeString) {
      value = value.toString()
    }
    const jsonString = JSON.stringify(value, null, spaces).replace(/</g, '\u003c')
    return nunjucks.runtime.markSafe(jsonString)
  })

用法:

<script src="/static/foo/bar.js"></script>
<script>
  foo.bar.init({{ data | json }});
</script>

仍然欢迎更好的解决方案。