安全加固 + 结构清理:修注入/串写/竞态,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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user