veza/docs/PENTEST_SCOPE_2026.md

150 lines
13 KiB
Markdown
Raw Permalink Normal View History

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 13:06:08 +00:00
# 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.
## 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.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