fix(editor): Sanitize HTML binary-data before rendering in the UI (#7400)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-10-11 12:09:19 +02:00
committed by GitHub
parent 47e8953ec9
commit 2b075bfc2d
7 changed files with 31 additions and 21 deletions

View File

@@ -7,16 +7,17 @@
<source :src="embedSource" :type="binaryData.mimeType" />
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
</video>
<audio v-if="binaryData.fileType === 'audio'" controls autoplay>
<audio v-else-if="binaryData.fileType === 'audio'" controls autoplay>
<source :src="embedSource" :type="binaryData.mimeType" />
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
</audio>
<vue-json-pretty
v-else-if="binaryData.fileType === 'json'"
:data="jsonData"
:data="data"
:deep="3"
:showLength="true"
/>
<run-data-html v-else-if="binaryData.fileType === 'html'" :inputHtml="data" />
<embed v-else :src="embedSource" class="binary-data" :class="embedClass()" />
</span>
</span>
@@ -30,11 +31,13 @@ import { jsonParse } from 'n8n-workflow';
import type { PropType } from 'vue';
import VueJsonPretty from 'vue-json-pretty';
import { useWorkflowsStore } from '@/stores';
import RunDataHtml from '@/components/RunDataHtml.vue';
export default defineComponent({
name: 'BinaryDataDisplayEmbed',
components: {
VueJsonPretty,
RunDataHtml,
},
props: {
binaryData: {
@@ -47,27 +50,29 @@ export default defineComponent({
isLoading: true,
embedSource: '',
error: false,
jsonData: '',
data: '',
};
},
computed: {
...mapStores(useWorkflowsStore),
},
async mounted() {
const { id, data, fileName, fileType, mimeType } = (this.binaryData || {}) as IBinaryData;
const { id, data, fileName, fileType, mimeType } = this.binaryData;
const isJSONData = fileType === 'json';
const isHTMLData = fileType === 'html';
if (!id) {
if (isJSONData) {
this.jsonData = jsonParse(atob(data));
if (isJSONData || isHTMLData) {
this.data = jsonParse(atob(data));
} else {
this.embedSource = 'data:' + mimeType + ';base64,' + data;
}
} else {
try {
const binaryUrl = this.workflowsStore.getBinaryUrl(id, 'view', fileName, mimeType);
if (isJSONData) {
this.jsonData = await (await fetch(binaryUrl)).json();
if (isJSONData || isHTMLData) {
const fetchedData = await fetch(binaryUrl, { credentials: 'include' });
this.data = await (isJSONData ? fetchedData.json() : fetchedData.text());
} else {
this.embedSource = binaryUrl;
}
@@ -80,7 +85,7 @@ export default defineComponent({
},
methods: {
embedClass(): string[] {
const { fileType } = (this.binaryData || {}) as IBinaryData;
const { fileType } = this.binaryData;
return [fileType ?? 'other'];
},
},

View File

@@ -347,7 +347,7 @@
</Suspense>
<Suspense v-else-if="hasNodeRun && isPaneTypeOutput && displayMode === 'html'">
<run-data-html :inputData="inputData" />
<run-data-html :inputHtml="inputData[0].json.html" />
</Suspense>
<Suspense v-else-if="hasNodeRun && isSchemaView">
@@ -1320,7 +1320,9 @@ export default defineComponent({
},
isViewable(index: number, key: string): boolean {
const { fileType } = this.binaryData[index][key];
return !!fileType && ['image', 'audio', 'video', 'text', 'json', 'pdf'].includes(fileType);
return (
!!fileType && ['image', 'audio', 'video', 'text', 'json', 'pdf', 'html'].includes(fileType)
);
},
isDownloadable(index: number, key: string): boolean {
const { mimeType, fileName } = this.binaryData[index][key];

View File

@@ -1,11 +1,10 @@
<template>
<iframe class="__html-display" :srcdoc="html" />
<iframe class="__html-display" :srcdoc="sanitizedHtml" />
</template>
<script lang="ts">
import type { PropType } from 'vue';
import sanitizeHtml, { defaults, type IOptions as SanitizeOptions } from 'sanitize-html';
import type { INodeExecutionData } from 'n8n-workflow';
const sanitizeOptions: SanitizeOptions = {
allowVulnerableTags: false,
@@ -24,14 +23,13 @@ const sanitizeOptions: SanitizeOptions = {
export default {
name: 'RunDataHtml',
props: {
inputData: {
type: Array as PropType<INodeExecutionData[]>,
inputHtml: {
type: String as PropType<string>,
},
},
computed: {
html() {
const markup = (this.inputData?.[0].json.html as string) ?? '';
return sanitizeHtml(markup, sanitizeOptions);
sanitizedHtml() {
return sanitizeHtml(this.inputHtml, sanitizeOptions);
},
},
};