fix: 修复学生代码窃取 token、提示词链 XSS 和打分没有上限的问题
- 运行提交代码的 iframe(编辑器预览、随机评分弹窗、/submission/:id、全屏预览) 加上 sandbox,不给 allow-same-origin,代码读不到 localStorage 里的 token - 提示词和 AI 回复的 markdown 渲染时转义原始 HTML,链接只放行 http(s)/mailto; ChainModal 也改用这个渲染函数 - 打分限定 1-5 分(超出返回 422),不能给自己的提交打分 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4014aaabbf
commit
73788c4595
8 files changed
+78
-34
No files matched your search
@@ -14,7 +14,11 @@
|
||||
{{ current.task_title }} · 提交者 {{ current.username }}
|
||||
</n-text>
|
||||
<div class="preview-wrapper">
|
||||
<iframe class="preview-iframe" :srcdoc="previewContent"></iframe>
|
||||
<iframe
|
||||
class="preview-iframe"
|
||||
:sandbox="PREVIEW_SANDBOX"
|
||||
:srcdoc="previewContent"
|
||||
></iframe>
|
||||
</div>
|
||||
<div class="rate-row">
|
||||
<n-rate
|
||||
@@ -38,7 +42,10 @@ import { ref, watch, onUnmounted } from "vue"
|
||||
import { useMessage } from "naive-ui"
|
||||
import { Submission } from "../../api"
|
||||
import { streaming } from "../../store/prompt"
|
||||
import { buildPreviewDocument } from "../../utils/previewDocument"
|
||||
import {
|
||||
buildPreviewDocument,
|
||||
PREVIEW_SANDBOX,
|
||||
} from "../../utils/previewDocument"
|
||||
import type { RandomRatingItem } from "../../utils/type"
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
@@ -42,7 +42,11 @@
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<div class="iframe-wrapper" :style="iframeWrapperStyle">
|
||||
<iframe class="iframe" :srcdoc="previewContent"></iframe>
|
||||
<iframe
|
||||
class="iframe"
|
||||
:sandbox="PREVIEW_SANDBOX"
|
||||
:srcdoc="previewContent"
|
||||
></iframe>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -54,7 +58,10 @@ import { submission } from "../../store/submission"
|
||||
import { useMessage } from "naive-ui"
|
||||
import { Icon } from "@iconify/vue"
|
||||
import copy from "copy-text-to-clipboard"
|
||||
import { buildPreviewDocument } from "../../utils/previewDocument"
|
||||
import {
|
||||
buildPreviewDocument,
|
||||
PREVIEW_SANDBOX,
|
||||
} from "../../utils/previewDocument"
|
||||
|
||||
interface Props {
|
||||
html: string
|
||||
@@ -145,9 +152,14 @@ function open() {
|
||||
} else {
|
||||
const newTab = window.open("about:blank", "_blank")
|
||||
if (!newTab) return
|
||||
newTab.document.open()
|
||||
newTab.document.write(getContent())
|
||||
newTab.document.close()
|
||||
// 新标签页和本站同源,代码要放进带 sandbox 的 iframe 里跑
|
||||
const doc = newTab.document
|
||||
doc.open()
|
||||
doc.write(
|
||||
`<!DOCTYPE html><title>预览</title><style>html,body,iframe{margin:0;width:100%;height:100%;border:0;display:block}</style><iframe sandbox="${PREVIEW_SANDBOX}"></iframe>`,
|
||||
)
|
||||
doc.close()
|
||||
doc.querySelector("iframe")!.srcdoc = getContent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@
|
||||
import { computed, ref, watch } from "vue"
|
||||
import { NPopconfirm, NButton, useMessage } from "naive-ui"
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { marked } from "marked"
|
||||
import { renderMarkdown } from "../../utils/markdown"
|
||||
import { Prompt, Submission } from "../../api"
|
||||
import type { PromptRound } from "../../utils/type"
|
||||
import { user, roleSuper } from "../../store/user"
|
||||
@@ -301,10 +301,6 @@ const selectedPageHtml = computed(() => {
|
||||
return `<!DOCTYPE html><html><head><meta charset="utf-8">${style}</head><body>${round.html}${script}</body></html>`
|
||||
})
|
||||
|
||||
function renderMarkdown(text: string): string {
|
||||
return marked.parse(text) as string
|
||||
}
|
||||
|
||||
function isPromptLong(text: string): boolean {
|
||||
return text.length > 220 || text.split(/\r?\n/).length > 4
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user