Lint / PHP (phpcs PSR-12) (push) Successful in 28s
Lint / JS (eslint) (push) Successful in 12s
Lint / PHP requirements (version + extensions) (push) Successful in 30s
Lint / Notify on failure (push) Skipped
Security / PHP Security (semgrep) (push) Successful in 3m9s
Lint / Deploy (push) Successful in 2s
Four interrelated gaps in the same rate-limiting path: - #80: RATE_LIMIT_DEFAULT/RATE_LIMIT_API were defined in config.php but RateLimitMiddleware never read them (hardcoded class constants instead), and they weren't in .env.example — a deployer editing them saw zero effect with no documented way to actually change the limit. - #81: Bearer traffic was rate-limited purely by a shared IP bucket (the session-based half was a no-op for stateless clients, since a fresh session starts on every request). Two different API keys from the same host/NAT egress IP shared ONE bucket, so a chatty or misbehaving key could 429 a completely unrelated key's traffic. - #82: X-RateLimit-* headers reported the meaningless session counter for Bearer clients instead of whatever bucket actually governed them. - #83: RateLimitMiddleware::check() called session_start() unconditionally, before ApiKeyAuth even runs — continuous session-file churn and an unnecessary Set-Cookie on every stateless API request, using un-hardened cookie defaults since it runs before AuthMiddleware's hardening (which Bearer requests never reach anyway). Fixed as one pass since they're the same code path: config.php now reads RATE_LIMIT_DEFAULT/RATE_LIMIT_API from .env (added there too, documented); the middleware now extracts the raw Bearer token (independent of ApiKeyAuth, so no DB round-trip needed before rate limiting, and it works whether or not the token later turns out valid) and rate-limits it via its own per-token bucket instead of starting a session — the existing IP-based bucket still applies underneath as defense-in-depth against volumetric abuse from one network path, but each distinct key now gets real isolated headroom. getStatus()/addHeaders() report that per-token bucket for Bearer requests instead of the session counter. Verified: a Bearer request creates zero session files (confirmed via real session-directory file count before/after); two different keys from different IPs are fully isolated (one exhausting its own 120/min bucket has zero effect on the other); a config-driven RATE_LIMIT_API override (e.g. 5) is correctly honored for session-based (non-Bearer) traffic; X-RateLimit-* status correctly reflects the per-key bucket for a Bearer request. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv
93 lines
3.9 KiB
Bash
93 lines
3.9 KiB
Bash
; Tinker Tickets Environment Configuration
|
|
; Copy this file to .env and fill in your values
|
|
;
|
|
; NOTE: This file is parsed with PHP's parse_ini_file. Any value containing
|
|
; special characters -- #, ;, =, quotes, spaces, etc. -- MUST be wrapped in
|
|
; double quotes, e.g. DB_PASS="p@ss;word#1". The application now fails loudly
|
|
; -- dies with a clear error -- if the .env file cannot be parsed, so an
|
|
; unquoted special character will take the whole app down rather than
|
|
; silently using a wrong value.
|
|
;
|
|
; Comments in this file use ";" rather than "#": PHP's ini parser treats "#"
|
|
; comments as fragile -- punctuation like parentheses or quotes inside a "#"
|
|
; comment can produce a syntax error even though the line is meant to be
|
|
; inert, silently breaking every value below it. ";" comments don't have this
|
|
; problem, so keep using ";" for any comment added to this file.
|
|
|
|
; Database Configuration
|
|
DB_HOST=10.10.10.50
|
|
DB_USER=tinkertickets
|
|
DB_PASS=your_password_here
|
|
DB_NAME=ticketing_system
|
|
|
|
; Matrix Webhook (optional - for notifications via matrix-hookshot)
|
|
; Set to your hookshot generic webhook URL, e.g.:
|
|
; https://matrix.lotusguild.org/webhook/uuid-goes-here
|
|
MATRIX_WEBHOOK_URL=
|
|
|
|
; Matrix users to @mention on every new ticket (comma-separated Matrix user IDs)
|
|
; e.g. @jared:matrix.lotusguild.org,@alice:matrix.lotusguild.org
|
|
MATRIX_NOTIFY_USERS=
|
|
|
|
; Matrix homeserver domain (used to build Matrix user IDs from LLDAP usernames)
|
|
MATRIX_DOMAIN=
|
|
|
|
; Synapse internal URL and admin token (used to resolve usernames -> Matrix IDs
|
|
; for watcher DMs)
|
|
SYNAPSE_ADMIN_URL=
|
|
SYNAPSE_ADMIN_TOKEN=
|
|
|
|
; Optional: send a Matrix notification on comments and/or assignments (0/1)
|
|
MATRIX_NOTIFY_COMMENTS=0
|
|
MATRIX_NOTIFY_ASSIGNMENTS=0
|
|
|
|
; Application Domain (required for Matrix webhook ticket links)
|
|
; Set this to your public domain, e.g. t.lotusguild.org
|
|
APP_DOMAIN=
|
|
|
|
; Allowed Hosts for HTTP_HOST validation (comma-separated)
|
|
; Include all domains that can access this application
|
|
ALLOWED_HOSTS=localhost,127.0.0.1
|
|
|
|
; ============================================================================
|
|
; REQUIRED FOR PRODUCTION -- READ BEFORE DEPLOYING -- TRUSTED_PROXIES
|
|
; ============================================================================
|
|
; Trusted reverse proxy IPs, comma-separated -- e.g. the Authelia/nginx proxy.
|
|
; Set this to the IP address(es) of your reverse proxy. Authelia forward-auth
|
|
; headers (Remote-User / Remote-Groups) and forwarded client IPs are only
|
|
; trusted when REMOTE_ADDR is in this list.
|
|
;
|
|
; Leaving this EMPTY disables reverse-proxy verification entirely: the app then
|
|
; trusts Remote-User / Remote-Groups headers from ANY source. If the PHP
|
|
; backend is reachable directly -- a misconfigured firewall rule, a container
|
|
; network accidentally exposing the port, SSRF from another internal service
|
|
; -- ANYONE can set Remote-User: admin themselves and fully impersonate any
|
|
; user, including an admin, with ZERO authentication. Only leave it empty when
|
|
; network topology guarantees PHP is reachable solely via the trusted proxy
|
|
; (e.g. local development), never in a real deployment.
|
|
;
|
|
; Exact IP match only (no CIDR). Example (single proxy): TRUSTED_PROXIES=10.10.10.27
|
|
; Example (multiple): TRUSTED_PROXIES=10.10.10.27,10.10.10.28
|
|
; ============================================================================
|
|
TRUSTED_PROXIES=
|
|
|
|
; Timezone (default: America/New_York)
|
|
TIMEZONE=America/New_York
|
|
|
|
; LDAP / lldap (for user avatar lookups)
|
|
LDAP_ENABLED=true
|
|
LDAP_HOST=10.10.10.39
|
|
LDAP_PORT=3890
|
|
LDAP_BIND_DN="uid=tinker-tickets,ou=people,dc=example,dc=com"
|
|
LDAP_BIND_PW=
|
|
LDAP_BASE_DN="dc=example,dc=com"
|
|
LDAP_USER_BASE="ou=people,dc=example,dc=com"
|
|
; How long to cache avatar images locally (seconds, default 3600)
|
|
AVATAR_CACHE_TTL=3600
|
|
|
|
; Session-based rate limits (requests per 60s window). These govern
|
|
; browser/session traffic on general and API endpoints respectively;
|
|
; Bearer-key API traffic is rate-limited separately, per API key.
|
|
RATE_LIMIT_DEFAULT=100
|
|
RATE_LIMIT_API=60
|