fix(backend): single error body on pages paths, reject newline entry names, empty-index cache skip
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user