diff --git a/backend/cmd/webui/handlers/books.go b/backend/cmd/webui/handlers/books.go index 04531dc..b23c237 100644 --- a/backend/cmd/webui/handlers/books.go +++ b/backend/cmd/webui/handlers/books.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" "strconv" - "strings" "time" "github.com/gin-gonic/gin" @@ -38,20 +37,16 @@ func (h *H) bookFromParam(c *gin.Context) (store.Book, bool) { return h.getBookRow(c, id) } -// absBookPath: books.path 永远相对且不含 ..;拼接后二次前缀校验(纵深防御) +// absBookPath: books.path 永远相对且不含 ..;拼接后二次前缀校验(纵深防御)。 +// bookfile.Contains 带 EvalSymlinks,比裸 filepath.Rel 更能拦住软链逃逸。 func absBookPath(root string, b store.Book) (string, error) { abs := filepath.Join(root, filepath.FromSlash(b.Path)) - if filepath.Clean(abs) != abs || !hasPrefixDir(abs, root) { + if filepath.Clean(abs) != abs || !bookfile.Contains(root, abs) { return "", os.ErrPermission } return abs, nil } -func hasPrefixDir(p, dir string) bool { - rel, err := filepath.Rel(filepath.Clean(dir), filepath.Clean(p)) - return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator)) -} - func bookJSON(b store.Book, percent float64, libraryName string) gin.H { h := bookfile.Hash(b.FileSize, b.ModTS) j := gin.H{ diff --git a/backend/internal/seed/seed.go b/backend/internal/seed/seed.go index 168f2b0..1cd4418 100644 --- a/backend/internal/seed/seed.go +++ b/backend/internal/seed/seed.go @@ -2,11 +2,8 @@ package seed import ( "context" - "errors" "log" - "github.com/jackc/pgx/v5/pgconn" - "booklib/internal/auth" "booklib/internal/store" ) @@ -28,8 +25,7 @@ func Admin(ctx context.Context, s *store.Store, user, pass string) error { return err } if _, err := s.CreateUser(ctx, user, h, "admin"); err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) && pgErr.Code == "23505" { + if store.IsUniqueViolation(err) { log.Printf("seed admin %q may already exist: %v", user, err) return nil }