安全加固 + 结构清理:修注入/串写/竞态,DOMPurify 上线,后端补事务与 handler 测试

后端:
- ORDER BY 白名单(sanitizeOrder)堵住 ?order= SQL 注入,补回归测试
- 登录限速(每 IP 10 次失败/10 分钟 429)、TLS/反代下 Secure cookie、NewAPI 构造器
- Delete/setTags/MergeTags/DeleteTag 包事务;Archive 去 500 篇上限
- 列表接口裁剪:不传 content_md,长文 content_html 截 600,新增 content_len;health 探 DB

前端:
- EditorView 路由复用串写修复(RouterView :key + sync watch 回写原文章)
- v-html 出口统一过 DOMPurify(sanitizeHtml),stripTags 改 DOMParser
- 列表竞态防护(Home/Tag/Posts 请求序号)、TagView 分页修复
- 侧栏接口 30s 缓存去重;one:unauthorized 监听器泄漏修复
- 删 styles.css 498 行重复块;移除 tailwind/marked/vue-tsc 死依赖;CommandPalette a11y 语义
This commit is contained in:
Sakurasan
2026-09-21 23:59:09 +08:00
parent c762f06cd7
commit dd2994189a
25 changed files with 539 additions and 1411 deletions
+6 -12
View File
@@ -1,16 +1,11 @@
import { marked } from 'marked'
import DOMPurify from 'dompurify'
marked.setOptions({ breaks: true, gfm: true })
// 编辑器预览与前台渲染共用这一个入口:先 marked 渲染,再过 DOMPurify
export function renderMarkdown(text) {
return DOMPurify.sanitize(marked.parse(text || '', { async: false }))
// 后端 goldmark 已转义原始 HTML,这里再过一道 DOMPurify 作纵深防御,
// 所有 v-html 出口必须经过它。
export function sanitizeHtml(html) {
return DOMPurify.sanitize(html || '')
}
// 编辑器按 `md.render(...)` 的用法调用
export const md = { render: renderMarkdown }
// 用 Intl.DateTimeFormat — locale 感知,未来要 i18n 只换 locale 即可
const longDateFmt = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
@@ -50,9 +45,8 @@ export function relativeDate(iso) {
}
export function stripTags(html) {
const div = document.createElement('div')
div.innerHTML = html || ''
return div.textContent || ''
// 不用 div.innerHTML:那会真正解析并触发 <img onerror> 之类的事件
return new DOMParser().parseFromString(html || '', 'text/html').body.textContent || ''
}
// 短文在列表里没有标题,用正文首句当索引