fix(security): resolve audit findings — secrets, env contract, migrations

Removes hardcoded fallback secrets and makes a misconfigured deploy fail
loudly instead of silently falling back to development defaults.

- Remove insecure JWT fallback secrets (messages.module, configuration)
- Remove the 'default-secret' fallback for the 2FA TOTP encryption key and
  allow a dedicated TWO_FACTOR_ENCRYPTION_KEY so rotating JWT_SECRET no
  longer locks out every 2FA user (see docs/2fa-key-rotation.md)
- Require EMAIL_API_URL; drop the hardcoded vendor email endpoint
- Drive WebSocket CORS from CORS_ORIGINS instead of origin:'*'
- Load .env before any Nest module is imported (src/load-env.ts). Decorator
  arguments evaluate at import time, so the gateway previously froze its CORS
  config to the localhost fallback even when CORS_ORIGINS was set
- Add boot-time env validation: missing required vars, weak JWT_SECRET, and
  inverted access/refresh token lifetimes now abort startup
- Enable Redis TLS certificate verification
- Require ADMIN_EMAIL/ADMIN_PASSWORD for the seed; remove the published
  default super-admin credentials and stop printing them
- Add the initial Prisma migration and stop gitignoring prisma/migrations
- Make .env.example an accurate configuration contract (admin bootstrap,
  REDIS_TLS, S3_ENDPOINT, 2FA key, Firebase path; drop the dead SMTP block)
- Add handover documentation: architecture, ER model, sequence and data-flow
  diagrams, 2FA key rotation runbook

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-04 11:30:36 +05:30
parent 4df4d8c9b5
commit c9b38dc6ab
20 changed files with 2167 additions and 27 deletions

View File

@@ -20,6 +20,22 @@ async function main() {
const prisma = new PrismaClient({ adapter });
// Validate up front so a misconfigured run fails before it writes anything.
// No defaults: a seed run without these would create a super-admin whose
// credentials are published in this file.
const adminEmail = process.env.ADMIN_EMAIL;
const adminPassword = process.env.ADMIN_PASSWORD;
if (!adminEmail || !adminPassword) {
throw new Error(
'ADMIN_EMAIL and ADMIN_PASSWORD must be set to seed the super-admin account',
);
}
if (adminPassword.length < 12) {
throw new Error('ADMIN_PASSWORD must be at least 12 characters');
}
console.log('🌱 Starting database seeding...\n');
// =============================================
@@ -1256,9 +1272,7 @@ async function main() {
// =============================================
console.log('👤 Seeding Admin User...');
const adminEmail = process.env.ADMIN_EMAIL || 'admin@re-quest.com';
const adminPassword = process.env.ADMIN_PASSWORD || 'Admin@123456';
// adminEmail / adminPassword are validated at the top of main().
// Hash password with Argon2 (more secure than bcrypt)
const hashedPassword = await argon2.hash(adminPassword);
@@ -1286,7 +1300,6 @@ async function main() {
console.log(' ✅ Admin user created successfully!');
console.log(' ───────────────────────────────');
console.log(` 📧 Email: ${adminEmail}`);
console.log(` 🔑 Password: ${adminPassword}`);
console.log(` 👤 Role: ${admin.role}`);
console.log(` 🆔 ID: ${admin.id}`);
console.log(' ───────────────────────────────\n');