api/custom_fields.php, clone_ticket.php, delete_comment.php, delete_attachment.php, bulk_operation.php, generate_api_key.php, manage_templates.php, manage_recurring.php, revoke_api_key.php, manage_workflows.php, ticket_dependencies.php, and upload_attachment.php all duplicate CsrfMiddleware::validateToken() inline instead of routing through api/bootstrap.php, and their 403 rejection body is just {"success":false,"error":"Invalid CSRF token"} — no csrf_token field.
Compare to api/bootstrap.php (~lines 40-47) and its direct users (add_comment.php, update_ticket.php), which correctly echo CsrfMiddleware::getToken() on rejection specifically so lt.api's resync (assets/js/base.js:475) can recover.
Impact: Once window.CSRF_TOKEN goes stale for any reason (token expiry after ~1h idle — see related CSRF-retry issue for the exact numbers, or a write in another tab rotating the shared session-scoped token), the next write against any of these 12 endpoints returns a 403 with no recovery token in the body. The client has no way to self-heal at that point — every subsequent write to any endpoint (even ones that would normally resync fine) keeps failing until the user manually reloads the page. This is strictly broader and more severe than the already-filed #57 (notification bell's raw fetch(), one specific endpoint) — the resync mechanism is effectively only implemented on a minority of the app's write surface.
Fix: Either route all 12 of these endpoints through bootstrap.php's centralized CSRF handling (preferred — also fixes error-shape/display_errors inconsistencies other audit rounds already flagged), or at minimum add 'csrf_token' => CsrfMiddleware::getToken() to each of these 12 rejection bodies.
**Severity:** High
`api/custom_fields.php`, `clone_ticket.php`, `delete_comment.php`, `delete_attachment.php`, `bulk_operation.php`, `generate_api_key.php`, `manage_templates.php`, `manage_recurring.php`, `revoke_api_key.php`, `manage_workflows.php`, `ticket_dependencies.php`, and `upload_attachment.php` all duplicate `CsrfMiddleware::validateToken()` inline instead of routing through `api/bootstrap.php`, and their 403 rejection body is just `{"success":false,"error":"Invalid CSRF token"}` — no `csrf_token` field.
Compare to `api/bootstrap.php` (~lines 40-47) and its direct users (`add_comment.php`, `update_ticket.php`), which correctly echo `CsrfMiddleware::getToken()` on rejection specifically so `lt.api`'s resync (`assets/js/base.js:475`) can recover.
**Impact:** Once `window.CSRF_TOKEN` goes stale for any reason (token expiry after ~1h idle — see related CSRF-retry issue for the exact numbers, or a write in another tab rotating the shared session-scoped token), the *next* write against any of these 12 endpoints returns a 403 with no recovery token in the body. The client has no way to self-heal at that point — every subsequent write to *any* endpoint (even ones that would normally resync fine) keeps failing until the user manually reloads the page. This is strictly broader and more severe than the already-filed #57 (notification bell's raw `fetch()`, one specific endpoint) — the resync mechanism is effectively only implemented on a minority of the app's write surface.
**Fix:** Either route all 12 of these endpoints through `bootstrap.php`'s centralized CSRF handling (preferred — also fixes error-shape/display_errors inconsistencies other audit rounds already flagged), or at minimum add `'csrf_token' => CsrfMiddleware::getToken()` to each of these 12 rejection bodies.
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: High
api/custom_fields.php,clone_ticket.php,delete_comment.php,delete_attachment.php,bulk_operation.php,generate_api_key.php,manage_templates.php,manage_recurring.php,revoke_api_key.php,manage_workflows.php,ticket_dependencies.php, andupload_attachment.phpall duplicateCsrfMiddleware::validateToken()inline instead of routing throughapi/bootstrap.php, and their 403 rejection body is just{"success":false,"error":"Invalid CSRF token"}— nocsrf_tokenfield.Compare to
api/bootstrap.php(~lines 40-47) and its direct users (add_comment.php,update_ticket.php), which correctly echoCsrfMiddleware::getToken()on rejection specifically solt.api's resync (assets/js/base.js:475) can recover.Impact: Once
window.CSRF_TOKENgoes stale for any reason (token expiry after ~1h idle — see related CSRF-retry issue for the exact numbers, or a write in another tab rotating the shared session-scoped token), the next write against any of these 12 endpoints returns a 403 with no recovery token in the body. The client has no way to self-heal at that point — every subsequent write to any endpoint (even ones that would normally resync fine) keeps failing until the user manually reloads the page. This is strictly broader and more severe than the already-filed #57 (notification bell's rawfetch(), one specific endpoint) — the resync mechanism is effectively only implemented on a minority of the app's write surface.Fix: Either route all 12 of these endpoints through
bootstrap.php's centralized CSRF handling (preferred — also fixes error-shape/display_errors inconsistencies other audit rounds already flagged), or at minimum add'csrf_token' => CsrfMiddleware::getToken()to each of these 12 rejection bodies.