Systematic pass over every feature and page trying to break it through normal use: create/view/delete flows, expiry edge cases (1h, past dates, never), burn-after-read double reads, custom slug validation edges (unicode, very long, reserved, collisions), password flows, pagination boundaries (empty pages, offset beyond total), filter states, back/forward navigation, concurrent requests. File findings as separate issues; fix in this sweep.
Systematic pass over every feature and page trying to break it through normal use: create/view/delete flows, expiry edge cases (1h, past dates, never), burn-after-read double reads, custom slug validation edges (unicode, very long, reserved, collisions), password flows, pagination boundaries (empty pages, offset beyond total), filter states, back/forward navigation, concurrent requests. File findings as separate issues; fix in this sweep.
Bug-fixing sweep complete. Full systematic pass over create/view/burn/expiry/history/saved/navigation/concurrency flows against the live server, plus code review of main.go/web.go/burn.go/cans.go/customslug.go.
view_count never incremented on HTML paste views — handlePasteView (web.go) was missing IncrementViews; only /raw counted views, so the counter stayed at 0 no matter how many times a paste was opened. Fixed + regression test (TestPasteViewIncrementsViewCount). The just-created banner render (?created=1) correctly does NOT count (TestJustCreatedViewDoesNotCount).
API accepted invalid expiry durations — the UI enforces 1min–1yr (#48) but the API accepted -1h, 0s, 1ns (born-expired pastes) and 30000h (~3.4 years). Fixed with validExpiry bounds in CreatePaste + table test (TestCreatePasteExpiryBounds). Verified live: 400 for all invalid, 201 for 1m/90s/8760h.
History/saved search filter ignored custom_slug — table.js matches() only checked title+id, yet the URL column shows the slug. Slug terms now match.
Verified working (no fix needed)
Custom slugs: reserved words rejected (incl. case-insensitive API), leading dash/unicode/space/65-char rejected, 64-char ok, collision 400; live concurrency test: 8 parallel same-slug creates → exactly one 201, rest rejected, slug serves winner.
Burn-after-N: budget correct for cookie viewers (dedupe window), distinct viewers each consume one read, no-cookie clients count as their own viewer and burn correctly; burned paste → 404 on view/raw/API. Password+burn works via unlock form and API password. Expiry+burn both displayed.
Expired pastes 404 on view/raw/API. Unlisted: not in /api/public, viewable, in creator's /api/mine. Soft-deleted paste disappears from /api/mine immediately; offset beyond total → empty list not error; limit=0/-5/10⁶/abc all clamp safely.
XSS-safety of titles through create→history→view pipeline (quotes, angle brackets, emoji) — all properly escaped; title round-trips through API unchanged.
Highlighted vs unknown languages, blank lines, trailing newline, 5000-char single line, CRLF content, emoji — gutter counts and rendering correct. 5MB paste: create 1.1s, raw 0.4s, HTML view instant.
Unlock flow: wrong password → rate-limited after 5 tries (429 includes correct-password attempts, acceptable), valid unlock sets per-paste HMAC cookie, subsequent GETs unlocked. Direct /settings & /mine without cookie render fine.
Quirks noted (not fixed, by design or low value)
/raw on a password-protected paste returns 401 with no unlock path (raw is meant for password-less pastes).
Burned/expired paste IDs are never reused; deletion token hard-delete works and also frees nothing (ids unique random).
GET /{id}/ with trailing slash → 404 (chi default).
Duplicate parallel DELETEs of the same paste all return 200 (idempotent soft-delete) — harmless.
Test pastes cleaned up (72 removed, DB back to pre-sweep seeded state). go test ./... passes; service rebuilt, restarted and verified live.
Bug-fixing sweep complete. Full systematic pass over create/view/burn/expiry/history/saved/navigation/concurrency flows against the live server, plus code review of main.go/web.go/burn.go/cans.go/customslug.go.
## Bugs found & fixed (03600b2)
1. **view_count never incremented on HTML paste views** — handlePasteView (web.go) was missing IncrementViews; only /raw counted views, so the counter stayed at 0 no matter how many times a paste was opened. Fixed + regression test (TestPasteViewIncrementsViewCount). The just-created banner render (?created=1) correctly does NOT count (TestJustCreatedViewDoesNotCount).
2. **API accepted invalid expiry durations** — the UI enforces 1min–1yr (#48) but the API accepted `-1h`, `0s`, `1ns` (born-expired pastes) and `30000h` (~3.4 years). Fixed with validExpiry bounds in CreatePaste + table test (TestCreatePasteExpiryBounds). Verified live: 400 for all invalid, 201 for 1m/90s/8760h.
3. **History/saved search filter ignored custom_slug** — table.js `matches()` only checked title+id, yet the URL column shows the slug. Slug terms now match.
## Verified working (no fix needed)
- Custom slugs: reserved words rejected (incl. case-insensitive `API`), leading dash/unicode/space/65-char rejected, 64-char ok, collision 400; live concurrency test: 8 parallel same-slug creates → exactly one 201, rest rejected, slug serves winner.
- Burn-after-N: budget correct for cookie viewers (dedupe window), distinct viewers each consume one read, no-cookie clients count as their own viewer and burn correctly; burned paste → 404 on view/raw/API. Password+burn works via unlock form and API password. Expiry+burn both displayed.
- Expired pastes 404 on view/raw/API. Unlisted: not in /api/public, viewable, in creator's /api/mine. Soft-deleted paste disappears from /api/mine immediately; offset beyond total → empty list not error; limit=0/-5/10⁶/abc all clamp safely.
- XSS-safety of titles through create→history→view pipeline (quotes, angle brackets, emoji) — all properly escaped; title round-trips through API unchanged.
- Highlighted vs unknown languages, blank lines, trailing newline, 5000-char single line, CRLF content, emoji — gutter counts and rendering correct. 5MB paste: create 1.1s, raw 0.4s, HTML view instant.
- Unlock flow: wrong password → rate-limited after 5 tries (429 includes correct-password attempts, acceptable), valid unlock sets per-paste HMAC cookie, subsequent GETs unlocked. Direct /settings & /mine without cookie render fine.
## Quirks noted (not fixed, by design or low value)
- /raw on a password-protected paste returns 401 with no unlock path (raw is meant for password-less pastes).
- Burned/expired paste IDs are never reused; deletion token hard-delete works and also frees nothing (ids unique random).
- `GET /{id}/` with trailing slash → 404 (chi default).
- Duplicate parallel DELETEs of the same paste all return 200 (idempotent soft-delete) — harmless.
Test pastes cleaned up (72 removed, DB back to pre-sweep seeded state). `go test ./...` passes; service rebuilt, restarted and verified live.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Systematic pass over every feature and page trying to break it through normal use: create/view/delete flows, expiry edge cases (1h, past dates, never), burn-after-read double reads, custom slug validation edges (unicode, very long, reserved, collisions), password flows, pagination boundaries (empty pages, offset beyond total), filter states, back/forward navigation, concurrent requests. File findings as separate issues; fix in this sweep.
poslop referenced this issue2026-09-09 01:56:37 +00:00
Bug-fixing sweep complete. Full systematic pass over create/view/burn/expiry/history/saved/navigation/concurrency flows against the live server, plus code review of main.go/web.go/burn.go/cans.go/customslug.go.
Bugs found & fixed (
03600b2)-1h,0s,1ns(born-expired pastes) and30000h(~3.4 years). Fixed with validExpiry bounds in CreatePaste + table test (TestCreatePasteExpiryBounds). Verified live: 400 for all invalid, 201 for 1m/90s/8760h.matches()only checked title+id, yet the URL column shows the slug. Slug terms now match.Verified working (no fix needed)
API), leading dash/unicode/space/65-char rejected, 64-char ok, collision 400; live concurrency test: 8 parallel same-slug creates → exactly one 201, rest rejected, slug serves winner.Quirks noted (not fixed, by design or low value)
GET /{id}/with trailing slash → 404 (chi default).Test pastes cleaned up (72 removed, DB back to pre-sweep seeded state).
go test ./...passes; service rebuilt, restarted and verified live.