Skip to main content

max / makenotwork

ux: clamp library feed pagination like sibling paginators library_tab_feed used unwrap_or(1).max(1) with no upper bound, while feed/discover/admin/issues all clamp(1, 1_000_000_000). A large ?page= issued an unbounded SQL OFFSET deep-scan. Clamp it to match, and add a regression test. ultra-fuzz Run 5 M-UX1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-24 01:48 UTC
Commit: f562a1f1a965b50cd0159cd455911ea1528ea99e
Parent: dfde7a9
2 files changed, +24 insertions, -2 deletions
@@ -225,8 +225,10 @@ pub(super) async fn library_tab_feed(
225 225 ) -> Result<impl IntoResponse> {
226 226 use crate::templates::LibraryFeedTabTemplate;
227 227
228 - let page = query.page.unwrap_or(1).max(1);
229 - // Widen to i64 BEFORE multiplying to avoid u32 overflow on a large `?page=`.
228 + // Clamp the upper bound like every sibling paginator (feed/discover/
229 + // admin/issues): an unbounded `?page=` would issue a giant SQL OFFSET
230 + // deep-scan. Widen to i64 BEFORE multiplying to avoid u32 overflow.
231 + let page = query.page.unwrap_or(1).clamp(1, 1_000_000_000);
230 232 let offset = (page as i64 - 1) * constants::FEED_PAGE_SIZE as i64;
231 233
232 234 let total_items = db::follows::count_followed_feed_items(&state.db, user.id).await? as u32;
@@ -195,6 +195,26 @@ async fn own_profile_hides_follow_button() {
195 195 }
196 196
197 197 #[tokio::test]
198 + async fn library_feed_huge_page_is_clamped_not_overflowing() {
199 + // M-UX1: the feed tab's `?page=` must be clamped like every sibling
200 + // paginator. A large in-range value (above the 1e9 clamp ceiling) should
201 + // return a valid empty page, with the OFFSET bounded by the clamp rather
202 + // than running a multi-billion-row deep-scan.
203 + let mut h = TestHarness::new().await;
204 + h.signup("feedpage", "feedpage@test.com", "password123").await;
205 +
206 + let resp = h
207 + .client
208 + .get("/library/tabs/feed?page=4000000000")
209 + .await;
210 + assert_eq!(
211 + resp.status, 200,
212 + "large page should clamp to a valid empty page, got {} {}",
213 + resp.status, resp.text
214 + );
215 + }
216 +
217 + #[tokio::test]
198 218 async fn follow_nonexistent_rejected() {
199 219 let mut h = TestHarness::new().await;
200 220 let _user_id = h