fix(backend): single error body on pages paths, reject newline entry names, empty-index cache skip

This commit is contained in:
2026-09-05 20:56:05 +08:00
parent 0946f6be5a
commit 8206c7a4ed
3 changed files with 13 additions and 5 deletions
+10 -2
View File
@@ -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
}
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
}
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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)