Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions ui/src/components/markdown/IframeRender.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<template>
<div class="iframe-wrapper">
<iframe
v-show="visible"
ref="iframeRef"
class="iframe"
:srcdoc="finalSource"
@load="resize"
sandbox="allow-scripts allow-same-origin"
/>
</div>
</template>

<script setup lang="ts">
import { computed, ref, watch, nextTick } from 'vue'
const resize = async () => {
await nextTick()

const iframe = iframeRef.value
if (!iframe) return

const doc = iframe.contentDocument
if (!doc) return

const contentHeight = doc.documentElement.scrollHeight || doc.body.scrollHeight

const viewportHeight = window.innerHeight

const finalHeight = Math.min(contentHeight, viewportHeight)

iframe.style.height = finalHeight + 'px'

iframe.style.overflow = contentHeight > viewportHeight ? 'auto' : 'hidden'
}
const props = withDefaults(
defineProps<{
source?: string
script_exec?: boolean
visible?: boolean
}>(),
{
source: '',
script_exec: true,
visible: true,
},
)

const iframeRef = ref<HTMLIFrameElement>()

// 如果不允许执行 script,就过滤掉
const finalSource = computed(() => {
if (props.script_exec) return props.source

return props.source.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '')
})

// 如果 source 改变才刷新 iframe
watch(
() => props.source,
() => {
if (iframeRef.value) {
iframeRef.value.srcdoc = finalSource.value
}
},
)
</script>

<style scoped>
.iframe-wrapper {
width: 100%;
height: 100%;
}
.iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code template has some potential areas for improvement in terms of security, readability, and performance. Here are my suggestions:

Security Improvements

  1. Content Security Policy (CSP):
    • Ensure that the sandbox attributes include necessary CSP directives like 'content-security-policy': "default-src 'self';" to prevent XSS attacks.
@sandbox="'allow-scripts' 'allow-same-origin'" + (
  !props.script_exec ? '; object-src \'none\'' : ''
)
  1. Frame-Busting Protection:
    • Add basic frame-busting protection by checking against common anti-framebuster methods.
@if (iframes && Array.isArray(iframes)) {
  <script>
    try {
      document.top.location.href = window.self.location.toString();
    } catch (e) {}
  </script>
}
  1. Script Execution Control:
    • If you intend to allow scripts programmatically, ensure they're trusted sources or sanitize them before adding them manually.

Readability and Style Optimization

  1. Inline Styles in CSS:
    • Consider moving the styles inline rather than defining them globally in a separate style block. This can make the component more self-contained.
<style scoped>
.iframe-wrapper {
  width: 100%;
  height: 100%;
}

.iframe {
  width: 100%;
  height: 100%;
  border: none;
}
</style>
  1. Variable Naming Consistency:

    • Use consistent naming conventions. For example, resize, finalSource, iframe, etc., should be clear and descriptive.
  2. Whitespace Reduction:

    • Remove unnecessary spaces around operators and braces to improve legibility.

Performance Improvements

  1. Async/Await Best Practices:
    • Use await without wrapping promises in parentheses when not needed.
async function resize() {
  await nextTick()

  const iframe = iframeRef.value
  if (!iframe) return

  // ...
}
  1. Conditional Rendering Optimization:
    • Avoid unnecessarily re-running watch functions each time props change. Instead, optimize based on prop changes directly.
watch(
  () => props.visible,
  (newVisible) => {
    if (newVisible && iframeRef.value) {
      iframeRef.value.srcdoc = finalSource.value
    }
  },
)

By implementing these improvements, you can enhance the robustness, security, and efficiency of your Vue.js component using Vue Composition API.

Security Note: Always consider the security implications of your implementation when handling external data, especially if it involves executing scripts or allowing arbitrary content loading (<source>).

Loading