Files
2026-07-13 13:12:33 +08:00

74 lines
2.4 KiB
JavaScript

import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = fileURLToPath(new URL('..', import.meta.url))
function read(rel) {
return readFileSync(join(root, rel), 'utf8')
}
const failures = []
function assertAbsent(rel, pattern, message) {
const body = read(rel)
if (pattern.test(body)) failures.push(`${rel}: ${message}`)
}
function assertPresent(rel, pattern, message) {
const body = read(rel)
if (!pattern.test(body)) failures.push(`${rel}: ${message}`)
}
assertAbsent(
'src/utils/chat/artifacts.ts',
/\btoken\??:\s*string|searchParams\.set\(['"]token['"]|includeSessionKey\s*!==\s*false/,
'artifact URLs must not carry bearer tokens or default session keys in query params.',
)
assertPresent(
'src/utils/chat/artifacts.ts',
/searchParams\.delete\(['"]token['"]\)[\s\S]+searchParams\.delete\(['"]sessionKey['"]\)[\s\S]+searchParams\.delete\(['"]session_key['"]\)/,
'artifact URL sanitizer must strip sensitive same-origin query params.',
)
assertAbsent(
'src/composables/chat/useChatMarkdownExport.ts',
/\bsessionKey\b|\bauthToken\b|\btoken\b|artifactDownloadUrl/,
'Markdown export must not persist raw sessions, bearer tokens, or signed artifact URLs.',
)
assertAbsent(
'src/components/chat/ChatArtifactList.vue',
/artifactPreviewUrl\(|:href="artifactUrl\(|:src="artifactUrl\(/,
'artifact previews must not render credential-bearing artifact URLs directly into the DOM.',
)
assertPresent(
'src/components/chat/ChatArtifactList.vue',
/URL\.createObjectURL\(blob\)/,
'artifact previews must render fetched blob object URLs.',
)
// Assistant markdown is sanitized before it reaches the DOM: the renderer must
// not bypass DOMPurify, and must never let assistant text render arbitrary form
// controls. The only <input> markdown produces is a disabled task-list checkbox.
assertAbsent(
'src/composables/chat/useChatTextRendering.ts',
/forceKeepAttr/,
'markdown sanitization must not bypass DOMPurify via forceKeepAttr.',
)
assertPresent(
'src/composables/chat/useChatTextRendering.ts',
/addHook\(\s*['"]uponSanitizeElement['"][\s\S]*?removeChild/,
'markdown sanitizer must drop non-task-list <input> elements (uponSanitizeElement + removeChild).',
)
if (failures.length > 0) {
console.error(failures.join('\n'))
process.exit(1)
}
console.log('Chat security guard passed.')