hotfix: Security, performance, and code cleanup

- Remove hardcoded database password fallback
- Add encryption salt validation (min 32 chars)
- Separate EMAIL_ENCRYPTION_KEY from JWT_SECRET
- Fix command injection in status.service.js (use execFileSync)
- Remove unnecessary SQL injection regex middleware
- Create shared utilities (queryBuilder, pagination, emailAccountHelper)
- Fix N+1 query problems in contact and todo services
- Merge duplicate JMAP config functions
- Add database indexes migration
- Standardize error responses with error codes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2026-01-19 07:17:23 +01:00
parent 0523087961
commit 73a3c6bf95
15 changed files with 278 additions and 114 deletions

View File

@@ -66,15 +66,18 @@ export const generateVerificationToken = () => {
* @returns {string} Encrypted password in format: iv:authTag:encrypted
*/
export const encryptPassword = (text) => {
if (!process.env.JWT_SECRET) {
throw new Error('JWT_SECRET environment variable is required for password encryption');
if (!process.env.EMAIL_ENCRYPTION_KEY) {
throw new Error('EMAIL_ENCRYPTION_KEY environment variable is required for password encryption');
}
if (!process.env.ENCRYPTION_SALT) {
throw new Error('ENCRYPTION_SALT environment variable is required for password encryption');
}
if (process.env.ENCRYPTION_SALT.length < 32) {
throw new Error('ENCRYPTION_SALT must be at least 32 characters');
}
const algorithm = 'aes-256-gcm';
const key = crypto.scryptSync(process.env.JWT_SECRET, process.env.ENCRYPTION_SALT, 32);
const key = crypto.scryptSync(process.env.EMAIL_ENCRYPTION_KEY, process.env.ENCRYPTION_SALT, 32);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
@@ -92,15 +95,18 @@ export const encryptPassword = (text) => {
* @returns {string} Plain text password
*/
export const decryptPassword = (encryptedText) => {
if (!process.env.JWT_SECRET) {
throw new Error('JWT_SECRET environment variable is required for password decryption');
if (!process.env.EMAIL_ENCRYPTION_KEY) {
throw new Error('EMAIL_ENCRYPTION_KEY environment variable is required for password decryption');
}
if (!process.env.ENCRYPTION_SALT) {
throw new Error('ENCRYPTION_SALT environment variable is required for password decryption');
}
if (process.env.ENCRYPTION_SALT.length < 32) {
throw new Error('ENCRYPTION_SALT must be at least 32 characters');
}
const algorithm = 'aes-256-gcm';
const key = crypto.scryptSync(process.env.JWT_SECRET, process.env.ENCRYPTION_SALT, 32);
const key = crypto.scryptSync(process.env.EMAIL_ENCRYPTION_KEY, process.env.ENCRYPTION_SALT, 32);
const parts = encryptedText.split(':');
const iv = Buffer.from(parts[0], 'hex');