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>
13 KiB
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 athttps://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 :
- 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. - Validate the v1.0.9 surface added since the last review : DMCA workflow, marketplace pre-listen, embed widget, WebRTC ICE config, faceted search.
- 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/vezaonce 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 | 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=trueon 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_DOMAINSallow-list — does the validation hold for IDN homograph URLs ?
2. Payment / marketplace
- Order tampering : the
POST /api/v1/marketplace/ordersbody 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/hyperswitchvalidates a signature. Does the implementation check timestamps, or only the HMAC ? - Refund window race :
RefundDeadlineis set to+14don order completion. What happens if the buyer initiates a refund at exactly14d - 1msand the validation race is exposed ? - Pre-listen abuse :
?preview=30is anonymous-OK whenproducts.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/noticeis 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: truefield is trusted. Can a malformed JSON body land a notice withsworn_statementabsent (Go's zero-value) ? - Admin takedown enumeration :
GET /api/v1/admin/dmca/noticesreturns 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/initiateallocates 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/webrtcendpoint is intentionally public perSECURITY_PRELAUNCH_AUDIT.md. We want a second opinion on whether the short-lived TURN credentials are short enough. - Embed iframe XSS :
/embed/track/:idinterpolatestrack.title+track.artistinto HTML body + OG tags viahtml.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_keyare 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.0–8.9), Medium (4.0–6.9), Low (0.1–3.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 athttps://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.
Legal context
- 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.frimmediately ; 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.mdfollow-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/swaggermirrors 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