diff --git a/backend/internal/api/content.go b/backend/internal/api/content.go index 332212f..057397e 100644 --- a/backend/internal/api/content.go +++ b/backend/internal/api/content.go @@ -36,7 +36,7 @@ func (a *api) serveCover(c *gin.Context) { } a.immutable(c) dir := bookfile.CoverDir(a.cfg.CacheDir, bookfile.DirKey(b.ID, bookfile.Hash(b.FileSize, b.ModTS))) - if entries, err := os.ReadDir(dir); err == nil && len(entries) > 0 { + if entries, e := os.ReadDir(dir); e == nil && len(entries) > 0 { http.ServeFile(c.Writer, c.Request, filepath.Join(dir, entries[0].Name())) return } @@ -85,7 +85,7 @@ func (a *api) openBook(c *gin.Context, b store.Book, root string) (*os.File, int func (a *api) pageIndex(c *gin.Context, b store.Book, root string) ([]string, error) { hash := bookfile.Hash(b.FileSize, b.ModTS) key := fmt.Sprintf("pagesidx:%d:%s", b.ID, hash) - if v, ok := a.rdb.Get(c, key); ok { + if v, ok := a.rdb.Get(c, key); ok && v != "" { return strings.Split(v, "\n"), nil } f, size, ok := a.openBook(c, b, root) @@ -97,7 +97,9 @@ func (a *api) pageIndex(c *gin.Context, b store.Book, root string) ([]string, er if e != nil { return nil, e } - a.rdb.Set(c, key, strings.Join(idx, "\n"), 7*24*time.Hour) + if len(idx) > 0 { // 空索引不缓存,否则 warm 命中 "" 会 Split 出幽灵页 + a.rdb.Set(c, key, strings.Join(idx, "\n"), 7*24*time.Hour) + } return idx, nil } @@ -116,6 +118,9 @@ func (a *api) pagesCount(c *gin.Context) { } idx, e := a.pageIndex(c, b, root) if e != nil { + if c.Writer.Written() { + return // openBook 已写 403/404,不再叠加 422 + } err(c, http.StatusUnprocessableEntity, "broken", e.Error()) return } @@ -142,6 +147,9 @@ func (a *api) page(c *gin.Context) { } idx, e := a.pageIndex(c, b, root) if e != nil { + if c.Writer.Written() { + return // openBook 已写 403/404,不再叠加 422 + } err(c, http.StatusUnprocessableEntity, "broken", e.Error()) return } diff --git a/backend/internal/bookfile/zip.go b/backend/internal/bookfile/zip.go index e2b71ff..7514104 100644 --- a/backend/internal/bookfile/zip.go +++ b/backend/internal/bookfile/zip.go @@ -15,7 +15,7 @@ var ErrNotZip = errors.New("not a readable zip") var ErrUnsafeZip = errors.New("unsafe zip entry") func unsafeEntry(n string) bool { - return strings.HasPrefix(n, "/") || strings.Contains(n, "..") || strings.ContainsRune(n, '\\') + return strings.HasPrefix(n, "/") || strings.Contains(n, "..") || strings.ContainsRune(n, '\\') || strings.ContainsAny(n, "\n\r") } func isImage(name string) bool { diff --git a/backend/internal/bookfile/zip_test.go b/backend/internal/bookfile/zip_test.go index 5117c93..22c4b16 100644 --- a/backend/internal/bookfile/zip_test.go +++ b/backend/internal/bookfile/zip_test.go @@ -39,7 +39,7 @@ func TestPageIndexSortAndFilter(t *testing.T) { } func TestPageIndexRejectsSlip(t *testing.T) { - for _, bad := range []string{"../evil.jpg", "/etc/passwd.jpg", "a\\..\\b.jpg"} { + for _, bad := range []string{"../evil.jpg", "/etc/passwd.jpg", "a\\..\\b.jpg", "pag\ne.jpg", "pag\re.jpg"} { r := zipOf(t, bad) if _, err := PageIndex(r, int64(r.Len())); err == nil { t.Fatalf("entry %q must be rejected", bad)