安全加固 + 结构清理:修注入/串写/竞态,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
+12 -1
View File
@@ -87,6 +87,10 @@ function onKey(e) {
e.preventDefault()
const it = flat.value[idx.value]
if (it) run(it.item)
} else if (e.key === 'Tab') {
// 面板内只有一个输入框,Tab 不外逃
e.preventDefault()
inputEl.value?.focus()
}
}
@@ -107,9 +111,13 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
v-model="query"
placeholder="输入命令、文章标题…"
aria-label="搜索命令和文章"
role="combobox"
aria-expanded="true"
aria-controls="cmd-listbox"
:aria-activedescendant="flat.length ? 'cmd-opt-' + idx : undefined"
@input="idx = 0"
/>
<div class="cmd-list">
<div class="cmd-list" id="cmd-listbox" role="listbox">
<template v-for="(sec, si) in sections" :key="si">
<div style="padding: 8px 18px 4px; font-size: 11px; color: var(--admin-muted); letter-spacing: 0.08em; text-transform: uppercase;">
{{ sec.name }}
@@ -119,6 +127,9 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
:key="it.id"
class="row"
:class="{ on: idx === it._idx }"
role="option"
:id="'cmd-opt-' + it._idx"
:aria-selected="idx === it._idx"
@click="run(it)"
@mouseenter="idx = it._idx"
>