Files
opencrm/frontend/src/utils/url.ts
T
duffyduckandClaude Opus 4.7 d0d2715baa Pentest 46.1 HIGH + Info-Konsolidierung: zentrale URL-Validierung
46.1 HIGH (Stored XSS via provider.portalUrl): PUT /api/providers/:id
nahm `javascript:alert(...)` als portalUrl ohne Validierung an, das
Portal rendert es als <a href={portalUrl}> → Klick im Kunden-Browser
löste XSS aus.

Fix: neuer zentraler Helper backend/utils/url.validateHttpUrl
- erlaubt nur http(s)-Schemas (sperrt javascript:, data:, file:,
  vbscript:, blob: usw.)
- erfordert absoluten URL mit Host
- per Default keine privaten/Loopback-Hosts (über
  isPrivateOrBlockedHost), weil der Wert Endkunden gezeigt wird
- Trailing-Slash wird gestrippt

Eingebaut in:
- provider.service createProvider + updateProvider (HIGH-Fix)
- appSetting.service validateSettingValue für portalLoginUrl
  (Refactor der bestehenden ad-hoc Validierung → konsolidiert)

Defense-in-depth Frontend: frontend/utils/url.safeHttpUrl liefert
URLs nur zurück wenn http(s), sonst undefined. Eingesetzt in
ContractDetail bei Portal-Link-Rendering und Auto-Login, damit
Alt-Daten in der DB (vor diesem Fix angelegt) nicht klickbar
bleiben.

INFO-Konsolidierung: damit ist die Schema-/Host-Validierung
einheitlich an einer Stelle. Sanitize-Layer (stripHtml in
sanitize.ts) bleibt für reine Text-Felder zuständig.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 11:48:14 +02:00

25 lines
919 B
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Defense-in-depth: liefert einen URL-String nur zurück, wenn er ein
* sicheres http(s)-Schema hat. Sonst undefined.
*
* Hintergrund: das Backend validiert beim Speichern (Pentest 46.1),
* aber Alt-Daten in der DB können noch `javascript:alert(...)` o.ä.
* enthalten. React eskapt URLs in `href` NICHT automatisch ein Klick
* auf einen `javascript:`-Link triggert die XSS im User-Browser.
*
* Diese Funktion wird überall dort eingesetzt, wo wir User-Input
* als `<a href>` rendern oder per `window.open` öffnen.
*/
export function safeHttpUrl(value: string | null | undefined): string | undefined {
if (!value) return undefined;
const trimmed = value.trim();
if (trimmed === '') return undefined;
try {
const parsed = new URL(trimmed);
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return undefined;
return trimmed;
} catch {
return undefined;
}
}