fix: XSS prevention and frontend timer cleanup
- Add DOMPurify to sanitize all v-html content - Create sanitize.js utility with sanitizeHtml() - Wrap renderMarkdown output in Step4Report and Step5Interaction - Guard all setInterval calls with clearInterval to prevent timer leaks
This commit is contained in:
parent
985f89f49a
commit
af5c1c6841
|
|
@ -11,6 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
|
"dompurify": "^3.3.3",
|
||||||
"vue": "^3.5.24",
|
"vue": "^3.5.24",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -464,10 +464,12 @@ let statusTimer = null
|
||||||
let detailTimer = null
|
let detailTimer = null
|
||||||
|
|
||||||
const startStatusPolling = () => {
|
const startStatusPolling = () => {
|
||||||
|
if (statusTimer) clearInterval(statusTimer)
|
||||||
statusTimer = setInterval(fetchRunStatus, 2000)
|
statusTimer = setInterval(fetchRunStatus, 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDetailPolling = () => {
|
const startDetailPolling = () => {
|
||||||
|
if (detailTimer) clearInterval(detailTimer)
|
||||||
detailTimer = setInterval(fetchRunStatusDetail, 3000)
|
detailTimer = setInterval(fetchRunStatusDetail, 3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -393,6 +393,7 @@
|
||||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick, h, reactive } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick, h, reactive } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { getAgentLog, getConsoleLog } from '../api/report'
|
import { getAgentLog, getConsoleLog } from '../api/report'
|
||||||
|
import { sanitizeHtml } from '../utils/sanitize.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
@ -1968,7 +1969,7 @@ const renderMarkdown = (content) => {
|
||||||
}
|
}
|
||||||
html = tokens.join('')
|
html = tokens.join('')
|
||||||
|
|
||||||
return html
|
return sanitizeHtml(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTimelineItemClass = (log, idx, total) => {
|
const getTimelineItemClass = (log, idx, total) => {
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { chatWithReport, getReport, getAgentLog } from '../api/report'
|
import { chatWithReport, getReport, getAgentLog } from '../api/report'
|
||||||
|
import { sanitizeHtml } from '../utils/sanitize.js'
|
||||||
import { interviewAgents, getSimulationProfilesRealtime } from '../api/simulation'
|
import { interviewAgents, getSimulationProfilesRealtime } from '../api/simulation'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -635,7 +636,7 @@ const renderMarkdown = (content) => {
|
||||||
}
|
}
|
||||||
html = tokens.join('')
|
html = tokens.join('')
|
||||||
|
|
||||||
return html
|
return sanitizeHtml(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat Methods
|
// Chat Methods
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import DOMPurify from 'dompurify'
|
||||||
|
|
||||||
|
export function sanitizeHtml(html) {
|
||||||
|
if (!html) return ''
|
||||||
|
return DOMPurify.sanitize(html)
|
||||||
|
}
|
||||||
|
|
@ -292,6 +292,7 @@ const startBuildGraph = async () => {
|
||||||
const startGraphPolling = () => {
|
const startGraphPolling = () => {
|
||||||
addLog('Started polling for graph data...')
|
addLog('Started polling for graph data...')
|
||||||
fetchGraphData()
|
fetchGraphData()
|
||||||
|
if (graphPollTimer) clearInterval(graphPollTimer)
|
||||||
graphPollTimer = setInterval(fetchGraphData, 10000)
|
graphPollTimer = setInterval(fetchGraphData, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,6 +316,7 @@ const fetchGraphData = async () => {
|
||||||
|
|
||||||
const startPollingTask = (taskId) => {
|
const startPollingTask = (taskId) => {
|
||||||
pollTaskStatus(taskId)
|
pollTaskStatus(taskId)
|
||||||
|
if (pollTimer) clearInterval(pollTimer)
|
||||||
pollTimer = setInterval(() => pollTaskStatus(taskId), 2000)
|
pollTimer = setInterval(() => pollTaskStatus(taskId), 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue