Commit Graph
9 Commits
Author SHA1 Message Date
jaredandClaude Sonnet 5 9d8a73c355 Add recovery csrf_token to 12 hand-rolled CSRF rejection responses (#85)
Lint / PHP (phpcs PSR-12) (push) Successful in 38s
Lint / JS (eslint) (push) Successful in 15s
Lint / PHP requirements (version + extensions) (push) Successful in 38s
Lint / Notify on failure (push) Skipped
Security / PHP Security (semgrep) (push) Successful in 1m27s
Lint / Deploy (push) Successful in 2s
api/bootstrap.php's centralized CSRF handling echoes
CsrfMiddleware::getToken() on a 403 rejection specifically so
lt.api's client-side resync (assets/js/base.js) can recover once
window.CSRF_TOKEN goes stale (token expiry, or a write in another tab
rotating the shared session-scoped token). 12 endpoints duplicate
CsrfMiddleware::validateToken() inline instead of routing through
bootstrap.php, and their 403 body omitted csrf_token entirely —
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.

Once a client's token drifted out of sync, the next write to any of
these 12 endpoints returned a 403 with no way to self-heal — every
subsequent write to any endpoint kept failing until a manual reload,
since the resync mechanism was only wired up on a minority of the
app's write surface. Took the minimal fix the issue names as
sufficient (add 'csrf_token' => CsrfMiddleware::getToken() to each
rejection body) rather than restructuring all 12 through bootstrap.php,
to avoid behavioral risk from rewiring each endpoint's differing
auth/bootstrapping. generate_api_key.php and revoke_api_key.php threw
a generic Exception for this case (swallowed into a plain error-message
response with no room for extra fields), so those two now short-circuit
with a direct JSON response instead, matching the other 10.

Verified end-to-end against real running endpoints with a real
session and real MariaDB: sent a wrong CSRF token to one endpoint of
each response shape (plain json_encode, ResponseHelper::error, and the
formerly exception-based path) and confirmed all three now return the
current valid csrf_token in the 403 body.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv
2026-09-11 11:42:46 -04:00
jaredandClaude Opus 4.8 327c225ded Fix API security: dependency/visibility leaks, authz, CSRF, comment spoofing
- ticket_dependencies.php: pass current user id/groups/is_admin into the
  visibility-filtered DependencyModel methods; drop (int) casts that
  stripped leading zeros from varchar ticket_ids
- update_ticket.php: authorize visibility changes (admin or creator only);
  enforce requires_comment transitions server-side (400 + requires_comment
  flag so the client can prompt-and-retry); return proper 401/400/403
- add_comment.php: take commenter name from the session not the client
  (anti-spoofing); validate parent_comment_id belongs to the ticket;
  reject empty comments; pass ticket visibility to notifications so
  non-public comment bodies aren't leaked
- add_comment/update_comment/bulk_operation: validate CSRF for all
  state-changing methods, not just POST
- bootstrap.php: return the current CSRF token on rejection and never
  rotate it on a rejected request, so a desynced client can auto-recover
- correct auth->401 and validation->400 status codes across these endpoints

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:48:34 -04:00
jared c90bdc8ac8 style: auto-fix 1340 phpcs PSR-12 violations via phpcbf; exclude MissingNamespace and SideEffects
Lint / PHP (phpcs PSR-12) (push) Failing after 29s
Lint / JS (eslint) (push) Successful in 12s
2026-04-13 20:56:10 -04:00
jaredandClaude Sonnet 4.6 e6b6a2a88c Security/correctness: visibility filtering, Content-Type headers, group validation
- TicketModel::getAllTickets() now accepts optional $user param and applies
  getVisibilityFilter() so non-admin users cannot see internal/confidential
  tickets they lack access to from the dashboard listing
- DashboardController passes $GLOBALS['currentUser'] to getAllTickets()
- clone_ticket.php: move Content-Type header to top so all error paths send
  correct JSON content type
- AuthMiddleware: filter group names from HTTP header to [a-z0-9_-] only,
  preventing header injection via malformed group names
- add_comment.php: return HTTP 201 on success, 500 in catch block
- update_comment.php, delete_comment.php: return 500 in catch blocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 18:23:16 -04:00
jaredandClaude Sonnet 4.6 89a685a502 Integrate web_template design system and fix security/quality issues
Security fixes:
- Add HTTP method validation to delete_comment.php (block CSRF via GET)
- Remove $_GET fallback in comment deletion (was CSRF bypass vector)
- Guard session_start() with session_status() check across API files
- Escape json_encode() data attributes with htmlspecialchars in views
- Escape inline APP_TIMEZONE config values in DashboardView/TicketView
- Validate timezone param against DateTimeZone::listIdentifiers() in index.php
- Remove Database::escape() (was using real_escape_string, not safe)
- Fix AttachmentModel hardcoded connection; inject via constructor

Backend fixes:
- Fix CommentModel bind_param type for ticket_id (s→i)
- Fix buildCommentThread orphan parent guard
- Fix StatsModel JOIN→LEFT JOIN so unassigned tickets aren't excluded
- Add ticket ID validation in BulkOperationsModel before implode()
- Add duplicate key retry in TicketModel::createTicket() for race conditions
- Wrap SavedFiltersModel default filter changes in transactions
- Add null result guards in WorkflowModel query methods

Frontend JS:
- Rewrite toast.js as lt.toast shim (base.js dependency)
- Delegate escapeHtml() to lt.escHtml()
- Rewrite keyboard-shortcuts.js using lt.keys.on()
- Migrate settings.js to lt.api.* and lt.modal.open/close()
- Migrate advanced-search.js to lt.api.* and lt.modal.open/close()
- Migrate dashboard.js fetch calls to lt.api.*; update all dynamic
  modals (bulk ops, quick actions, confirm/input) to lt-modal structure
- Migrate ticket.js fetchMentionUsers to lt.api.get()
- Remove console.log/error/warn calls from JS files

Views:
- Add /web_template/base.css and base.js to all 10 view files
- Call lt.keys.initDefaults() in DashboardView, TicketView, admin views
- Migrate all modal HTML from settings-modal/settings-content to
  lt-modal-overlay/lt-modal/lt-modal-header/lt-modal-body/lt-modal-footer
- Replace style="display:none" with aria-hidden="true" on all modals
- Replace modal open/close style.display with lt.modal.open/close()
- Update modal buttons to lt-btn lt-btn-primary/lt-btn-ghost classes
- Remove manual ESC keydown handlers (replaced by lt.keys.initDefaults)
- Fix unescaped timezone values in TicketView inline script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 23:22:24 -04:00
jaredandClaude Opus 4.6 bcc163bc77 Audit fixes: security, dead code removal, API consolidation, JS dedup
Security:
- Fix IDOR in delete/update comment (add ticket visibility check)
- XSS defense-in-depth in DashboardView active filters
- Replace innerHTML with DOM construction in toast.js
- Remove redundant real_escape_string in check_duplicates
- Add rate limiting to get_template, download_attachment, audit_log,
  saved_filters, user_preferences endpoints

Bug fixes:
- Session timeout now reads from config instead of hardcoded 18000
- TicketController uses $GLOBALS['config'] instead of duplicate .env parsing
- Add DISCORD_WEBHOOK_URL to centralized config
- Cleanup script uses hashmap for O(1) ticket ID lookups

Dead code removal (~100 lines):
- Remove dead getTicketComments() from TicketModel (wrong bind_param type)
- Remove dead getCategories()/getTypes() from DashboardController
- Remove ~80 lines dead Discord webhook code from update_ticket API

Consolidation:
- Create api/bootstrap.php for shared API setup (auth, CSRF, rate limit)
- Convert 6 API endpoints to use bootstrap
- Extract escapeHtml/getTicketIdFromUrl into shared utils.js
- Batch save for user preferences (1 request instead of 7)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 14:50:06 -05:00
jaredandClaude Opus 4.5 ed9c2a39d1 Fix error message disclosure in API endpoints
Replace exception getMessage() exposure with generic error messages
to prevent internal information disclosure. Errors are now logged
with full details while clients receive sanitized responses.

Affected endpoints:
- add_comment, update_comment, delete_comment
- update_ticket, export_tickets
- generate_api_key, revoke_api_key
- manage_templates, manage_workflows, manage_recurring
- custom_fields, get_users

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 18:56:29 -05:00
jaredandClaude Opus 4.5 7575d6a277 Add performance, security, and reliability improvements
- Consolidate all 20 API files to use centralized Database helper
- Add optimistic locking to ticket updates to prevent concurrent conflicts
- Add caching to StatsModel (60s TTL) for dashboard performance
- Add health check endpoint (api/health.php) for monitoring
- Improve rate limit cleanup with cron script and efficient DirectoryIterator
- Enable rate limit response headers (X-RateLimit-*)
- Add audit logging for workflow transitions
- Log Discord webhook failures instead of silencing
- Fix visibility check on export_tickets.php
- Add database migration system with performance indexes
- Fix cron recurring tickets to use assignTicket method

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 14:39:13 -05:00
jaredandClaude Opus 4.5 98db586bcf feat: Comment edit/delete, auto-link URLs, markdown tables, mobile fixes
- Add comment edit/delete functionality (owner or admin can modify)
- Add edit/delete buttons to comments in TicketView
- Create update_comment.php and delete_comment.php API endpoints
- Add updateComment() and deleteComment() methods to CommentModel
- Show "(edited)" indicator on modified comments
- Add migration script for updated_at column

- Auto-link URLs in plain text comments (non-markdown)
- Add markdown table support with proper HTML rendering
- Preserve code blocks during markdown parsing

- Fix mobile UI elements showing on desktop (add display:none defaults)
- Add mobile styles for CreateTicketView form elements
- Stack status-priority-row on mobile devices

- Update cache busters to v20260124e
- Update Claude.md and README.md documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 16:59:29 -05:00