- Replace CBC+HMAC with GCM (built-in authentication via authTag)
- Remove createHmac and timingSafeEqual imports (no longer needed)
- Remove Snyk-ignore comments (no longer suppressible findings)
- Update test: tampered MAC test -> tampered auth tag test
- Add one-time migration script for existing CBC-encrypted 2FA secrets
- Replace hardcoded test secrets with crypto-generated values in laravel-encrypter.test.ts and totp.test.ts
- Add 'secure' attribute to locale cookie in language-switcher.tsx
- Validate image URLs before rendering in media-grid.tsx and media-picker.tsx (XSS prevention)
- Validate redirect URL is HTTPS before window.location assignment in TopUpForm.tsx (open redirect prevention)
- Document intentional MD5 usage for legacy PHP compatibility in password.ts
- Document HMAC integrity protection for CBC cipher in laravel-encrypter.ts
- Remove production DB dump (db_backup_*.sql) and update.log from git tracking
- Add DB backups to .gitignore
- Replace all console.log/console.error with structured logger module
- Translate Dutch error messages to English (link-discord.ts)
- Remove dead code blocks (register-form.tsx false && pattern)
- Add ESLint flat config with TypeScript, React, Next.js, jsx-a11y, and security plugins
- Add Prettier config
- Add eslint-plugin-security for security-aware linting
- Fix all 119+ ESLint warnings across the codebase:
- Resolve security/detect-object-injection with safe access patterns
- Resolve security/detect-non-literal-fs-filename with path traversal validation
- Replace <img> with next/image <Image> component
- Remove unused variables and imports
- Replace non-null assertions with proper type guards
- Replace <a> with <Link> for internal navigation
- Use next/script Script component for external scripts
- Fix setState-in-useEffect anti-patterns (navbar-color-picker, logo-generator, theme-switcher)
- Add lint and format scripts to package.json
All checks: typecheck ✓, tests 58/58 ✓, lint 0 errors 0 warnings ✓
- Add DB index on bans.user_id to speed up per-request ban lookups (migration 0008)
- Replace in-process rate limiter with Redis-backed implementation with in-memory fallback
- Add Redis caching layer for site settings with TTL invalidation (migration 0009)
- Add rate limiting to resetPassword to prevent token brute-force attacks
- Update all rateLimit callers to await the now-async function
- Flesh out RadioContests and RadioGiveaways models with title, description, prize, date, and winner columns
- Update radio contest/giveaway pages to display new fields
- Add tests for rate limiter (4 tests) and password-reset actions (3 tests)
- Add REDIS_URL environment variable (optional, falls back to in-memory)
- H1: Add missing sanitize() to help center content rendering
- H2: Tighten CSP by removing unsafe-inline/unsafe-eval from script-src;
move theme init to external JS file with meta tag for defaultDark
- M1: Add SSRF protection for radio API URLs (block private IPs)
- M2: Add rate limiting to SSO ticket endpoint (5 req/30s per user)
- M4: Document locale validation safety in i18n dynamic import
- L1: Truncate stacktraces in admin commandocentrum to first 20 lines
The /client launcher read a non-existent `nitro_client_url` setting, so it
always fell through to "No client configured" and the client never launched.
Faithful to AtomCMS's NitroController + nitro.blade.php, build the launch URL
as {nitro_path}/index.html?sso=<ticket> plus the toolbar colour params, via a
shared buildNitroClientUrl helper. The Flash launcher's Nitro fallback used the
same dead key and is fixed too.
Beyond parity — the web-feasible versions of the "host-only" items plus
extras AtomCMS doesn't have:
- App-level abuse/DDoS guard (src/lib/services/abuse-guard.ts): counts
requests per IP and auto-adds flooders to website_ip_blacklist (enforced
by the access guard) + fires ddosDetected(). OFF by default, tunable via
settings. The iptables layer stays host-only; this is the real app-tier
mitigation. Access guard now also enforces the IP blacklist (cached).
- PWA: a themeable web manifest (src/app/manifest.ts) + a service worker
(public/sw.js, cache-first assets / network-first pages) registered after
hydration — the hotel is now installable.
- /api/health: DB + emulator(RCON) + runtime status probe.
- /developers: a public API documentation page covering every REST endpoint
with its method, path and auth requirement.
- jobs-worker: daily emulator JAR backup (runs host-side in the worker, like
AtomCMS's backup command) — copies + prunes; no-ops unless EMULATOR_JAR_PATH
+ EMULATOR_BACKUP_DIR are set.
Verified live (prod, amx_test): /api/health ok, manifest + sw served, docs
page renders, normal pages unaffected by the guard. tsc 0, vitest 49/49,
next build 0.