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)
|
a.immutable(c)
|
||||||
dir := bookfile.CoverDir(a.cfg.CacheDir, bookfile.DirKey(b.ID, bookfile.Hash(b.FileSize, b.ModTS)))
|
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()))
|
http.ServeFile(c.Writer, c.Request, filepath.Join(dir, entries[0].Name()))
|
||||||
return
|
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) {
|
func (a *api) pageIndex(c *gin.Context, b store.Book, root string) ([]string, error) {
|
||||||
hash := bookfile.Hash(b.FileSize, b.ModTS)
|
hash := bookfile.Hash(b.FileSize, b.ModTS)
|
||||||
key := fmt.Sprintf("pagesidx:%d:%s", b.ID, hash)
|
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
|
return strings.Split(v, "\n"), nil
|
||||||
}
|
}
|
||||||
f, size, ok := a.openBook(c, b, root)
|
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 {
|
if e != nil {
|
||||||
return nil, e
|
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
|
return idx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +118,9 @@ func (a *api) pagesCount(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
idx, e := a.pageIndex(c, b, root)
|
idx, e := a.pageIndex(c, b, root)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
|
if c.Writer.Written() {
|
||||||
|
return // openBook 已写 403/404,不再叠加 422
|
||||||
|
}
|
||||||
err(c, http.StatusUnprocessableEntity, "broken", e.Error())
|
err(c, http.StatusUnprocessableEntity, "broken", e.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -142,6 +147,9 @@ func (a *api) page(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
idx, e := a.pageIndex(c, b, root)
|
idx, e := a.pageIndex(c, b, root)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
|
if c.Writer.Written() {
|
||||||
|
return // openBook 已写 403/404,不再叠加 422
|
||||||
|
}
|
||||||
err(c, http.StatusUnprocessableEntity, "broken", e.Error())
|
err(c, http.StatusUnprocessableEntity, "broken", e.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ var ErrNotZip = errors.New("not a readable zip")
|
|||||||
var ErrUnsafeZip = errors.New("unsafe zip entry")
|
var ErrUnsafeZip = errors.New("unsafe zip entry")
|
||||||
|
|
||||||
func unsafeEntry(n string) bool {
|
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 {
|
func isImage(name string) bool {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func TestPageIndexSortAndFilter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPageIndexRejectsSlip(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)
|
r := zipOf(t, bad)
|
||||||
if _, err := PageIndex(r, int64(r.Len())); err == nil {
|
if _, err := PageIndex(r, int64(r.Len())); err == nil {
|
||||||
t.Fatalf("entry %q must be rejected", bad)
|
t.Fatalf("entry %q must be rejected", bad)
|
||||||
|
|||||||
Reference in New Issue
Block a user