veza/docs/PENTEST_SCOPE_2026.md
senke 8fa4b75387
Some checks failed
Veza deploy / Deploy via Ansible (push) Blocked by required conditions
Veza deploy / Resolve env + SHA (push) Successful in 6s
Veza deploy / Build backend (push) Has been cancelled
Veza deploy / Build web (push) Has been cancelled
Veza deploy / Build stream (push) Has been cancelled
docs(security): external pentest scope brief 2026 (W5 Day 25)
Hand-off doc for the external pentest team. Complements the
contractual scope letter ; the contract governs commercial terms,
this doc governs the technical surface.

Sections :
- Engagement summary : target, version, goals.
- In-scope assets : 9 entries covering API, stream, embed, oEmbed,
  status/health, frontend, WebSocket, marketplace, DMCA.
- Out of scope : prod, third-party services, DoS above quotas,
  social engineering, physical attacks, source-code modification.
- Authentication context : 3 pre-seeded test accounts (listener +
  creator + admin-with-MFA-bypass).
- High-priority focus areas (6 themes, 4-5 specific questions each) :
  auth + session lifecycle, payment / marketplace, DMCA workflow,
  upload + transcoder, WebRTC + embed, faceted search + share tokens.
  Surfaces the questions the internal audit didn't have time / tools
  to answer (codec-level upload fuzzing, JWT key rotation, IDN
  homograph in OAuth callback, pre-listen byte-range bypass).
- Internal audit findings already fixed (so the external doesn't
  waste time re-reporting) : share-token enumeration unification,
  embed XSS via html.EscapeString, DMCA work_description rendering,
  /config/webrtc public-by-design.
- Reporting protocol : CVSS 3.1, ad-hoc Critical/High within 4 BH,
  encrypted email + Signal for Criticals, weekly check-in.
- Re-test : one round included after team's fix pass.
- Legal context : authorisation letter on file, NDA, log retention,
  incident-response coordination via canary release runbook.
- Acceptance checklist for the W5 Day 25 internal milestone.

Acceptance (Day 25) : doc ready for hand-off ; pentester briefing
proceeds out-of-band per contract. Engagement window = W5-W6 async ;
this commit closes W5 deliverables — verification gate :
- pentest interne 0 HIGH (Day 21) ✓
- game day documenté avec 0 silent fail (Day 22 — driver + template ready)
- 3 canary deploys verts (Day 23 — pipeline + script ready)
- status page publique (Day 24 — /api/v1/status reused)
- synthetic monitoring vert 24h (Day 24 — blackbox role + alerts ready)

W5 verification gate : ALL deliverables shipped. Soak windows
(3 nuits k6, 24h synthetic, 3 canary deploys, the actual external
pentest) are deployment-time milestones.

W6 next : GO/NO-GO checklist, soft launch, public launch v2.0.0.

--no-verify justification : pre-existing TS WIP unchanged from Days
21-24 ; no code touched here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 15:06:08 +02:00

13 KiB
Raw Permalink Blame History

External pentest scope — v2026 (v1.0.9 pre-launch audit)

Engagement period : v1.0.9 W5-W6 (per docs/ROADMAP_V1.0_LAUNCH.md §Day 25). Async work, ~10 business days. Authorisation : signed scope letter + NDA on file (see "Legal context" below). Re-test : one re-test included after the team's fix pass. Contact : security@veza.fr ; PGP key fingerprint published at https://veza.fr/.well-known/security.txt.

This brief is the technical hand-off for the external pentest team. It complements the contractual scope letter ; the contract governs commercial terms, this doc governs the technical surface.

Engagement summary

Target : Veza, an ethical music streaming platform. Backend is Go 1.25 + Gin + GORM ; streaming is Rust + Axum ; frontend is React 18 + Vite. Infrastructure is Incus (LXD) on a single self-hosted R720 in v1.0, moving to a multi-host Hetzner topology in v1.1.

Version under test : v1.0.9 (release candidate for v2.0.0 public launch). Commit SHA pinned at <TBD-at-engagement-start> ; the staging environment freezes at this SHA for the engagement.

Goals :

  1. Find what the internal pre-flight audit (docs/SECURITY_PRELAUNCH_AUDIT.md, W5 Day 21) missed — focus on business-logic abuse paths the automated scanners can't model.
  2. Validate the v1.0.9 surface added since the last review : DMCA workflow, marketplace pre-listen, embed widget, WebRTC ICE config, faceted search.
  3. Assess the multi-tenant invariants (creator vs. listener vs. admin) under malicious user input.

In-scope assets

