views/layout_footer.php (~lines 237-258) — both loadNotifications() and the "mark all read" handler call fetch() directly instead of lt.api.*, violating the project's own documented convention (README Dev Notes #20: "No raw fetch() ... The wrapper auto-adds ... X-CSRF-Token ... auto-parses JSON").
Concretely:api/bootstrap.php rotates the CSRF token on every successful write and returns the new token in the response's csrf_token field; lt.api.* reads that field and updates window.CSRF_TOKEN (base.js ~lines 478/2728) — but this raw-fetch mark-read POST never does.
Impact: After a user clicks "Mark all read," the server has rotated its token but the client's cached window.CSRF_TOKEN is now stale. The user's next write anywhere else in the app (comment, status change, etc., via lt.api) sends the stale token, gets a 403 "Invalid CSRF token" on the first attempt, and only resyncs from that rejection — a reproducible one-off failure on every session that clicks "mark all read" then immediately tries another action.
Fix: Replace both fetch() calls with lt.api.get/lt.api.post so the CSRF token stays in sync like every other write in the app.
**Severity:** Medium
`views/layout_footer.php` (~lines 237-258) — both `loadNotifications()` and the "mark all read" handler call `fetch()` directly instead of `lt.api.*`, violating the project's own documented convention (README Dev Notes #20: "No raw fetch() ... The wrapper auto-adds ... X-CSRF-Token ... auto-parses JSON").
**Concretely:** `api/bootstrap.php` rotates the CSRF token on every successful write and returns the new token in the response's `csrf_token` field; `lt.api.*` reads that field and updates `window.CSRF_TOKEN` (base.js ~lines 478/2728) — but this raw-fetch mark-read POST never does.
**Impact:** After a user clicks "Mark all read," the server has rotated its token but the client's cached `window.CSRF_TOKEN` is now stale. The user's *next* write anywhere else in the app (comment, status change, etc., via `lt.api`) sends the stale token, gets a 403 "Invalid CSRF token" on the first attempt, and only resyncs from that rejection — a reproducible one-off failure on every session that clicks "mark all read" then immediately tries another action.
**Fix:** Replace both `fetch()` calls with `lt.api.get`/`lt.api.post` so the CSRF token stays in sync like every other write in the app.
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.
Severity: Medium
views/layout_footer.php(~lines 237-258) — bothloadNotifications()and the "mark all read" handler callfetch()directly instead oflt.api.*, violating the project's own documented convention (README Dev Notes #20: "No raw fetch() ... The wrapper auto-adds ... X-CSRF-Token ... auto-parses JSON").Concretely:
api/bootstrap.phprotates the CSRF token on every successful write and returns the new token in the response'scsrf_tokenfield;lt.api.*reads that field and updateswindow.CSRF_TOKEN(base.js ~lines 478/2728) — but this raw-fetch mark-read POST never does.Impact: After a user clicks "Mark all read," the server has rotated its token but the client's cached
window.CSRF_TOKENis now stale. The user's next write anywhere else in the app (comment, status change, etc., vialt.api) sends the stale token, gets a 403 "Invalid CSRF token" on the first attempt, and only resyncs from that rejection — a reproducible one-off failure on every session that clicks "mark all read" then immediately tries another action.Fix: Replace both
fetch()calls withlt.api.get/lt.api.postso the CSRF token stays in sync like every other write in the app.