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:
xuyueandClaude Opus 5.5 committed 2026-10-26 21:33:26 +08:00
1 parent 4014aaabbf
commit 73788c4595
8 files changed
+78 -34

No files matched your search

+9 -18
View File
@@ -1,39 +1,30 @@
<script lang="ts" setup>
import { onMounted, useTemplateRef } from "vue"
import { onMounted, ref } from "vue"
import { Submission } from "../api"
import type { SubmissionAll } from "../utils/type"
import { buildPreviewDocument } from "../utils/previewDocument"
import { buildPreviewDocument, PREVIEW_SANDBOX } from "../utils/previewDocument"
interface Props {
id: string
}
const props = defineProps<Props>()
const iframe = useTemplateRef<HTMLIFrameElement>("iframe")
const content = ref("")
async function init() {
const submission: SubmissionAll = await Submission.get(props.id)
Submission.incrementView(props.id)
if (!iframe.value) return
const doc = iframe.value.contentDocument
if (doc) {
doc.open()
doc.write(
buildPreviewDocument({
html: submission.html,
css: submission.css,
js: submission.js,
}),
)
doc.close()
}
content.value = buildPreviewDocument({
html: submission.html,
css: submission.css,
js: submission.js,
})
}
onMounted(init)
</script>
<template>
<iframe class="iframe" ref="iframe"></iframe>
<iframe class="iframe" :sandbox="PREVIEW_SANDBOX" :srcdoc="content"></iframe>
</template>
<style scoped>
.iframe {