🐞 fix(WebUI): 解决XSS注入的问题
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
"js-md5": "^0.8.3",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "^15.0.7",
|
||||
"markdown-it": "^14.1.0",
|
||||
"pinia": "2.1.6",
|
||||
"remixicon": "3.5.0",
|
||||
"vee-validate": "4.11.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { marked } from 'marked';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/github.css';
|
||||
import { useI18n } from '@/i18n/composables';
|
||||
@@ -74,29 +74,28 @@ function openRepoInNewTab() {
|
||||
}
|
||||
}
|
||||
|
||||
// 配置markdown-it,启用代码高亮
|
||||
const md = new MarkdownIt({
|
||||
html: true, // 启用HTML标签
|
||||
breaks: true, // 换行转<br>
|
||||
linkify: true, // 自动转链接
|
||||
typographer: false, // 禁用智能引号
|
||||
highlight: function(code, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return hljs.highlight(code, { language: lang }).value;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return hljs.highlightAuto(code).value;
|
||||
}
|
||||
});
|
||||
|
||||
// 渲染Markdown内容
|
||||
function renderMarkdown(content) {
|
||||
if (!content) return '';
|
||||
|
||||
// 配置marked使用highlight.js进行语法高亮
|
||||
marked.setOptions({
|
||||
highlight: function(code, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return hljs.highlight(code, { language: lang }).value;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
gfm: true, // GitHub Flavored Markdown
|
||||
breaks: true, // Convert \n to <br>
|
||||
headerIds: true, // Add id attributes to headers
|
||||
mangle: false // Don't mangle email addresses
|
||||
});
|
||||
|
||||
return marked(content);
|
||||
return md.render(content);
|
||||
}
|
||||
|
||||
// 刷新README内容
|
||||
|
||||
@@ -7,9 +7,17 @@ import LanguageSwitcher from '@/components/shared/LanguageSwitcher.vue';
|
||||
import {md5} from 'js-md5';
|
||||
import {useAuthStore} from '@/stores/auth';
|
||||
import {useCommonStore} from '@/stores/common';
|
||||
import {marked} from 'marked';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import { useI18n } from '@/i18n/composables';
|
||||
|
||||
// 配置markdown-it,默认安全设置
|
||||
const md = new MarkdownIt({
|
||||
html: false, // 启用HTML标签
|
||||
breaks: true, // 换行转<br>
|
||||
linkify: true, // 自动转链接
|
||||
typographer: false // 禁用智能引号
|
||||
});
|
||||
|
||||
const customizer = useCustomizerStore();
|
||||
const { t } = useI18n();
|
||||
let dialog = ref(false);
|
||||
@@ -323,7 +331,7 @@ commonStore.getStartTime();
|
||||
|
||||
<div v-if="releaseMessage"
|
||||
style="background-color: #646cff24; padding: 16px; border-radius: 10px; font-size: 14px; max-height: 400px; overflow-y: auto;"
|
||||
v-html="marked(releaseMessage)" class="markdown-content">
|
||||
v-html="md.render(releaseMessage)" class="markdown-content">
|
||||
</div>
|
||||
|
||||
<div class="mb-4 mt-4">
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
</v-avatar>
|
||||
<div class="bot-message-content">
|
||||
<div class="message-bubble bot-bubble">
|
||||
<div v-html="marked(msg.message)" class="markdown-content"></div>
|
||||
<div v-html="md.render(msg.message)" class="markdown-content"></div>
|
||||
</div>
|
||||
<div class="message-actions">
|
||||
<v-btn :icon="getCopyIcon(index)" size="small" variant="text"
|
||||
@@ -261,7 +261,7 @@
|
||||
<script>
|
||||
import { router } from '@/router';
|
||||
import axios from 'axios';
|
||||
import { marked } from 'marked';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import { ref } from 'vue';
|
||||
import { useCustomizerStore } from '@/stores/customizer';
|
||||
import { useI18n, useModuleI18n } from '@/i18n/composables';
|
||||
@@ -270,8 +270,11 @@ import ProviderModelSelector from '@/components/chat/ProviderModelSelector.vue';
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/github.css';
|
||||
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
// 配置markdown-it,启用代码高亮
|
||||
const md = new MarkdownIt({
|
||||
html: false, // 禁用HTML标签,防XSS
|
||||
breaks: true, // 换行转<br>
|
||||
linkify: true, // 自动转链接
|
||||
highlight: function (code, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
@@ -303,7 +306,7 @@ export default {
|
||||
t,
|
||||
tm,
|
||||
router,
|
||||
marked,
|
||||
md,
|
||||
ref
|
||||
};
|
||||
},
|
||||
|
||||
@@ -318,12 +318,16 @@
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { VueMonacoEditor } from '@guolao/vue-monaco-editor';
|
||||
import { marked } from 'marked';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import { useCommonStore } from '@/stores/common';
|
||||
import { useI18n, useModuleI18n } from '@/i18n/composables';
|
||||
|
||||
marked.setOptions({
|
||||
breaks: true
|
||||
// 配置markdown-it,默认安全设置
|
||||
const md = new MarkdownIt({
|
||||
html: false, // 禁用HTML标签(关键!)
|
||||
breaks: true, // 换行转<br>
|
||||
linkify: true, // 自动转链接
|
||||
typographer: false // 禁用智能引号(避免干扰)
|
||||
});
|
||||
|
||||
export default {
|
||||
@@ -879,8 +883,9 @@ export default {
|
||||
// 处理字符串内容
|
||||
final_content = content;
|
||||
} else if (!final_content) return this.tm('status.emptyContent');
|
||||
// 使用marked处理Markdown格式
|
||||
return marked(final_content);
|
||||
|
||||
// 使用markdown-it处理,默认安全(html: false会禁用HTML标签)
|
||||
return md.render(final_content);
|
||||
},
|
||||
|
||||
// 显示成功消息
|
||||
|
||||
Reference in New Issue
Block a user