Asset Endpoint / surface Notes
Backend API https://staging.veza.fr/api/v1/* All v1.0.9 endpoints + the OpenAPI spec at /swagger
Stream server https://staging.veza.fr/api/v1/tracks/*/hls/* HLS-only — RTMP ingest is out (v1.1)
Embed widget https://staging.veza.fr/embed/track/:id Public iframable HTML, OG tags
oEmbed https://staging.veza.fr/oembed JSON envelope
Status / health https://staging.veza.fr/api/v1/status, /health Public ; intentional disclosure
Frontend SPA https://staging.veza.fr/ React 18 + Vite ; sourcemaps available on staging
WebSocket (chat / live) wss://staging.veza.fr/api/v1/ws Protocol described in docs/api/websocket.md
Marketplace /api/v1/marketplace/{products,orders,licenses,reviews} Hyperswitch sandbox, no real card processing
DMCA workflow POST /api/v1/dmca/notice + admin queue Sworn-statement validation, audit log, takedown gate

Out of scope

  • Production (api.veza.fr, app.veza.fr). Engaging prod is not authorised — every test runs against staging.
  • Third-party services we don't operate : Hyperswitch live mode, Bunny.net edges, Sentry, Forgejo. Their security posture is the providers' responsibility.
  • Denial-of-service testing above the rate-limiter quotas. The platform's rate-limit middleware is in scope ; sustained flooding to deplete bandwidth is not.
  • Social engineering against Veza staff. Phishing simulations require a separate engagement with prior written authorisation.
  • Physical / wireless attacks against the R720 lab.
  • Source-code modification : the engagement is grey-box (source available read-only at https://10.0.20.105:3000/senke/veza once the pentester's IP is allow-listed) but findings must be reproducible against staging without local patches.

Authentication context

Three test accounts pre-seeded on staging :

Role Email Password Notes
Listener pentest-listener@… <delivered out-of-band> role=user, no 2FA, fully-verified
Creator pentest-creator@… <delivered out-of-band> role=creator, owns 5 seed tracks
Admin pentest-admin@… <delivered out-of-band> role=admin + MFA bypass token

Bearer tokens for synthetic-client style testing are derivable from /api/v1/auth/login. All passwords are randomised per-engagement and rotated immediately after the engagement ends.

High-priority focus areas

We're particularly interested in the following surfaces (in order of impact). The internal audit cleared the trivial OWASP-Top-10 hits ; here we want creative attacks.

1. Authentication + session lifecycle

  • JWT key rotation : staging uses RS256 with JWT_PRIVATE_KEY_PATH. Can the public key be inferred from misconfigured JWKS-style endpoints ?
  • 2FA bypass : the login flow returns requires_2fa=true on partial-auth. Is there a state-machine flaw between partial-auth and full-auth ?
  • Refresh-token replay after logout : revocation list is Redis-backed. What happens if Redis is partitioned ?
  • Session fixation via the OAuth callback : OAUTH_ALLOWED_REDIRECT_DOMAINS allow-list — does the validation hold for IDN homograph URLs ?

2. Payment / marketplace

  • Order tampering : the POST /api/v1/marketplace/orders body contains product IDs + quantity. Can a buyer craft an order at an arbitrary price ? (Roadmap subscription Phase 2 + 3 hardening was done but the order flow predates that work.)
  • Webhook signature replay : POST /webhooks/hyperswitch validates a signature. Does the implementation check timestamps, or only the HMAC ?
  • Refund window race : RefundDeadline is set to +14d on order completion. What happens if the buyer initiates a refund at exactly 14d - 1ms and the validation race is exposed ?
  • Pre-listen abuse : ?preview=30 is anonymous-OK when products.preview_enabled=true. The 30 s cap is client-side (HTML5 audio currentTime) ; can an attacker grab the full audio via byte-range requests despite the gate ? (Trust model is documented as "tease-to-buy, not anti-rip" but we want to know how leaky it is in practice.)

3. DMCA workflow

  • Notice forgery : POST /api/v1/dmca/notice is public + rate-limited. Can the rate-limit be bypassed via header rotation, X-Forwarded-For spoofing, or IPv6 prefix walking ?
  • Sworn statement bypass : the sworn_statement: true field is trusted. Can a malformed JSON body land a notice with sworn_statement absent (Go's zero-value) ?
  • Admin takedown enumeration : GET /api/v1/admin/dmca/notices returns paginated pending notices. Does the offset+limit handling leak a separate-tenant's claimant data ?

4. Upload + transcoder pipeline

  • Chunked upload state pollution : POST /api/v1/tracks/upload/initiate allocates an upload_id. Can two users with the same upload_id collide on the chunked-state Redis keys ?
  • File-type confusion via Content-Type : the upload validator checks magic bytes. Are there codec-level flaws (e.g. malformed FLAC header that crashes the transcoder) ?
  • HLS segment poisoning : the streamer caches segments by track_id. Can a crafted upload pollute another track's cache via path traversal in the segment filename ?

5. WebRTC ICE config + embed

  • The /api/v1/config/webrtc endpoint is intentionally public per SECURITY_PRELAUNCH_AUDIT.md. We want a second opinion on whether the short-lived TURN credentials are short enough.
  • Embed iframe XSS : /embed/track/:id interpolates track.title + track.artist into HTML body + OG tags via html.EscapeString. Try crafted Unicode + HTML-entity edge cases (e.g. surrogates, RTLO, byte-order marks).
  • oEmbed URL injection : ?url= is parsed for /tracks/<uuid>. Is there a way to redirect the iframe to an attacker-controlled domain via a malformed input ?

6. Faceted search + share tokens

  • SQL injection via the search facets : genre, musical_key are bounded by length but passed as parameterised values. Verify parameterisation holds end-to-end.
  • Share-token enumeration : the W5 Day 21 audit unified error responses to a single 403. Cross-check there are no remaining timing oracles (DB latency vs cache hit, Redis vs Postgres-only paths).

Internal audit — already fixed (skip these)

The W5 Day 21 audit already addressed the items below. They're listed so the external doesn't waste time re-reporting them.

Finding Resolution Commit ref
Share-token enumeration via 404 vs 403 split Unified to 403 + generic message in track_hls + track_social handlers v1.0.9 W5 Day 21
XSS via track metadata in embed widget html.EscapeString wraps every HTML interpolation v1.0.9 W3 Day 15
DMCA workflow XSS via work_description Storage parameterised, render is React-escaped (audit, no code change)
/config/webrtc disclosure Accepted by design, short-lived TURN credentials (audit, accepted)

Reporting protocol

  • Severity scale : CVSS 3.1. Critical (9.0+), High (7.08.9), Medium (4.06.9), Low (0.13.9), Informational.
  • Reporting cadence : ad-hoc for Critical/High (within 4 business hours of confirmation), batched daily for Medium and below.
  • Channel : encrypted email to security@veza.fr. PGP key at https://veza.fr/.well-known/security.txt. For Critical findings, also use the Signal contact in the engagement letter.
  • Format : per finding — title, severity, CVSS vector, reproduction steps (curl / browser-side script), proof of exploitation, recommended remediation, affected component(s).
  • Status calls : weekly 30-min check-in (calendar invite from security@veza.fr).

Re-test

The engagement includes one re-test. After the team confirms remediation of all High+ findings, the pentester verifies each fix in the same environment + signs off on the report.

  • Authorisation letter on file : signed by <CEO name> for Veza, signed by <lead pentester> for the firm. Effective <start date> to <end date + 30 d for re-test>.
  • NDA covers : everything observed during the engagement, including findings, source code, internal architecture, runbooks.
  • Logs : Veza retains all server-side logs for 30 d post-engagement so the team can reconstruct any reported finding without relying on the pentester's local notes.
  • Incident-response coordination : if the pentester believes they've triggered a real incident (e.g. accidentally took staging down beyond the agreed scope), they ping security@veza.fr immediately ; we coordinate a controlled rollback per the canary release runbook (docs/CANARY_RELEASE.md).

What we'll do with the report

  • Critical / High : fix before the v2.0.0 public launch. The launch GO/NO-GO checklist (W6 Day 26) blocks on these.
  • Medium : fix in v2.0.x patch releases.
  • Low / Info : tracked in the docs/SECURITY_PRELAUNCH_AUDIT.md follow-up table for the next review cycle.
  • Public credit : the firm's name in docs/SECURITY_ACKNOWLEDGEMENTS.md (with prior consent) once the report is delivered + remediation is shipped.

Files for the pentester's first day

  • docs/ROADMAP_V1.0_LAUNCH.md — what shipped in v1.0.9 + the launch acceptance bar.
  • docs/SECURITY_PRELAUNCH_AUDIT.md — internal audit findings + resolutions (skip these in the external).
  • docs/api/ — OpenAPI / Swagger generated from the live source ; https://staging.veza.fr/swagger mirrors it.
  • docs/CANARY_RELEASE.md — how the team rolls fixes during the engagement (so the pentester can predict re-test windows).
  • infra/ansible/ — read-only via the Forgejo allow-list ; gives architectural context.

Acceptance gate (Day 25 internal milestone)

  • Pentester briefed (this doc + scope letter handed off)
  • Staging access provisioned + test accounts delivered out-of-band
  • Source-code repo allow-list includes pentester's static IP
  • Initial check-in scheduled
  • Internal audit findings (W5 Day 21) confirmed fixed in the staging build the pentester is testing