feat(frontend): Add HTML result rendering (#1160)

* feat(frontend): Add HTML result rendering

* fix(frontend): Add reference for XSS

* fix(frontend): Require approval only in apps

* fix(frontend): Require approval in public apps
This commit is contained in:
Ádám Kovács
2023-01-26 18:03:25 +01:00
committed by GitHub
parent 62c9e2b553
commit e400eefee1
4 changed files with 44 additions and 2 deletions
@@ -7,11 +7,13 @@
import autosize from 'svelte-autosize'
export let result: any
export let requireHtmlApproval = false
let resultKind:
| 'json'
| 'table-col'
| 'table-row'
| 'html'
| 'png'
| 'file'
| 'jpeg'
@@ -19,9 +21,11 @@
| 'error'
| 'approval'
| undefined
$: resultKind = inferResultKind(result)
let forceJson = false
let enableHtml = false
function isRectangularArray(obj: any) {
if (!Array.isArray(obj) || obj.length == 0) {
@@ -53,6 +57,8 @@
return 'table-row'
} else if (keys.map((k) => Array.isArray(result[k])).reduce((a, b) => a && b)) {
return 'table-col'
} else if (keys.length == 1 && keys[0] == 'html') {
return 'html'
} else if (keys.length == 1 && keys[0] == 'png') {
return 'png'
} else if (keys.length == 1 && keys[0] == 'jpeg') {
@@ -119,6 +125,34 @@
</tbody>
</TableCustom>
</div>
{:else if !forceJson && resultKind == 'html'}
<div class="h-full">
{#if !requireHtmlApproval || enableHtml}
{@html result.html}
{:else}
<div class="font-main text-sm">
<div class="flex flex-col">
<div class="bg-red-400 py-1 rounded-t text-white font-bold text-center">
Warning
</div>
<p
class="text-gray-600 mb-2 text-left border-2 !border-t-0 rounded-b border-red-400 overflow-auto p-1"
>Rendering HTML can expose you to <a href="https://owasp.org/www-community/attacks/xss/" target="_blank" rel="noreferrer" class="hover:underline">XSS attacks</a>.
Only enable it if you trust the author of the script.
</p>
</div>
<div class="center-center">
<Button
size="sm"
color="dark"
on:click={() => enableHtml = true}
>
Enable HTML rendering
</Button>
</div>
</div>
{/if}
</div>
{:else if !forceJson && resultKind == 'png'}
<div class="h-full"
><img alt="png rendered" class="w-auto h-full" src="data:image/png;base64,{result.png}" />
@@ -1,11 +1,14 @@
<script lang="ts">
import DisplayResult from '$lib/components/DisplayResult.svelte'
import { getContext } from 'svelte'
import type { AppInput } from '../inputType'
import { IS_APP_PUBLIC_CONTEXT_KEY } from '../types'
import RunnableWrapper from './helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
let result: any = undefined
export const staticOutputs: string[] = ['result', 'loading']
@@ -16,6 +19,6 @@
Results
</div>
<div class="p-2">
<DisplayResult {result} />
<DisplayResult {result} {requireHtmlApproval} />
</div>
</RunnableWrapper>
@@ -179,4 +179,6 @@ export type AppEditorContext = {
export type EditorMode = 'dnd' | 'preview'
export type EditorBreakpoint = 'sm' | 'lg'
export const IS_APP_PUBLIC_CONTEXT_KEY = 'isAppPublicContext' as const
type ComponentID = string
@@ -2,12 +2,13 @@
import { browser } from '$app/environment'
import { page } from '$app/stores'
import AppPreview from '$lib/components/apps/editor/AppPreview.svelte'
import type { EditorBreakpoint } from '$lib/components/apps/types'
import { IS_APP_PUBLIC_CONTEXT_KEY, type EditorBreakpoint } from '$lib/components/apps/types'
import { Alert, Skeleton } from '$lib/components/common'
import { WindmillIcon } from '$lib/components/icons'
import { AppService, AppWithLastVersion, GlobalUserInfo, UserService } from '$lib/gen'
import { userStore } from '$lib/stores'
import { setContext } from 'svelte'
import github from 'svelte-highlight/styles/github'
import { writable } from 'svelte/store'
@@ -15,6 +16,8 @@
let user: GlobalUserInfo | undefined = undefined
let notExists = false
setContext(IS_APP_PUBLIC_CONTEXT_KEY, true)
async function loadApp() {
try {
app = await AppService.getPublicAppBySecret({