From 13534498faac22bbbf988f3694931419a12b3e1e Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 2 Apr 2026 08:07:25 +0200 Subject: [PATCH] feat(user): enhance email handling with validation and normalization functions - Introduced `looksLikePlausibleEmail` to validate email format, ensuring only plausible addresses are processed. - Added `normalizeEmailCandidate` to standardize email input, returning null for invalid formats. - Updated `decodeEncryptedBlob` to utilize normalization functions for both UTF-8 and hex formats, improving email decryption reliability. - Adjusted `SettingsService` to ensure email is set after merging adult access state, maintaining data integrity. --- backend/models/community/user.js | 32 ++++++++++++++++++++++++----- backend/services/settingsService.js | 5 +++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/backend/models/community/user.js b/backend/models/community/user.js index 4de6d12..b5717f2 100644 --- a/backend/models/community/user.js +++ b/backend/models/community/user.js @@ -8,6 +8,26 @@ function encodeEncryptedValueToBlob(value) { return Buffer.from(encrypted, 'utf8'); } +/** Nur echte Adressen zurückgeben — verhindert Anzeige von Base64-/Key-artigem Müll bei fehlender Entschlüsselung. */ +function looksLikePlausibleEmail(s) { + if (typeof s !== 'string') { + return false; + } + const t = s.trim(); + if (!t || t.length > 254) { + return false; + } + return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i.test(t); +} + +function normalizeEmailCandidate(s) { + if (!s || typeof s !== 'string') { + return null; + } + const t = s.trim(); + return looksLikePlausibleEmail(t) ? t : null; +} + function decodeEncryptedBlob(value) { if (!value) { return null; @@ -16,8 +36,9 @@ function decodeEncryptedBlob(value) { try { const encryptedUtf8 = value.toString('utf8'); const decryptedUtf8 = decrypt(encryptedUtf8); - if (decryptedUtf8) { - return decryptedUtf8; + const fromUtf8 = normalizeEmailCandidate(decryptedUtf8); + if (fromUtf8) { + return fromUtf8; } } catch (error) { console.warn('Email utf8 decryption failed, trying legacy hex format:', error.message); @@ -26,15 +47,16 @@ function decodeEncryptedBlob(value) { try { const encryptedHex = value.toString('hex'); const decryptedHex = decrypt(encryptedHex); - if (decryptedHex) { - return decryptedHex; + const fromHex = normalizeEmailCandidate(decryptedHex); + if (fromHex) { + return fromHex; } } catch (error) { console.warn('Email legacy hex decryption failed:', error.message); } try { - return value.toString('utf8'); + return normalizeEmailCandidate(value.toString('utf8')); } catch (error) { console.warn('Email could not be read as plain text:', error.message); return null; diff --git a/backend/services/settingsService.js b/backend/services/settingsService.js index 9b72b76..ca679b2 100644 --- a/backend/services/settingsService.js +++ b/backend/services/settingsService.js @@ -426,11 +426,12 @@ class SettingsService extends BaseService{ const adultAccess = await this.getAdultAccessStateByUserId(user.id); + /* adultAccess zuletzt mergen, email danach setzen — nie von anderen Payload-Feldern überschreiben */ return { username: user.username, - email: email, showinsearch: user.searchable, - ...adultAccess + ...adultAccess, + email, }; } catch (error) { console.error('Error getting account settings:', error);