veza/veza-backend-api/internal/validators
senke 29cb93767f feat(security): open-redirect protection on Stripe Connect + KYC return URLs
v1.0.10 sécu item 7. The SSRF audit flagged callbacks on Hyperswitch +
distribution submissions ; investigating those revealed a different
risk class on the user-supplied return_url fields :

  * sell_handler.ConnectOnboard accepts return_url + refresh_url and
    forwards them to Stripe Connect.
  * kyc_handler.StartVerification accepts return_url and forwards it
    to Stripe Identity.

Stripe doesn't fetch these URLs server-side (so SSRF is not the
risk), but it redirects the user's browser there after the flow
completes. Without an allow-list, an attacker can craft an onboarding
or verification link with `return_url=https://attacker.com/phishing`
and a victim who clicks the resulting Stripe URL lands on the
attacker's page after Stripe finishes — open-redirect attack
disguised as a legitimate Stripe flow.

Hyperswitch + distribution were already protected :
  * Webhook URLs go through validators.ValidateWebhookURL
    (services/webhook_service.go:54) which blocks private IPs +
    requires HTTPS — pre-existing SSRF guard from SEC-07.
  * Hyperswitch's own callback URL is configured server-side, not
    user-supplied (cf. hyperswitch/client.go) — no SSRF surface.
  * Distribution submissions don't carry user-supplied callbacks —
    the destination platforms are hard-coded.

What's added :

  validators/url_validator.go
    * ValidateRedirectURL(rawURL, allowedHosts) — accepts http or
      https (since Stripe-redirect targets may be local dev hosts),
      requires hostname to match one of allowedHosts exactly OR be
      a subdomain of one. Empty allowedHosts ⇒ permissive (used in
      dev / unconfigured envs ; only checks for non-internal IPs).
    * Reuses the existing IsInternalOrPrivateURL guard so SSRF
      protection still applies for the permissive branch.

  handlers/sell_handler.go + handlers/kyc_handler.go
    * Both handlers now take an allowedRedirectHosts []string param
      at construction. Validation runs after the URL defaults are
      applied so the caller's submitted URL is checked, not the
      backend-derived fallback.
    * Validation failure → 400 with a clear message ("invalid
      return_url: <reason>") so the SPA can render the right error.

  api/routes_marketplace.go
    * Both handlers receive the existing
      cfg.OAuthAllowedRedirectDomains list at construction. Same
      list as the OAuth callback validation, same operator config,
      single source of truth.

Tests pass : go test ./internal/{handlers,validators} -short.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 11:42:41 +02:00
..
email_validator.go v0.9.4 2026-03-05 23:03:43 +01:00
email_validator_test.go adding initial backend API (Go) 2025-12-03 20:29:37 +01:00
password_validator.go feat(v0.13.3): complete - Polish Sécurité Avancée 2026-03-13 10:09:01 +01:00
password_validator_policy_test.go feat(v0.13.3): complete - Polish Sécurité Avancée 2026-03-13 10:09:01 +01:00
password_validator_test.go [T0-002] fix(rust): Corriger erreurs compilation Rust 2026-01-04 01:44:20 +01:00
url_validator.go feat(security): open-redirect protection on Stripe Connect + KYC return URLs 2026-05-02 11:42:41 +02:00
validator.go v0.9.4 2026-03-05 23:03:43 +01:00
validator_test.go STABILISATION: phase 3–5 – API contract, tests & chat-server hardening 2025-12-06 17:21:59 +01:00