fix(security): resolve client audit findings #1
Reference in New Issue
Block a user
Delete Branch "fix/security-audit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Addresses the RE:Quest code-audit findings for the backend. 20 files, no behaviour
change to any feature — the only runtime difference is that a misconfigured deploy
now fails at startup instead of silently falling back to a development default.
Security fixes
messages.module.ts,configuration.ts).'fallbacksecret'/'super-secret-key'were publicly visible in source, makingtokens forgeable if
JWT_SECRETwas ever unset.'default-secret'fallback for the 2FA TOTP encryption key, and add anoptional
TWO_FACTOR_ENCRYPTION_KEY. The key was derived fromJWT_SECRET, sorotating
JWT_SECRET(which the handover requires) would have made every storedTOTP secret undecryptable and locked out every 2FA user. See
docs/2fa-key-rotation.mdbefore rotating.EMAIL_API_URL; drop the hardcoded vendor email endpoint.CORS_ORIGINSinstead oforigin: '*'.rejectUnauthorized: falseremoved).15m/7d(were7d/365d).Env-load ordering fix (please read — this one is subtle)
Decorator arguments are evaluated at module-import time, which is strictly earlier
than
ConfigModule.forRoot().MessagesGatewayreadsprocess.env.CORS_ORIGINSinside its
@WebSocketGatewaydecorator, so it was freezing to the localhostfallback even when
CORS_ORIGINSwas set — which would have rejected everybrowser websocket connection from the live domain.
Fixed by
src/load-env.ts, imported first inmain.ts. It must stay the firstimport, and it has to be a side-effect import because tsc emits all
require()calls ahead of any statement. Verified empirically: the gateway now picks up the
configured origins where it previously did not.
Fail-fast env validation (
src/config/env.validation.ts)Aborts startup on: missing
DATABASE_URL/JWT_SECRET/EMAIL_API_URL; missingCORS_ORIGINS/FRONTEND_URLin production;JWT_SECRETunder 32 chars inproduction; and a refresh lifetime not longer than the access lifetime.
Warns on: access tokens over 1h,
REDIS_TLSoff in production,sslmode=no-verify,localhostin a productionCORS_ORIGINS.Run against the current production env, this reports the missing
EMAIL_API_URLand the inverted
JWT_ACCESS_EXPIRATION=7d/JWT_REFRESH_EXPIRATION=1m.Admin bootstrap
prisma/seed.tsnow requiresADMIN_EMAIL/ADMIN_PASSWORD(min 12 chars),validated before it writes anything. Removes the published
admin@re-quest.com/Admin@123456defaults and stops printing credentials.Migrations
Adds the initial migration and stops gitignoring
prisma/migrations/.⚠️ Production was built with
db push, so it must be baselined once before thefirst
migrate deploy, or it fails with "relation already exists":npx prisma migrate resolve --applied 20260721102313_init
npx prisma migrate deploy
Config contract + docs
.env.examplemade accurate: admin bootstrap,REDIS_TLS,S3_ENDPOINT,S3_PUBLIC_ENDPOINT, 2FA key, Firebase path; dead SMTP block removed with a notethat no SMTP path exists in the code.
docs/— architecture, ER model (24 entities, generated from the schema),sequence diagrams for all 8 core flows, data-flow diagram, 2FA runbook.
Mermaid-in-Markdown; covers the client's §8 documentation requirement.
Verification
nest buildandtsc --noEmitclean. Websocket CORS fix and env validation bothverified by execution, not inspection.