Commit Graph
8 Commits
Author SHA1 Message Date
jaredandClaude Sonnet 5 c78d24154a Make TRUSTED_PROXIES' insecure-by-default risk loudly visible (#94)
TRUSTED_PROXIES ships empty in .env.example, which disables
AuthMiddleware's reverse-proxy allowlist entirely — a fresh deployment
that doesn't explicitly set it has zero verification that
Remote-User/Remote-Groups headers actually came from the trusted
Authelia proxy. Anything that can reach the app directly (a
misconfigured firewall rule, an exposed container port, SSRF from
another internal service) can set Remote-User: admin and fully
impersonate any user with zero authentication. The enforcement logic
itself was already correct; this was purely a dangerous, easy-to-miss
default.

Added a boxed, unmissable warning around TRUSTED_PROXIES in
.env.example (previously just an inline comment easy to skim past),
added the same warning to README's setup instructions (which didn't
mention this variable at all), and added a Check 8 to api/health.php
that reports a 'warning' status when TRUSTED_PROXIES is empty, so a
deployment that forgets it doesn't go unnoticed after the fact.
Verified against real MariaDB via a running server: the health
endpoint correctly reports 'warning' when empty and 'ok' once set.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv
2026-09-11 11:42:29 -04:00
jaredandClaude Sonnet 5 67d3c13bb6 Fix .env.example: missing Matrix vars, plus a real parse_ini_file outage (#63)
Primary fix (#63): added the 5 env vars config.php reads and README
documents but .env.example never listed: MATRIX_DOMAIN,
SYNAPSE_ADMIN_URL, SYNAPSE_ADMIN_TOKEN, MATRIX_NOTIFY_COMMENTS, and
MATRIX_NOTIFY_ASSIGNMENTS. A deployer following only .env.example had
no indication these existed, silently missing watcher Matrix DMs and
comment/assignment notifications.

While verifying the fix by actually running .env.example through
parse_ini_file() (what config.php calls), found this file could not
be parsed at all — a real, currently-live outage for anyone following
its own first-line instruction ("Copy this file to .env and fill in
your values"):

1. PHP's ini parser treats "#" comments as fragile: punctuation like
   parentheses or quotes inside a "#" comment can throw a syntax error
   even though the line is meant to be inert. The file's header
   comment itself (and 15+ other comment lines) tripped this. Switched
   every comment to ";", which parse_ini_file treats as a true inert
   comment regardless of content — verified with isolated repros of
   both prefixes under all three INI_SCANNER_* modes.
2. LDAP_BIND_DN's example value contained unquoted "=" and commas,
   violating the file's own documented quoting rule and causing a
   second, independent parse failure. Quoted it (and the two other
   comma-bearing LDAP DN values) to match the rule.

config.php has zero fallback for a parse failure — it die()s
immediately — so either bug alone would have taken down every fresh
deployment that didn't hand-edit the example file's comments first.

Verified end-to-end: copied .env.example to a real .env file
unmodified and ran it through config.php's exact parse_ini_file +
quote-stripping logic; it now parses cleanly with all 23 keys
(including the 5 new ones) and LDAP_BIND_DN resolves to the correct
unquoted DN string.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGDKHiU5RJdo3dqQUDow3X
2026-09-08 14:12:04 -04:00
jaredandClaude Opus 4.8 d6214a0339 Add schema baseline, fix cron/retention, restore cleanup, correct docs
Lint / PHP (phpcs PSR-12) (push) Successful in 26s
Lint / JS (eslint) (push) Successful in 12s
Lint / PHP requirements (version + extensions) (push) Successful in 21s
Security / PHP Security (semgrep) (push) Successful in 1m11s
Lint / Deploy (push) Successful in 2s
Lint / Notify on failure (push) Has been skipped
Lint / PHP (phpcs PSR-12) (pull_request) Successful in 47s
Lint / JS (eslint) (pull_request) Successful in 12s
Lint / PHP requirements (version + extensions) (pull_request) Successful in 59s
Security / PHP Security (semgrep) (pull_request) Successful in 1m6s
Lint / Deploy (pull_request) Has been skipped
Lint / Notify on failure (pull_request) Has been skipped
- migrations/000_baseline.sql: full schema baseline captured from prod
  (validated on a throwaway DB: 17 tables/17 FKs), so the schema is
  reproducible for fresh installs / disaster recovery
- create_recurring_tickets cron: send the Matrix ticket-created
  notification and invalidate the stats cache like the other create paths
- create_ticket_api.php + TicketController::create: invalidate the stats
  cache on create/escalate/reopen so dashboard counts aren't stale
- scripts/cleanup_orphan_uploads.php: restored, made safe (24h mtime
  grace, 9-digit-dir only, skips avatars/symlinks, matches the unique
  filename column, --dry-run)
- cron/cleanup_audit_log.php: enforce the configured audit-log retention
  (deleteOldLogs was implemented but never called)
- README: correct CSRF-rotation, hwmon dedup (no 24h window), SLA (no P3),
  stats-cache callers, and the project structure/endpoint listing
- .env.example: document TRUSTED_PROXIES fail-open risk and .env quoting

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:11:26 -04:00
jaredandClaude Opus 4.8 4164f85051 Fix bugs found in second multi-agent review
Security / PHP Security (semgrep) (push) Successful in 1m14s
Lint / Deploy (push) Successful in 3s
Lint / PHP (phpcs PSR-12) (push) Successful in 19s
Lint / JS (eslint) (push) Successful in 8s
Lint / PHP requirements (version + extensions) (push) Successful in 24s
Lint / Notify on failure (push) Has been skipped
XSS / security:
- markdown.js: sanitize footnote labels to a safe slug before using them in
  id/href attributes. Labels are captured before the HTML-escape pass, so a
  label like x"><img onerror=...> broke out → stored XSS (the earlier quote-
  escape fix didn't cover this path). Verified neutralized.
- RateLimitMiddleware: only trust X-Forwarded-For / X-Real-IP when REMOTE_ADDR
  is a configured trusted proxy, and use the rightmost (proxy-appended) entry.
  Previously any client could rotate XFF to escape the per-IP rate limit.
- .env.example: document TRUSTED_PROXIES so fresh deploys aren't fail-open on
  the Authelia forward-auth spoofing protection.

Correctness:
- notifications.php: my previous assigned-to LIKE fix anchored only on '}', so
  BULK assignments (logged {"assigned_to":N,"bulk_operation_id":..}) produced
  no "assigned to you" notification — now matches both '}' and ',' delimiters.
- notifications.php: implement the documented @mention notifications (query
  action_type='mention' rows for the current user); they were never delivered.
- NotificationHelper::notifyWatchers: guard unchecked prepare() so a missing
  ticket_watchers table can't fatal the request after its DB write committed.
- AuditLogModel::getTicketTimeline: JSON_UNQUOTE the extracted ticket_id so
  comment events actually match (string vs JSON-number comparison never did).
- AuditLogModel/audit_log.php: CSV export no longer silently truncates to the
  1000-row UI cap; uses a dedicated higher export limit.
- DashboardView: quick-preview drawer read .ticket-link from the title cell
  (which has none), so the title was always blank — use the cell text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 12:00:36 -04:00
jaredandClaude Sonnet 4.6 18bf1fde0e feat: LDAP avatar support via lldap
- Create tinker-tickets service account in lldap (lldap_strict_readonly)
- Add /api/user_avatar.php: binds to lldap, fetches avatar attribute,
  caches JPEG to uploads/avatars/, returns 404 sentinel for missing photos
- Install php8.2-ldap on LXC 132 (beta) and LXC coding server
- Update layout_header.php: show lt-avatar with photo overlay + initials fallback
- Update TicketView.php: comment avatars use photo overlay pattern
- Add .lt-avatar-img / .lt-avatar-initials CSS for photo-over-initials layout
- Add LDAP_* config keys to config.php and .env.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:47:08 -04:00
jaredandClaude Sonnet 4.6 f59913910f Replace Discord webhook notifications with Matrix (hookshot)
- Add helpers/NotificationHelper.php: shared Matrix webhook sender
  that reads MATRIX_WEBHOOK_URL and MATRIX_NOTIFY_USERS from config
- Remove sendDiscordWebhook() from TicketController; call
  NotificationHelper::sendTicketNotification() instead
- Replace 60-line Discord embed block in create_ticket_api.php
  with a single NotificationHelper call
- config/config.php: DISCORD_WEBHOOK_URL → MATRIX_WEBHOOK_URL +
  new MATRIX_NOTIFY_USERS key (comma-separated Matrix user IDs)
- .env.example: updated env var names and comments

Payload sent to hookshot includes notify_users array so the
JS transform can build proper @mention links for each user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 19:17:46 -05:00
jaredandClaude Opus 4.5 019eaf8980 Add assignment dropdown on ticket creation and fix Discord webhook URLs
- Add APP_DOMAIN config for correct Discord webhook ticket links
- Add "Assign To" dropdown on create ticket form
- Update TicketModel.createTicket() to support assigned_to field
- Update documentation for APP_DOMAIN requirement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 10:24:00 -05:00
jaredandClaude Opus 4.5 6d03f9c89b fix: Session auth, sidebar toggle, and dependencies table
- Change session.cookie_samesite from Strict to Lax for Authelia compatibility
- Redesign sidebar toggle with separate collapse/expand buttons
- Add script to create missing ticket_dependencies table
- Add .env.example template
- Add check for missing .env with helpful error message

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