veza/veza-backend-api/internal
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
..
api feat(security): open-redirect protection on Stripe Connect + KYC return URLs 2026-05-02 11:42:41 +02:00
common v0.9.2 2026-03-05 19:27:34 +01:00
config feat(auth): DB-backed JWT jti revocation ledger (sécu item 6) 2026-05-02 11:37:02 +02:00
core feat(marketplace): multi-creator royalty splits with audit ledger 2026-05-01 20:53:22 +02:00
database v0.9.4 2026-03-05 23:03:43 +01:00
dto feat(auth): RGPD/COPPA age gate at registration (16+ minimum) 2026-05-01 18:05:47 +02:00
elasticsearch fix(backend): unblock handlers + elasticsearch test packages 2026-04-30 14:48:23 +02:00
email refactor(backend,infra): unify SMTP env schema on canonical SMTP_* names 2026-04-16 20:44:09 +02:00
errors v0.9.8 2026-03-06 19:13:16 +01:00
eventbus fix(eventbus): log RabbitMQ publish failures instead of silent drop 2026-04-16 20:50:51 +02:00
features adding initial backend API (Go) 2025-12-03 20:29:37 +01:00
handlers feat(security): open-redirect protection on Stripe Connect + KYC return URLs 2026-05-02 11:42:41 +02:00
infrastructure v0.9.4 2026-03-05 23:03:43 +01:00
integration style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
interfaces adding initial backend API (Go) 2025-12-03 20:29:37 +01:00
jobs feat(webhooks): persist raw hyperswitch payloads to audit log — v1.0.7 item E 2026-04-18 02:44:58 +02:00
logging style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
metrics feat(cdn): Bunny.net signed URLs + HLS cache headers + metric collision fix (W3 Day 13) 2026-04-28 14:07:20 +02:00
middleware feat(auth): DB-backed JWT jti revocation ledger (sécu item 6) 2026-05-02 11:37:02 +02:00
models feat(legal): DMCA notice handler + admin queue + 451 playback gate (W3 Day 14) 2026-04-28 15:39:33 +02:00
monitoring feat(cdn): Bunny.net signed URLs + HLS cache headers + metric collision fix (W3 Day 13) 2026-04-28 14:07:20 +02:00
pagination v0.9.8 2026-03-06 19:13:16 +01:00
recovery chore(v0.102): consolidate remaining changes — docs, frontend, backend 2026-02-20 13:02:12 +01:00
repositories fix(v0.12.6.1): remediate 2 CRITICAL + 10 HIGH + 1 MEDIUM pentest findings 2026-03-12 05:40:53 +01:00
resilience chore: consolidate CI, E2E, backend and frontend updates 2026-02-17 16:43:21 +01:00
response fix: stabilize builds, tests, and lint across all stacks 2026-04-05 16:48:07 +02:00
security refactor(backend): replace 40 fmt.Printf calls with zap structured logging 2026-02-22 17:44:38 +01:00
services feat(auth): DB-backed JWT jti revocation ledger (sécu item 6) 2026-05-02 11:37:02 +02:00
shutdown incus deployement fully implemented, Makefile updated and make fmt ran 2026-01-13 19:47:57 +01:00
testutils ci: retire legacy backend-ci.yml, centralize Docker probe in SkipIfNoIntegration 2026-04-15 16:12:45 +02:00
tracing feat(observability): OTel SDK + collector + Tempo + 4 hot path spans (W2 Day 9) 2026-04-28 01:15:11 +02:00
types fix(backend): commit swagger annotation pass + missing handler methods 2026-05-01 10:16:57 +02:00
upload [INT-015] int: Add file upload format standardization 2025-12-25 15:40:01 +01:00
utils fix(v0.12.6): apply all pentest remediations — 36 findings across 36 files 2026-03-14 00:44:46 +01:00
validators feat(security): open-redirect protection on Stripe Connect + KYC return URLs 2026-05-02 11:42:41 +02:00
websocket feat(redis): Sentinel HA + cache hit rate metrics (W3 Day 11) 2026-04-28 13:36:55 +02:00
workers feat(transcode): read from S3 signed URL when track is s3-backed (v1.0.8 P2) 2026-04-23 23:34:51 +02:00