安全加固 + 结构清理:修注入/串写/竞态,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
+33 -6
View File
@@ -43,11 +43,11 @@ func TestPostInputDefaults(t *testing.T) {
func TestNormalizeStatus(t *testing.T) {
cases := map[string]string{
"": model.StatusDraft,
"draft": model.StatusDraft,
"published": model.StatusPublished,
"": model.StatusDraft,
"draft": model.StatusDraft,
"published": model.StatusPublished,
" Published ": model.StatusPublished,
"pending": model.StatusDraft, // unknown → draft
"pending": model.StatusDraft, // unknown → draft
}
for in, want := range cases {
if got := NormalizeStatus(in); got != want {
@@ -150,9 +150,9 @@ func TestStoreSQLite(t *testing.T) {
// the value is preserved across updates.
upd, err := s.Update(p1.ID, model.PostInput{
Kind: model.KindLong, Title: "第一篇",
CoverURL: p1.CoverURL,
CoverURL: p1.CoverURL,
ContentMd: "# hi\n这是新版本。",
Tags: []string{"Go"},
Tags: []string{"Go"},
})
if err != nil {
t.Fatalf("update: %v", err)
@@ -264,3 +264,30 @@ func openTestStore(t *testing.T) *Store {
}
return s
}
func TestSanitizeOrder(t *testing.T) {
if got := sanitizeOrder("published_at ASC"); got != "published_at asc" {
t.Errorf("whitelisted order rejected: %q", got)
}
for _, bad := range []string{
"published_at DESC; DROP TABLE posts; --",
"(SELECT 1) DESC",
"1 DESC",
"published_at DESC, (SELECT COUNT(*) FROM sqlite_master) ASC",
} {
if got := sanitizeOrder(bad); got != "published_at desc" {
t.Errorf("sanitizeOrder(%q) = %q, want fallback", bad, got)
}
}
}
func TestListWithMaliciousOrderByFailsSafe(t *testing.T) {
s := openTestStore(t)
page, err := s.List(ListOptions{Status: "any", OrderBy: "published_at DESC; DROP TABLE posts; --"})
if err != nil {
t.Fatalf("List: %v", err)
}
if page.Total != 0 {
t.Errorf("unexpected total %d", page.Total)
}
}