From c47397ac1e2ceafafe2be3cdec86366dd396ed6f Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 18 Sep 2026 13:49:53 +0300 Subject: Security: Clamp page numbers so a huge ?p= cannot overflow the offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ?p=9223372036854775807 made (n-1)*per wrap negative, Postgres refused the OFFSET and every listing, tag and search page answered 500 — a line in the log per request for any bot fuzzing query strings. Page numbers now stop at 100000. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- internal/web/handlers_blog.go | 10 ++++++---- internal/web/web_test.go | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/web/handlers_blog.go b/internal/web/handlers_blog.go index d9caf22..f138c58 100644 --- a/internal/web/handlers_blog.go +++ b/internal/web/handlers_blog.go @@ -67,13 +67,15 @@ func (s *Server) blogView(r *http.Request) (map[string]any, error) { // (typed, so the layout's index lookups still work). var noNotices = map[string][]Notice{} +// maxPageNum bounds ?p=: no listing is that long, and (n-1)*per must not +// overflow into a negative OFFSET, which Postgres refuses (a 500 for a bot to +// trigger at will). +const maxPageNum = 100000 + // pageNum reads the ?p= of a paginated listing (1 when absent or silly). func pageNum(r *http.Request) int { n, _ := strconv.Atoi(r.URL.Query().Get("p")) - if n < 1 { - n = 1 - } - return n + return min(max(n, 1), maxPageNum) } func (s *Server) handleBlogHome(w http.ResponseWriter, r *http.Request) { diff --git a/internal/web/web_test.go b/internal/web/web_test.go index dfc9629..2f3bc4f 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -1080,3 +1080,11 @@ func TestSecurityHeaders(t *testing.T) { } } } + +func TestPageNum(t *testing.T) { + for in, want := range map[string]int{"": 1, "0": 1, "-3": 1, "x": 1, "7": 7, "9223372036854775807": maxPageNum} { + if got := pageNum(httptest.NewRequest("GET", "/?p="+in, nil)); got != want { + t.Errorf("p=%q: %d, want %d", in, got, want) + } + } +} -- cgit v1.2.3