将 Vue 组件/模板渲染为 (html) 字符串

时间:2021-02-24 11:09:44

标签: vue.js vuejs2 xss buefy html-sanitizing

我正在使用一个需要字符串的 Vue 组件。我喜欢在字符串中使用模板值,但当然应该是 XSS 安全的。

当前不安全的例子

这是不安全的,因为 this.name 是不安全的。我可以使用 NPM 包对名称进行 html 编码,但我更喜欢使用 Vue。

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  props: {
    name: { type: String, required: false },
  },
  methods: {
    showModal() {
      this.$buefy.dialog.confirm({
        title: 'myTitle',
        message: `<p>Hello ${this.name}</p>`, // unsafe - possible XSS!
        cancelText: 'Cancel',
        confirmText: 'OK',
        type: 'is-success',
        onConfirm: async () => {
          // something
        },
      });
    },
  },
});
</script>

这是使用的 Buefy 组件的问题,如文档所示 here

enter image description here

所需设置

我创建了一个新组件,在本例中我将其命名为 ModalMessage.Vue

<template>
    <p>Hello {{name}}</p>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  props: {
    name: { type: String, required: true },
  },
});
</script>

然后我喜欢在 Typescript 中将 ModalMessage.Vue 渲染为 string

<script lang="ts">
import Vue from 'vue';
import ModalMessage from 'ModalMessage.vue';

export default Vue.extend({
  props: {
    name: { type: String, required: false },
  },
  methods: {
    showModal() {
      this.$buefy.dialog.confirm({
        title: 'myTitle',
        message:, // todo render ModalMessage and pass name prop
        cancelText: 'Cancel',
        confirmText: 'OK',
        type: 'is-success',
        onConfirm: async () => {
          // something
        },
      });
    },
  },
});
</script>

问题

如何呈现 ModalMessage.Vue 并将名称道具传递给 string

我很确定这是可能的 - 我过去见过。不幸的是,我无法在网络或 StackOverflow 上找到它。我只能找到关于渲染模板 from 字符串的问题,但我不需要 - 它需要是 to 字符串。

2 个答案:

答案 0 :(得分:1)

恕我直言,您真正的问题是“如何将 Buefy 的 Dialog 组件与用户提供的内容一起使用并在 XSS 方面保持安全”

所以您想要创建一些 HTML,在该 HTML 内容中包含一些用户提供的内容 (this.name) 并将其显示在对话框中。您是对的,将未经过滤的用户输入放入 messageDialog 参数是不安全的(如 Buefy docs 中明确指出)

但您的“所需设置”似乎没有必要复杂。恕我直言,最简单的方法是使用(记录不足)事实,即 Buefy message 配置对象的 Dialog 参数可以是 VNode 的 Array 而不是字符串。它的文档很差,但是从源 herehere 中可以很清楚地看出,如果您传递 VNode 的数组,Buefy 会将该内容放入 Dialogs 默认插槽中,而不是使用 v-html 呈现它(这是危险的部分)

在 Vue 中获得 ArrayVNode 的最简单方法是使用插槽...

所以组件:

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  methods: {
    showModal() {
      this.$buefy.dialog.confirm({
        title: 'myTitle',
        message: this.$slots.default, // <- this is important part
        cancelText: 'Cancel',
        confirmText: 'OK',
        type: 'is-success',
        onConfirm: async () => {
          // something
        },
      });
    },
  },
});
</script>

它的用法:

<MyDialog>
  <p>Hello {{name}}</p>
</MyDialog>

<MyDialog>
  <ModalMessage :name="name" />
</MyDialog>

在这两种情况下,如果 name 包含任何 HTML,它将是 encoded by Vue

Here 是上述技术的简单演示(使用纯 JS - 而不是 TS)

答案 1 :(得分:0)

试试这个。

<script lang="ts">
import Vue from 'vue';
import ModalMessage from 'ModalMessage.vue';

export default Vue.extend({
  props: {
    name: { type: String, required: false },
  }, 
  methods: {
    showModal() {
      const message = new Vue({
        components: { ModalMessage },
        render: h => h('ModalMessage', { name: this.name })
      })
      message.$mount()

      const dialog = this.$buefy.dialog.confirm({
        title: 'myTitle',
        message: [message._vnode],
        cancelText: 'Cancel',
        confirmText: 'OK',
        type: 'is-success',
        onConfirm: async () => {
          // something
        },
      });

      dialog.$on('hook:beforeDestroy', () => {
          message.$destroy()
      });
    },
  },
});
</script>

源代码:

enter image description here

enter image description here

演示:

enter image description here

相关问题