Pentest 48.1 MEDIUM + 50.1 MEDIUM: customerEmailLabel-Strip + SSRF strict
48.1 (XSS in customerEmailLabel):
- Neuer sanitizeCustomerEmailLabel-Helper (stripHtml + trim +
60-Zeichen-Cap)
- Eingesetzt in createProviderConfig + updateProviderConfig
(Write-Pfad) und getProviderPublicSettings (Read-Defensive)
- Damit landet kein <script>/<img onerror>/<svg onload> mehr roh
in der DB, das Längen-Limit ist serverseitig erzwungen, und
Alt-Daten kommen über /public-settings ebenfalls gestrippt raus.
50.1 (SSRF, unvollständige Blockliste bei test-connection):
- safeResolveHost + assertAllowedHost akzeptieren jetzt
{ strict: boolean }. strict=true → isPrivateOrBlockedHost
(sperrt 127/8, 10/8, 172.16/12, 192.168/16, ::1, fc00::/7
unabhängig von SSRF_BLOCK_PRIVATE_IPS).
- test-connection und test-mail-access nutzen strict=true per
Default. Opt-out via env SSRF_ALLOW_INTERNAL_TESTING=true
für On-Prem mit internem Plesk.
- Defense-in-Depth: assertAllowedHost wird jetzt auch VOR der
DNS-Resolution auf den Hostname selbst angewendet, damit
Block-Hostnames (z.B. "metadata.google.internal", "localhost")
nicht via custom-DNS umgangen werden können.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,22 @@
|
||||
|
||||
import prisma from '../../lib/prisma.js';
|
||||
import { decrypt } from '../../utils/encryption.js';
|
||||
import { stripHtml } from '../../utils/sanitize.js';
|
||||
|
||||
// Pentest 48.1 (MEDIUM, 2026-06-01): customerEmailLabel landete roh in der
|
||||
// DB und kam über /api/email-providers/public-settings 1:1 raus. React
|
||||
// escapt zwar als Textnode, aber Defense-in-Depth verlangt Stripping schon
|
||||
// beim Schreiben (PDF/Mail-Templates wären sofort betroffen). Zudem war
|
||||
// das Längenlimit nur frontendseitig gesetzt – hier 60 Zeichen enforced.
|
||||
const CUSTOMER_EMAIL_LABEL_MAX = 60;
|
||||
function sanitizeCustomerEmailLabel(raw: unknown): string | null {
|
||||
if (raw == null) return null;
|
||||
if (typeof raw !== 'string') return null;
|
||||
const stripped = stripHtml(raw) as string;
|
||||
const trimmed = stripped.trim();
|
||||
if (trimmed === '') return null;
|
||||
return trimmed.slice(0, CUSTOMER_EMAIL_LABEL_MAX);
|
||||
}
|
||||
import {
|
||||
IEmailProvider,
|
||||
EmailProviderConfig,
|
||||
@@ -126,7 +142,7 @@ export async function createProviderConfig(data: CreateProviderConfigData) {
|
||||
allowSelfSignedCerts: data.allowSelfSignedCerts ?? false,
|
||||
systemEmailAddress: data.systemEmailAddress || null,
|
||||
systemEmailPasswordEncrypted,
|
||||
customerEmailLabel: data.customerEmailLabel || null,
|
||||
customerEmailLabel: sanitizeCustomerEmailLabel(data.customerEmailLabel),
|
||||
isActive: data.isActive ?? true,
|
||||
isDefault: data.isDefault ?? false,
|
||||
},
|
||||
@@ -159,7 +175,7 @@ export async function updateProviderConfig(
|
||||
if (data.smtpEncryption !== undefined) updateData.smtpEncryption = data.smtpEncryption;
|
||||
if (data.allowSelfSignedCerts !== undefined) updateData.allowSelfSignedCerts = data.allowSelfSignedCerts;
|
||||
if (data.systemEmailAddress !== undefined) updateData.systemEmailAddress = data.systemEmailAddress || null;
|
||||
if (data.customerEmailLabel !== undefined) updateData.customerEmailLabel = data.customerEmailLabel?.trim() || null;
|
||||
if (data.customerEmailLabel !== undefined) updateData.customerEmailLabel = sanitizeCustomerEmailLabel(data.customerEmailLabel);
|
||||
if (data.isActive !== undefined) updateData.isActive = data.isActive;
|
||||
if (data.isDefault !== undefined) updateData.isDefault = data.isDefault;
|
||||
|
||||
@@ -532,7 +548,10 @@ export async function getProviderPublicSettings(): Promise<{
|
||||
}> {
|
||||
const config = await getActiveProviderConfig();
|
||||
const domain = config?.domain ?? null;
|
||||
const customLabel = config?.customerEmailLabel?.trim();
|
||||
// Read-Time-Defensive (Pentest 48.1): falls je rohe Alt-Daten in der DB
|
||||
// landeten, hier nochmal durch den Sanitizer schicken. Stellt sicher,
|
||||
// dass /public-settings nicht ungewollt XSS-Payloads rausreicht.
|
||||
const customLabel = sanitizeCustomerEmailLabel(config?.customerEmailLabel) ?? null;
|
||||
|
||||
return {
|
||||
domain,
|
||||
|
||||
Reference in New Issue
Block a user