Security hardening covering credentials, brute-force protection, CSRF, TOCTOU races, upload validation, and migration failure handling. Secret rotation is deferred; the existing secrets in .env will be rotated in a later phase. This phase reduces the attack surface and removes the most exploitable issues. Removed: - Hardcoded 'super'/'admin' super-admin credentials in src/app/api/admin/login/route.ts. Username/hash are now loaded from env (SUPER_ADMIN_USERNAME / SUPER_ADMIN_PASSWORD_HASH) and verified via bcrypt like regular admins. Added: - src/lib/config.ts: single source of truth for APP_DOMAIN, APP_URL, subdomain regex/lengths, validation bounds, admin password policy, image-key allow-list regex, and super-admin env credentials. - src/lib/rate-limit.ts: in-memory LRU (via lru-cache) rate limiters — admin login (5/min), validate-code (20/min), check-subdomain (60/min), upload (10/min) — keyed by client IP, returning 429 with X-RateLimit-* headers. - requireAdmin / requireAdminPost / requireSuperAdminPost guards in src/lib/admin-session.ts. All admin mutating routes now enforce same-origin (Origin/Referer/Host check against NEXT_PUBLIC_APP_URL) before running — CSRF protection for the custom admin auth layer. - Logout now imports COOKIE_NAME_ADMIN instead of hardcoding the string. - Server-side magic-byte detection for image uploads (no new dep) — rejects spoofed Content-Type. GIF removed from allowed types. - /api/publish is now transactional (prisma.) with explicit P2002 → 409 handling for subdomain collisions. - /api/image validates the key against an allow-list regex and returns Macedonian error messages (was the only English-localized file). - /api/validate-code enforces a 12-hex-char pattern and uses updateMany with usedByUserId=null guard to make the claim atomic. - /api/check-subdomain validates the slug against the shared regex before hitting the DB and returns a short private Cache-Control. - Admin password minimum length bumped from 6 to 12 with letter+digit complexity requirement across change-password, users POST and users/[id] PUT. Changed: - scripts/start.sh: prisma migrate deploy failures now exit non-zero instead of silently continuing (prevents schema drift in prod). - .env.example: documents SUPER_ADMIN_USERNAME / SUPER_ADMIN_PASSWORD_HASH with example bcrypt-hash generation. - package.json: lru-cache added as direct dependency (already present transitively, promoted to explicit).
43 lines
1.0 KiB
JSON
43 lines
1.0 KiB
JSON
{
|
|
"name": "spomeniqr",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "next dev",
|
|
"build": "next build",
|
|
"start": "next start",
|
|
"lint": "eslint",
|
|
"db:migrate": "prisma migrate dev",
|
|
"db:push": "prisma db push",
|
|
"db:studio": "prisma studio",
|
|
"db:generate": "prisma generate"
|
|
},
|
|
"dependencies": {
|
|
"@aws-sdk/client-s3": "^3.1073.0",
|
|
"@aws-sdk/s3-request-presigner": "^3.1073.0",
|
|
"@clerk/nextjs": "^7.5.7",
|
|
"@prisma/client": "^5.22.0",
|
|
"bcryptjs": "^3.0.3",
|
|
"lru-cache": "^11.0.0",
|
|
"next": "^15.5.19",
|
|
"qrcode": "^1.5.4",
|
|
"react": "19.2.4",
|
|
"react-dom": "19.2.4",
|
|
"uuid": "^14.0.1"
|
|
},
|
|
"devDependencies": {
|
|
"@tailwindcss/postcss": "^4",
|
|
"@types/bcryptjs": "^2.4.6",
|
|
"@types/node": "^20",
|
|
"@types/qrcode": "^1.5.6",
|
|
"@types/react": "^19",
|
|
"@types/react-dom": "^19",
|
|
"@types/uuid": "^10.0.0",
|
|
"eslint": "^9",
|
|
"eslint-config-next": "^15.5.19",
|
|
"prisma": "^5.22.0",
|
|
"tailwindcss": "^4",
|
|
"typescript": "^5"
|
|
}
|
|
}
|