From 0c5dfd435359ffdaf68326ee1da0be7e86409ff3 Mon Sep 17 00:00:00 2001 From: XingfenD Date: Mon, 14 Sep 2026 19:42:52 +0800 Subject: [PATCH] fix(library): reject reserved names (cache, .uploads) with 400 reserved_name (B8) --- backend/cmd/webui/handlers/libraries.go | 6 ++++++ backend/internal/media/reserved.go | 16 ++++++++++++++++ backend/internal/media/reserved_test.go | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 backend/internal/media/reserved.go create mode 100644 backend/internal/media/reserved_test.go diff --git a/backend/cmd/webui/handlers/libraries.go b/backend/cmd/webui/handlers/libraries.go index 586be1a..2543444 100644 --- a/backend/cmd/webui/handlers/libraries.go +++ b/backend/cmd/webui/handlers/libraries.go @@ -15,6 +15,7 @@ import ( "github.com/jackc/pgx/v5" "booklib/internal/bookfile" + "booklib/internal/media" "booklib/internal/store" ) @@ -61,6 +62,11 @@ func (h *H) CreateLibrary(c *gin.Context) { err(c, http.StatusBadRequest, "bad_request", "bad name") return } + // B8: reject reserved names that conflict with system directories. + if media.IsReservedName(safe) { + err(c, http.StatusBadRequest, "bad_request", "reserved_name") + return + } root := filepath.Join(filepath.Clean(h.cfg.BooksDir), safe) id, e := h.st.CreateLibrary(c, req.Name, root) if e != nil { diff --git a/backend/internal/media/reserved.go b/backend/internal/media/reserved.go new file mode 100644 index 0000000..e8290ee --- /dev/null +++ b/backend/internal/media/reserved.go @@ -0,0 +1,16 @@ +package media + +import "strings" + +// reservedNames are library names that conflict with system directories. +// This is the single source of truth for reserved name validation (B8). +var reservedNames = map[string]bool{ + "cache": true, // CACHE_DIR + ".uploads": true, // upload session directory + ".trash": true, // potential future use +} + +// IsReservedName reports whether name conflicts with system directories. +func IsReservedName(name string) bool { + return reservedNames[strings.ToLower(name)] +} diff --git a/backend/internal/media/reserved_test.go b/backend/internal/media/reserved_test.go new file mode 100644 index 0000000..f26c1fd --- /dev/null +++ b/backend/internal/media/reserved_test.go @@ -0,0 +1,16 @@ +package media + +import "testing" + +func TestIsReservedName(t *testing.T) { + for _, n := range []string{"cache", "Cache", "CACHE", ".uploads", ".Uploads", ".trash"} { + if !IsReservedName(n) { + t.Errorf("IsReservedName(%q) = false, want true", n) + } + } + for _, n := range []string{"comics", "books", "my-library", "Cache1"} { + if IsReservedName(n) { + t.Errorf("IsReservedName(%q) = true, want false", n) + } + } +}