From 124536dfd48361b455a4e7b1e1f8d670c2570fa3 Mon Sep 17 00:00:00 2001 From: senke Date: Fri, 13 Mar 2026 16:52:03 +0100 Subject: [PATCH] feat(v0.12.6): update pentest deliverables with comprehensive 36-finding audit Expanded from initial 14-finding analysis to full 36 findings after 6 specialized audit agents completed deep analysis. - PENTEST_REPORT: 5 CRITICAL, 10 HIGH, 12 MEDIUM, 6 LOW, 3 INFO - REMEDIATION_MATRIX: P0 (6h), P1 (17h), P2 (8h), P3 (10h) = ~41h total - ASVS_CHECKLIST: 70/102 (68.6%) with 5 FAIL, 26 PARTIAL Co-Authored-By: Claude Opus 4.6 --- docs/ASVS_CHECKLIST_v0.12.6.md | 95 +++--- docs/PENTEST_REPORT_VEZA_v0.12.6.md | 505 ++++++++++++++++------------ docs/REMEDIATION_MATRIX_v0.12.6.md | 120 +++++-- 3 files changed, 427 insertions(+), 293 deletions(-) diff --git a/docs/ASVS_CHECKLIST_v0.12.6.md b/docs/ASVS_CHECKLIST_v0.12.6.md index e6b43cae8..31da705e8 100644 --- a/docs/ASVS_CHECKLIST_v0.12.6.md +++ b/docs/ASVS_CHECKLIST_v0.12.6.md @@ -3,6 +3,7 @@ **Date** : 2026-03-13 **Standard** : OWASP Application Security Verification Standard v4.0.3, Level 2 **Scope** : VEZA monorepo (Go backend, Rust stream server, React frontend) +**Référence** : PENTEST_REPORT_VEZA_v0.12.6.md (36 findings) --- @@ -21,9 +22,9 @@ |---|----------|--------|-------| | V1.1.1 | Application uses a single vetted security architecture | ✅ PASS | Architecture hexagonale, middlewares auth centralisés | | V1.1.2 | Security controls are applied centrally | ✅ PASS | Auth middleware, rate limit middleware, CORS config centralisée | -| V1.1.3 | Sensitive data is identified and protected | ✅ PASS | `json:"-"` sur passwords, tokens, secrets MFA | +| V1.1.3 | Sensitive data is identified and protected | ⚠️ PARTIAL | `json:"-"` sur passwords/tokens, mais popularity metrics leakent — **HIGH-002** | | V1.1.4 | All application components are defined | ✅ PASS | ORIGIN_MASTER_ARCHITECTURE.md documente tous les composants | -| V1.1.5 | High-value business logic flows defined | ✅ PASS | ORIGIN_BUSINESS_LOGIC.md + payment flow tests | +| V1.1.5 | High-value business logic flows defined | ⚠️ PARTIAL | Flows définis mais race conditions dans payout/refund — **CRIT-002, CRIT-003** | | V1.1.6 | Threat model exists | ⚠️ PARTIAL | ORIGIN_SECURITY_FRAMEWORK.md existe mais pas de threat model formel | --- @@ -38,8 +39,8 @@ | V2.1.4 | Passwords are stored using approved hashing | ⚠️ PARTIAL | bcrypt utilisé mais cost incohérent (10 vs 12) — **MEDIUM-001** | | V2.1.5 | Password change requires old password | ✅ PASS | `ChangePassword()` vérifie l'ancien mot de passe | | V2.1.6 | Password breach checking | ✅ PASS | Password history (5 derniers) vérifié | -| V2.2.1 | Anti-automation controls on auth | ✅ PASS | Rate limiting sur login/register | -| V2.2.2 | Weak authenticator resistance | ✅ PASS | Brute-force protection via rate limiting | +| V2.2.1 | Anti-automation controls on auth | ⚠️ PARTIAL | Rate limiting existe mais bypassable via IP spoofing — **HIGH-001** | +| V2.2.2 | Weak authenticator resistance | ✅ PASS | Brute-force protection via rate limiting + account lockout | | V2.2.3 | Credential recovery resists abuse | ✅ PASS | Password reset tokens limités, expirent | | V2.3.1 | Session tokens are generated using approved CSPRNG | ✅ PASS | crypto/rand utilisé (19 fichiers) | | V2.4.1 | MFA available | ✅ PASS | TOTP MFA + recovery codes implémentés | @@ -48,7 +49,7 @@ | V2.5.3 | Stateless tokens include expiry | ✅ PASS | JWT `exp` claim enforced | | V2.5.4 | JWT algorithm whitelisting | ✅ PASS | RS256 primary, HS256 dev-only fallback | | V2.7.1 | Passwords are hashed with salt | ✅ PASS | bcrypt inclut le salt automatiquement | -| V2.8.1 | Session management for auth tokens | ✅ PASS | Session service avec revocation, token rotation | +| V2.8.1 | Session management for auth tokens | ⚠️ PARTIAL | Session service avec revocation, mais refresh token TOCTOU — **HIGH-005** | | V2.9.1 | WebAuthn support | ✅ PASS | WebAuthn credentials stored securely (`json:"-"`) | --- @@ -75,11 +76,11 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| -| V4.1.1 | Principle of least privilege | ✅ PASS | Role-based access (user, creator, moderator, admin) | +| V4.1.1 | Principle of least privilege | ⚠️ PARTIAL | Roles définis mais mass assignment permet escalation — **HIGH-003** | | V4.1.2 | Access control applied server-side | ✅ PASS | All auth checks in Go middleware/handlers | | V4.1.3 | Deny by default | ✅ PASS | Protected routes require explicit auth middleware | -| V4.2.1 | Sensitive data access restricted | ⚠️ PARTIAL | followers_count public — **HIGH-002** | -| V4.2.2 | User can only access own data | ✅ PASS | GDPR export/delete checks user ownership | +| V4.2.1 | Sensitive data access restricted | ❌ FAIL | Analytics IDOR (pas de ownership check) — **CRIT-004** ; followers_count public — **HIGH-002** | +| V4.2.2 | User can only access own data | ❌ FAIL | Track analytics de n'importe quel créateur accessible — **CRIT-004** | | V4.3.1 | Admin functions protected | ✅ PASS | `RequireRole("admin")` middleware | | V4.3.2 | Users cannot access unauthorized admin APIs | ✅ PASS | Role check in middleware before handler | @@ -90,11 +91,11 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| | V5.1.1 | HTTP parameter pollution defense | ✅ PASS | Gin framework handles this natively | -| V5.1.2 | Input validation on all inputs | ✅ PASS | Struct binding with validation tags | +| V5.1.2 | Input validation on all inputs | ⚠️ PARTIAL | Struct binding OK mais file upload marketplace sans validation taille — **MEDIUM-006** | | V5.1.3 | Output encoding for context | ✅ PASS | React auto-escapes, Go JSON marshaling | -| V5.2.1 | Untrusted HTML sanitized | ✅ PASS | No user HTML rendering | +| V5.2.1 | Untrusted HTML sanitized | ✅ PASS | DOMPurify (frontend) + `html.EscapeString` (backend) | | V5.3.1 | SQL injection prevention | ✅ PASS | Parameterized queries everywhere | -| V5.3.2 | OS command injection prevention | ✅ PASS | No exec.Command with user input | +| V5.3.2 | OS command injection prevention | ✅ PASS | `exec.CommandContext` avec arguments séparés | | V5.3.3 | LDAP injection prevention | ➖ N/A | No LDAP used | | V5.3.4 | XSS prevention | ✅ PASS | React JSX auto-escaping, CSP header | | V5.3.7 | SSRF prevention | ✅ PASS | No user-controlled URL fetching in backend | @@ -110,8 +111,8 @@ | V6.2.1 | Approved cryptographic algorithms | ✅ PASS | bcrypt, RS256, HMAC-SHA512, crypto/rand | | V6.2.2 | Industry-proven crypto libraries | ✅ PASS | Go stdlib crypto, `golang.org/x/crypto` | | V6.2.3 | Random values from CSPRNG | ✅ PASS | `crypto/rand` exclusively (not math/rand) | -| V6.2.4 | Key rotation supported | ✅ PASS | JWT key rotation documented | -| V6.3.1 | Secrets not in source code | ✅ PASS | `.env.production` uses `${VAR}` templates, gitleaks CI scan | +| V6.2.4 | Key rotation supported | ⚠️ PARTIAL | JWT key rotation documentée mais pas de `kid` header — **INFO-003** | +| V6.3.1 | Secrets not in source code | ⚠️ PARTIAL | `.env.production` uses templates, mais JWT_SECRET hardcodé dans docker-compose — **LOW-006** | | V6.4.1 | Key management procedures | ✅ PASS | JWT RSA keys loaded from env/files | --- @@ -138,7 +139,7 @@ | V8.1.2 | Sensitive data in transit encrypted | ✅ PASS | HTTPS/TLS enforced, HSTS header | | V8.2.1 | Sensitive data not in URL | ✅ PASS | Auth via cookies, not URL params | | V8.2.2 | HTTP caching headers on sensitive data | ✅ PASS | Cache-Control set on auth responses | -| V8.3.1 | Sensitive data removable on request | ✅ PASS | GDPR deletion endpoint functional | +| V8.3.1 | Sensitive data removable on request | ⚠️ PARTIAL | GDPR deletion fonctionne mais données financières non traitées — **HIGH-008** | | V8.3.2 | Data export available | ✅ PASS | GDPR export endpoint (JSON, 48h) | | V8.3.4 | Data retention policy defined | ✅ PASS | PRIVACY_POLICY.md §7 — retention table | @@ -148,7 +149,7 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| -| V9.1.1 | TLS for all connections | ✅ PASS | HTTPS enforced, HSTS | +| V9.1.1 | TLS for all connections | ⚠️ PARTIAL | HTTPS enforced, HSTS backend, mais nginx frontend manque HSTS — **MEDIUM-009** | | V9.1.2 | TLS 1.2+ only | ✅ PASS | Go net/http defaults to TLS 1.2+ | | V9.1.3 | Strong cipher suites | ✅ PASS | Go stdlib uses modern ciphers | | V9.2.1 | Webhook signature verification | ✅ PASS | HMAC-SHA512 with hmac.Equal() | @@ -161,7 +162,7 @@ |---|----------|--------|-------| | V10.1.1 | No malicious code (backdoors, etc.) | ✅ PASS | No suspicious patterns found | | V10.2.1 | No time bombs | ✅ PASS | No scheduled destructive operations | -| V10.3.1 | Source code review performed | ✅ PASS | This audit | +| V10.3.1 | Source code review performed | ✅ PASS | This audit (6 agents spécialisés) | --- @@ -169,10 +170,10 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| -| V11.1.1 | Business logic flow enforced server-side | ✅ PASS | Payment flow, payout flow verified | -| V11.1.2 | Business logic limits enforced | ✅ PASS | Rate limits, pagination limits | -| V11.1.3 | Anti-automation on business-critical functions | ✅ PASS | Rate limiting on payments, uploads | -| V11.1.4 | Ethical business logic verified | ⚠️ PARTIAL | Backend track metrics hidden, but API leaks followers/post likes — **HIGH-002, MEDIUM-004** | +| V11.1.1 | Business logic flow enforced server-side | ❌ FAIL | Race conditions dans payout, refund, promo, trial — **CRIT-002, CRIT-003, HIGH-006, HIGH-010** | +| V11.1.2 | Business logic limits enforced | ⚠️ PARTIAL | Rate limits existent mais pagination unbounded — **MEDIUM-005** | +| V11.1.3 | Anti-automation on business-critical functions | ⚠️ PARTIAL | Rate limiting bypassable via IP spoofing — **HIGH-001** | +| V11.1.4 | Ethical business logic verified | ⚠️ PARTIAL | Track metrics hidden, mais API leaks followers/post likes — **HIGH-002** | --- @@ -180,11 +181,11 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| -| V12.1.1 | File upload size limits | ✅ PASS | Upload validation service with size limits | +| V12.1.1 | File upload size limits | ⚠️ PARTIAL | Upload validation service OK, mais marketplace upload manque check taille — **MEDIUM-006** | | V12.1.2 | File type validation | ✅ PASS | `ValidateFile()` checks file types | | V12.1.3 | File stored outside webroot | ✅ PASS | MinIO/S3 object storage | -| V12.3.1 | User-submitted filenames sanitized | ✅ PASS | UUID-based storage paths | -| V12.4.1 | No path traversal | ✅ PASS | No user input in file system paths | +| V12.3.1 | User-submitted filenames sanitized | ❌ FAIL | Marketplace upload : path traversal via `filepath.Join` — **CRIT-005** | +| V12.4.1 | No path traversal | ❌ FAIL | `filepath.Join(previewDir, file.Filename)` sans sanitization — **CRIT-005** | --- @@ -192,13 +193,13 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| -| V13.1.1 | All API endpoints require auth or are explicitly public | ✅ PASS | Auth middleware on protected routes | +| V13.1.1 | All API endpoints require auth or are explicitly public | ⚠️ PARTIAL | Endpoints protégés, mais pprof exposé sans restriction — **MEDIUM-012** | | V13.1.2 | API uses standard auth mechanisms | ✅ PASS | JWT bearer tokens | -| V13.1.3 | No unnecessary API exposure | ✅ PASS | Metrics endpoint protected | +| V13.1.3 | No unnecessary API exposure | ⚠️ PARTIAL | pprof profiling endpoint exposé — **MEDIUM-012** | | V13.2.1 | Rate limiting on all APIs | ⚠️ PARTIAL | Rate limit bypassable via IP spoofing — **HIGH-001** | | V13.2.2 | API schema validation | ✅ PASS | Gin binding validation | -| V13.3.1 | Pagination implemented | ✅ PASS | Keyset pagination with limits | -| V13.4.1 | CORS properly configured | ⚠️ PARTIAL | HTTP origins in .env.production — **MEDIUM-003** | +| V13.3.1 | Pagination implemented | ⚠️ PARTIAL | Pagination existe mais limit non borné — **MEDIUM-005** | +| V13.4.1 | CORS properly configured | ⚠️ PARTIAL | HTTP origins in .env.production — **MEDIUM-003** ; Nginx RTMP wildcard — **MEDIUM-007** | --- @@ -206,11 +207,11 @@ | # | Contrôle | Statut | Notes | |---|----------|--------|-------| -| V14.1.1 | Server configuration hardened | ✅ PASS | Security headers (HSTS, CSP, X-Frame-Options) | +| V14.1.1 | Server configuration hardened | ⚠️ PARTIAL | Security headers backend OK, mais nginx frontend manque HSTS — **MEDIUM-009** | | V14.2.1 | Dependency integrity | ⚠️ PARTIAL | GitHub Actions not all pinned by SHA — **MEDIUM-002** | | V14.2.2 | No known vulnerabilities in dependencies | ✅ PASS | Trivy scans in CI, cargo audit, npm audit | -| V14.3.1 | Security headers set | ✅ PASS | HSTS, CSP, COEP, COOP | -| V14.4.1 | No default credentials | ✅ PASS | `.env.production` uses templates | +| V14.3.1 | Security headers set | ✅ PASS | HSTS, CSP, COEP, COOP (backend) | +| V14.4.1 | No default credentials | ⚠️ PARTIAL | `.env.production` uses templates, mais JWT_SECRET hardcodé en dev — **LOW-006** | | V14.5.1 | HTTP security headers | ✅ PASS | X-Content-Type-Options, Referrer-Policy | --- @@ -219,26 +220,30 @@ | Catégorie | Total | ✅ PASS | ⚠️ PARTIAL | ❌ FAIL | ➖ N/A | |-----------|-------|---------|------------|--------|--------| -| V1 Architecture | 6 | 5 | 1 | 0 | 0 | -| V2 Authentication | 18 | 17 | 1 | 0 | 0 | +| V1 Architecture | 6 | 3 | 3 | 0 | 0 | +| V2 Authentication | 18 | 14 | 4 | 0 | 0 | | V3 Session Mgmt | 11 | 10 | 1 | 0 | 0 | -| V4 Access Control | 7 | 6 | 1 | 0 | 0 | -| V5 Validation | 10 | 9 | 0 | 0 | 1 | -| V6 Cryptography | 7 | 7 | 0 | 0 | 0 | +| V4 Access Control | 7 | 4 | 1 | 2 | 0 | +| V5 Validation | 10 | 8 | 1 | 0 | 1 | +| V6 Cryptography | 7 | 5 | 2 | 0 | 0 | | V7 Error Handling | 7 | 7 | 0 | 0 | 0 | -| V8 Data Protection | 7 | 7 | 0 | 0 | 0 | -| V9 Communication | 4 | 4 | 0 | 0 | 0 | +| V8 Data Protection | 7 | 6 | 1 | 0 | 0 | +| V9 Communication | 4 | 3 | 1 | 0 | 0 | | V10 Malicious Code | 3 | 3 | 0 | 0 | 0 | -| V11 Business Logic | 4 | 3 | 1 | 0 | 0 | -| V12 Files | 5 | 5 | 0 | 0 | 0 | -| V13 API | 6 | 4 | 2 | 0 | 0 | -| V14 Configuration | 6 | 5 | 1 | 0 | 0 | -| **Total** | **101** | **92** | **9** | **0** | **1** | +| V11 Business Logic | 4 | 0 | 3 | 1 | 0 | +| V12 Files | 5 | 2 | 1 | 2 | 0 | +| V13 API | 7 | 2 | 5 | 0 | 0 | +| V14 Configuration | 6 | 3 | 3 | 0 | 0 | +| **Total** | **102** | **70** | **26** | **5** | **1** | -**Taux de conformité ASVS Level 2** : **92/101 (91.1%)** +**Taux de conformité ASVS Level 2** : **70/102 (68.6%)** -Les 9 contrôles partiels sont tous associés à des findings documentés dans le rapport de pentest avec des remédiations concrètes. +- 5 contrôles ❌ FAIL sont associés à des findings CRITICAL (CRIT-002/003/004/005, HIGH-003) +- 26 contrôles ⚠️ PARTIAL sont associés à des findings HIGH/MEDIUM/LOW documentés +- Tous les findings ont des remédiations concrètes dans la REMEDIATION_MATRIX + +**Objectif v1.0.0** : Résoudre tous les FAIL et réduire les PARTIAL pour atteindre ≥ 85% conformité. --- -*Checklist ASVS générée le 2026-03-13 — VEZA v0.12.6* +*Checklist ASVS générée le 2026-03-13 — VEZA v0.12.6 — Audit complet (36 findings)* diff --git a/docs/PENTEST_REPORT_VEZA_v0.12.6.md b/docs/PENTEST_REPORT_VEZA_v0.12.6.md index 6c6e55176..aebb62edf 100644 --- a/docs/PENTEST_REPORT_VEZA_v0.12.6.md +++ b/docs/PENTEST_REPORT_VEZA_v0.12.6.md @@ -1,7 +1,7 @@ # Rapport de Pentest — VEZA v0.12.6 **Date** : 2026-03-13 -**Auditeur** : Claude Code (SAST + revue manuelle de code) +**Auditeur** : Claude Opus 4.6 (SAST + revue manuelle de code + 6 agents d'audit spécialisés) **Scope** : Monorepo complet — Go backend, Rust stream server, React frontend, CI/CD, infrastructure Docker **Méthodologie** : OWASP Top 10 2021, OWASP API Security Top 10 2023, ASVS Level 2 **Référence** : ORIGIN_SECURITY_FRAMEWORK.md @@ -12,19 +12,117 @@ | Sévérité | Count | |----------|-------| -| CRITICAL | 0 | -| HIGH | 2 | -| MEDIUM | 5 | -| LOW | 4 | +| CRITICAL | 5 | +| HIGH | 10 | +| MEDIUM | 12 | +| LOW | 6 | | INFO | 3 | -| **Total findings** | **14** | -| **PASS (contrôles validés)** | **18** | +| **Total findings** | **36** | +| **PASS (contrôles validés)** | **24** | -**Posture globale** : La codebase VEZA présente une posture de sécurité **solide** pour une RC. Aucune vulnérabilité critique n'a été identifiée. Les 2 findings HIGH concernent des fuites de métriques de popularité dans l'API publique (violation éthique) et un contournement potentiel de rate limiting via IP spoofing. Les contrôles cryptographiques, l'authentification JWT, la prévention d'injection SQL et la conformité RGPD sont bien implémentés. +**Posture globale** : La codebase VEZA présente une architecture sécurisée avec d'excellentes fondations (JWT RS256, bcrypt, HMAC webhooks, rate limiting Redis). Cependant, l'audit approfondi révèle 5 vulnérabilités critiques — principalement des race conditions financières, des IDOR sur les analytics, et des WebSocket sans validation d'origine. Ces issues sont toutes remédiables sans refonte architecturale. --- -## Findings +## CRITICAL FINDINGS + +### CRIT-001 — WebSocket Cross-Site Hijacking (3 handlers) + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | CRITICAL | +| **OWASP** | A07:2021 — Cross-Site Request Forgery | +| **CWE** | CWE-346 (Origin Validation Error) | +| **Fichiers** | `chat_websocket_handler.go:50`, `playback_websocket_handler.go:106`, `co_listening_websocket_handler.go:105` | +| **CVSS** | 8.1 | + +**Description** : Les 3 handlers WebSocket utilisent `InsecureSkipVerify: true` dans `websocket.AcceptOptions`, désactivant la validation du header Origin. Un attaquant peut établir une connexion WebSocket depuis n'importe quel domaine et usurper les sessions utilisateur. + +```go +conn, err := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{ + InsecureSkipVerify: true, // VULNÉRABLE +}) +``` + +**Impact** : Cross-Site WebSocket Hijacking (CSWSH) — un attaquant peut intercepter les messages chat, contrôler la lecture audio, et accéder aux sessions d'écoute collaborative. + +**Remédiation** : Supprimer `InsecureSkipVerify: true`, implémenter `OriginPatterns` avec whitelist des domaines autorisés. + +--- + +### CRIT-002 — Payout Balance Race Condition (Double-Spend) + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | CRITICAL | +| **OWASP** | API6:2023 — Unrestricted Access to Sensitive Business Flows | +| **CWE** | CWE-362 (Race Condition) | +| **Fichier** | `veza-backend-api/internal/core/marketplace/payout.go:175-195` | +| **CVSS** | 9.1 | + +**Description** : `RequestPayout()` lit le solde hors transaction (`GetSellerBalance`), puis ouvre une transaction pour déduire. Entre la lecture et la déduction, une requête concurrente peut aussi lire le même solde et traiter un deuxième payout. + +**Scénario d'attaque** : Seller avec $100 → 2 requêtes simultanées → les 2 lisent $100 → les 2 traitent $100 → **$200 payés pour $100 de solde**. + +**Remédiation** : Ajouter `SELECT FOR UPDATE` (`clause.Locking{Strength: "UPDATE"}`) sur la ligne de balance DANS la transaction, avant la validation du montant. + +--- + +### CRIT-003 — Refund Double-Pay Race Condition + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | CRITICAL | +| **CWE** | CWE-362 | +| **Fichier** | `marketplace/service.go:1136-1189` | +| **CVSS** | 8.7 | + +**Description** : `RefundOrder()` lit l'order hors transaction, vérifie l'autorisation, puis appelle `rp.Refund()`. Deux requêtes concurrentes peuvent toutes les deux passer la validation et déclencher un double remboursement. + +**Remédiation** : Wrapper la lecture + validation + refund dans une seule transaction avec `SELECT FOR UPDATE` sur l'order. Vérifier `order.Status != "refunded"` atomiquement. + +--- + +### CRIT-004 — Track Analytics IDOR (No Ownership Check) + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | CRITICAL | +| **OWASP** | API1:2023 — Broken Object Level Authorization | +| **CWE** | CWE-639 (Authorization Bypass) | +| **Fichier** | `analytics/handler.go:727-777` | +| **CVSS** | 7.5 | + +**Description** : `GET /api/v1/analytics/tracks/:id` retourne les analytics détaillées (play count, completion rate, listener data) de n'importe quelle track sans vérifier que l'utilisateur authentifié en est le créateur. + +**Impact** : Tout utilisateur authentifié peut espionner les analytics privées de n'importe quel créateur. + +**Remédiation** : Ajouter une vérification `track.CreatorID == userID` avant de retourner les analytics. + +--- + +### CRIT-005 — Path Traversal in Marketplace Preview Upload + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | CRITICAL | +| **CWE** | CWE-22 (Path Traversal) | +| **Fichier** | `handlers/marketplace.go:285-290` | +| **CVSS** | 8.8 | + +**Description** : Le nom de fichier uploadé est utilisé directement dans `filepath.Join` sans sanitization : + +```go +destPath := filepath.Join(previewDir, file.Filename) // VULNÉRABLE +``` + +Un attaquant peut uploader un fichier avec `../../../etc/cron.d/evil` pour écrire n'importe où sur le filesystem. + +**Remédiation** : Utiliser `filepath.Base(file.Filename)` ou générer un UUID comme nom de fichier. + +--- + +## HIGH FINDINGS ### HIGH-001 — IP Spoofing via X-Forwarded-For (Rate Limit Bypass) @@ -32,23 +130,12 @@ |-------|--------| | **Sévérité** | HIGH | | **OWASP** | API4:2023 — Unrestricted Resource Consumption | -| **Fichier** | `veza-backend-api/internal/handlers/common.go:601-610` | -| **CVSS** | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | +| **Fichier** | `handlers/common.go:601-610` | +| **CVSS** | 7.5 | -**Description** : La méthode `GetClientIP()` lit directement le header `X-Forwarded-For` sans passer par `c.ClientIP()` de Gin, qui respecte `Engine.SetTrustedProxies`. Un attaquant peut injecter un faux header X-Forwarded-For pour contourner les rate limits basés sur l'IP. +**Description** : `GetClientIP()` lit directement `X-Forwarded-For` sans passer par `c.ClientIP()` de Gin. Permet le contournement du rate limiting. -```go -// VULNÉRABLE (common.go:604) -if ip := c.GetHeader("X-Forwarded-For"); ip != "" { - return strings.Split(ip, ",")[0] -} -``` - -**Impact** : Contournement complet du rate limiting pour les endpoints non authentifiés (login, register, password reset). Permet des attaques brute-force sur les credentials. - -**Remédiation** : Remplacer par `c.ClientIP()` et configurer `gin.Engine.SetTrustedProxies()` avec les IPs des reverse proxies de confiance. - -**Note** : Le middleware `metrics_protection.go:51-53` utilise correctement `c.ClientIP()` — le fix est déjà documenté dans un commentaire SECURITY(MEDIUM-002). +**Remédiation** : Remplacer par `c.ClientIP()` + configurer `SetTrustedProxies()`. --- @@ -57,215 +144,197 @@ if ip := c.GetHeader("X-Forwarded-For"); ip != "" { | Champ | Valeur | |-------|--------| | **Sévérité** | HIGH | -| **OWASP** | Business Logic Flaw | -| **Fichiers** | `user_service.go:75`, `social_service.go:321`, `types/stats.go:31` | -| **Réf. ORIGIN** | ORIGIN_UI_UX_SYSTEM.md §13.4, §14.2 | +| **Fichiers** | `user_service.go:75`, `social_service.go:321`, `types/stats.go:31`, `social/models.go:32` | -**Description** : Les endpoints publics de profil utilisateur (`GET /api/v1/users/:id`, `GET /api/v1/users/by-username/:username`) retournent `followers_count` et `following_count` dans la réponse JSON via `PublicUserResponse`. L'endpoint `GET /api/v1/users/suggestions` retourne aussi `followers_count` via `SuggestionUser`. +**Description** : `followers_count`, `following_count` exposés dans les réponses publiques (`PublicUserResponse`, `SuggestionUser`). `like_count` exposé sur le modèle `Post` social. -Ceci viole le principe éthique fondamental de VEZA : **les métriques de popularité ne doivent JAMAIS être exposées publiquement**. - -**Impact** : Permet le classement social des utilisateurs, contredit les engagements éthiques de la plateforme (privacy policy §4, ORIGIN §13.4). - -**Remédiation** : -- Supprimer `followers_count` et `following_count` de `PublicUserResponse` -- Supprimer `followers_count` de `SuggestionUser` -- Exposer ces métriques uniquement dans le dashboard créateur privé (derrière auth + ownership check) +**Remédiation** : Supprimer ces champs des réponses publiques. Les exposer uniquement dans le dashboard privé. --- -### MEDIUM-001 — Incohérence du coût bcrypt +### HIGH-003 — Mass Assignment / Privilege Escalation (UpdateUser) | Champ | Valeur | |-------|--------| -| **Sévérité** | MEDIUM | -| **OWASP** | A02:2021 — Cryptographic Failures | -| **Fichier** | `veza-backend-api/internal/core/auth/service.go:156, :986` | +| **Sévérité** | HIGH | +| **CWE** | CWE-915 (Mass Assignment) | +| **Fichier** | `api/user/handler.go:75-95`, `api/user/service.go:188-267` | -**Description** : Le service d'authentification utilise `bcrypt.DefaultCost` (10) pour le hachage des mots de passe lors de l'inscription et du reset, tandis que `password_service.go` utilise correctement `bcryptCost = 12`. +**Description** : `UpdateMe()` appelle `UpdateUser()` qui accepte des champs `Role`, `IsVerified`, `IsActive`. Un utilisateur non-admin pourrait escalader ses privilèges en envoyant `{"role": "admin"}`. -```go -// auth/service.go:156 — coût 10 -hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - -// password_service.go:22 — coût 12 -const bcryptCost = 12 -``` - -**Impact** : Les mots de passe créés via `Register()` ou `ResetPasswordWithToken()` sont 4x plus rapides à brute-forcer que ceux créés via `PasswordService.ChangePassword()`. - -**Remédiation** : Remplacer `bcrypt.DefaultCost` par la constante `bcryptCost` (12) dans auth/service.go, ou centraliser le hachage dans PasswordService. +**Remédiation** : Filtrer les champs autorisés par rôle. Seuls les admins peuvent modifier `role`, `is_verified`, `is_active`. --- -### MEDIUM-002 — GitHub Actions non épinglées par SHA +### HIGH-004 — RTMP Callback Authentication Faible | Champ | Valeur | |-------|--------| -| **Sévérité** | MEDIUM | -| **OWASP** | A08:2021 — Software and Data Integrity Failures | -| **Fichiers** | `sast.yml`, `security-scan.yml`, `cd.yml`, `stream-ci.yml`, `container-scan.yml`, `staging-validation.yml` | +| **Sévérité** | HIGH | +| **CWE** | CWE-345 | +| **Fichier** | `handlers/live_stream_callback.go:26-84` | -**Description** : 12+ actions GitHub sont référencées par tag (`@v3`, `@v2`, `@master`) au lieu de SHA complet. Un compromission du repo upstream permettrait l'injection de code malveillant dans le pipeline CI/CD. +**Description** : Les callbacks Nginx-RTMP utilisent un simple header secret statique (`X-RTMP-Callback-Secret`) sans HMAC, sans timestamp, sans protection contre le replay. -Actions concernées : -- `github/codeql-action/*@v3` (3 usages) -- `gitleaks/gitleaks-action@v2` -- `docker/setup-buildx-action@v3` (2 usages) -- `aquasecurity/trivy-action@0.28.0` et `@master` (5 usages) -- `sigstore/cosign-installer@v3` -- `actions-rust-lang/audit@v1` - -**Impact** : Supply chain attack sur le pipeline CI/CD. Exfiltration de secrets, injection de code dans les builds. - -**Remédiation** : Épingler toutes les actions par SHA (déjà fait pour `actions/checkout`, `actions/setup-go`, etc.). Utiliser Dependabot pour les mises à jour. +**Remédiation** : Implémenter HMAC-SHA256 avec timestamp + body dans la signature. --- +### HIGH-005 — Refresh Token TOCTOU Race Condition + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | HIGH | +| **CWE** | CWE-362 | +| **Fichier** | `auth/service.go:653-707` | + +**Description** : Entre `Validate()` et `Rotate()` du refresh token, une requête concurrente peut réutiliser le même token. Le `RefreshLock` (line 665-671) réduit la fenêtre mais ne l'élimine pas. + +**Remédiation** : Combiner validate + rotate dans une seule transaction DB atomique. + +--- + +### HIGH-006 — Promo Code Counter Race Condition + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | HIGH | +| **CWE** | CWE-362 | +| **Fichier** | `marketplace/service.go:464, 764-787` | + +**Description** : Le `used_count` du promo code est incrémenté APRÈS validation mais sans garantie atomique. Deux requêtes simultanées peuvent dépasser `MaxUses`. + +**Remédiation** : Effectuer validation et incrémentation dans la même transaction avec `SELECT FOR UPDATE`. + +--- + +### HIGH-007 — Stream Token Replay Attack (Rust) + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | HIGH | +| **CWE** | CWE-433 | +| **Fichier** | `veza-stream-server/src/auth/token_validator.rs:100-138` | + +**Description** : Les URLs de streaming signées avec HMAC-SHA256 vérifient l'expiration mais pas la réutilisation. Un attaquant peut rejouer une URL valide multiple fois avant expiration, gonflant artificiellement les analytics. + +**Remédiation** : Ajouter un nonce ou stocker les tokens consommés dans Redis avec TTL. + +--- + +### HIGH-008 — Account Deletion Missing Financial Data Cascade + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | HIGH | +| **Fichier** | `handlers/account_deletion_handler.go:75-135` | + +**Description** : La suppression de compte anonymise l'utilisateur mais ne traite pas : orders (buyer), licenses, seller payouts, seller balance, seller transfers. + +**Remédiation** : Ajouter le nettoyage GDPR pour les données financières (anonymiser plutôt que supprimer pour les obligations légales). + +--- + +### HIGH-009 — Webhook Amount Not Re-Validated + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | HIGH | +| **CWE** | CWE-1025 | +| **Fichier** | `marketplace/service.go:610-676` | + +**Description** : `ProcessPaymentWebhook()` ne vérifie pas que le montant payé (Hyperswitch) correspond au total de l'order. Un webhook forgé pourrait confirmer un paiement inférieur. + +**Remédiation** : Comparer `order.TotalAmount` avec le montant du webhook avant de traiter. + +--- + +### HIGH-010 — Free Trial Reuse Not Atomic + +| Champ | Valeur | +|-------|--------| +| **Sévérité** | HIGH | +| **CWE** | CWE-362 | +| **Fichier** | `subscription/service.go:237-251` | + +**Description** : La vérification du trial gratuit (`previousTrialCount`) est hors transaction. Deux requêtes concurrentes peuvent toutes les deux voir `count = 0` et obtenir un trial. + +**Remédiation** : Déplacer la vérification dans la transaction avec `SELECT FOR UPDATE` sur le user. + +--- + +## MEDIUM FINDINGS + +### MEDIUM-001 — Incohérence bcrypt cost (10 vs 12) +**Fichier** : `auth/service.go:156, :986` | `bcrypt.DefaultCost` au lieu de 12 + +### MEDIUM-002 — GitHub Actions non épinglées par SHA (12+ actions) +**Fichiers** : `sast.yml`, `security-scan.yml`, `cd.yml`, `stream-ci.yml`, `container-scan.yml` + ### MEDIUM-003 — CORS .env.production avec HTTP +**Fichier** : `.env.production:40` -| Champ | Valeur | -|-------|--------| -| **Sévérité** | MEDIUM | -| **OWASP** | A05:2021 — Security Misconfiguration | -| **Fichier** | `veza-backend-api/.env.production:40` | +### MEDIUM-004 — MinIO bucket publicly readable +**Fichier** : `docker-compose.yml:296` — `mc anonymous set download veza/veza-files/public` -**Description** : Le fichier `.env.production` contient des origines CORS en HTTP avec port 5173 (dev) : +### MEDIUM-005 — Unbounded pagination (multiple handlers) +**Fichiers** : `api/user/handler.go`, `core/admin/handler.go`, et ~15 autres endpoints sans `max(limit, 100)` -``` -CORS_ALLOWED_ORIGINS=http://veza.com:5173,http://veza.talas.fr:5173,... -``` +### MEDIUM-006 — File upload size validation missing (marketplace) +**Fichier** : `handlers/marketplace.go:270-289` — pas de vérification `file.Size` -Bien qu'un commentaire indique les valeurs HTTPS pour la production, le fichier pourrait être déployé tel quel. +### MEDIUM-007 — Nginx RTMP wildcard CORS +**Fichier** : `infra/nginx-rtmp/nginx.conf:46` — `Access-Control-Allow-Origin: *` -**Impact** : En cas de déploiement accidentel, les cookies auth pourraient être envoyés sur des connexions HTTP non chiffrées. +### MEDIUM-008 — Redis unprotected in development +**Fichier** : `docker-compose.yml:35-56` — pas de `--requirepass` en dev -**Remédiation** : Renommer en `.env.development` ou mettre les valeurs HTTPS par défaut avec override via variables d'environnement. +### MEDIUM-009 — Nginx production missing HSTS header +**Fichier** : `apps/web/nginx.production.conf:11-14` + +### MEDIUM-010 — WebSocket message size not limited (playback) +**Fichier** : `playback_websocket_handler.go:138-152` — pas de `conn.SetReadLimit()` + +### MEDIUM-011 — License download missing expiration check +**Fichier** : `marketplace/service.go:806-840` — `expires_at` non vérifié + +### MEDIUM-012 — pprof profiling endpoint exposed +**Fichier** : `cmd/api/main.go:8` — `import _ "net/http/pprof"` sans restriction d'accès --- -### MEDIUM-004 — Social Post like_count exposé publiquement - -| Champ | Valeur | -|-------|--------| -| **Sévérité** | MEDIUM | -| **OWASP** | Business Logic Flaw | -| **Fichier** | `veza-backend-api/internal/core/social/models.go:32` | - -**Description** : Le modèle `Post` (social) expose `like_count` avec `json:"like_count"` sans tag `json:"-"`. Les posts sociaux sérialisés dans les réponses API incluent le nombre de likes. - -Contrairement au modèle `Track` qui a correctement `json:"-"` sur `PlayCount` et `LikeCount`, les posts sociaux n'ont pas cette protection. - -**Impact** : Incohérence avec la politique éthique de non-exposition des métriques de popularité. - -**Remédiation** : Ajouter `json:"-"` au champ `LikeCount` du modèle `Post`, ou le rendre visible uniquement à l'auteur du post. - ---- - -### MEDIUM-005 — Dashboard TrackPreview expose play_count/like_count sans ownership check granulaire - -| Champ | Valeur | -|-------|--------| -| **Sévérité** | MEDIUM | -| **OWASP** | API1:2023 — Broken Object Level Authorization | -| **Fichier** | `veza-backend-api/internal/handlers/dashboard.go:149-150` | - -**Description** : La struct `TrackPreview` expose `play_count` et `like_count` en JSON. L'endpoint dashboard (`GET /api/v1/dashboard`) est protégé par auth et retourne les tracks du user authentifié — donc c'est le créateur qui voit ses propres métriques. Cependant, si la struct `TrackPreview` est réutilisée ailleurs, les métriques fuiteraient. - -**Impact** : Risque faible actuellement (endpoint bien protégé), mais architecture fragile. - -**Remédiation** : Créer un type `TrackPreviewPublic` sans métriques pour les contextes publics, garder `TrackPreview` avec métriques uniquement pour le dashboard privé. - ---- +## LOW FINDINGS ### LOW-001 — Cookie SameSite=Lax au lieu de Strict +**Fichier** : `config/config.go:138` -| Champ | Valeur | -|-------|--------| -| **Sévérité** | LOW | -| **Fichier** | `veza-backend-api/internal/config/config.go:138` | - -**Description** : Les cookies d'authentification utilisent `SameSite=Lax` au lieu de `Strict`. Cela permet l'envoi de cookies lors de navigations top-level depuis des sites tiers (par exemple, un lien depuis un email de phishing). - -**Impact** : Risque CSRF résiduel pour les requêtes GET qui modifient l'état (normalement aucune dans une API REST bien conçue). - -**Remédiation** : Passer à `SameSite=Strict` si aucun flow OAuth/redirect externe ne nécessite `Lax`. - ---- - -### LOW-002 — CASCADE DELETE extensif dans les migrations - -| Champ | Valeur | -|-------|--------| -| **Sévérité** | LOW | -| **Fichier** | `veza-backend-api/migrations/*.sql` (30+ occurrences) | - -**Description** : La quasi-totalité des clés étrangères utilisent `ON DELETE CASCADE`. En cas de suppression accidentelle d'un utilisateur, toutes ses données associées (tracks, messages, playlists, orders, etc.) sont supprimées en cascade. - -**Impact** : Perte de données irréversible en cas d'erreur admin ou de bug dans la logique de suppression. - -**Remédiation** : Utiliser `ON DELETE RESTRICT` ou `ON DELETE SET NULL` pour les entités business critiques (orders, payments). Implémenter le soft delete systématiquement. - ---- +### LOW-002 — CASCADE DELETE extensif (30+ migrations) +**Fichier** : `migrations/*.sql` ### LOW-003 — utils.HashPassword utilise bcrypt.DefaultCost - -| Champ | Valeur | -|-------|--------| -| **Sévérité** | LOW | -| **Fichier** | `veza-backend-api/internal/utils/utils.go:57` | - -**Description** : La fonction utilitaire `HashPassword()` utilise `bcrypt.DefaultCost` (10). Si elle est appelée en production (au lieu de `PasswordService`), le coût sera inférieur au standard de 12. - -**Remédiation** : Aligner sur `bcryptCost = 12` ou supprimer cette fonction si elle n'est pas utilisée en production. - ---- +**Fichier** : `utils/utils.go:57` ### LOW-004 — Staging validation workflow actions non épinglées +**Fichier** : `staging-validation.yml:35` -| Champ | Valeur | -|-------|--------| -| **Sévérité** | LOW | -| **Fichier** | `.github/workflows/staging-validation.yml:35` | +### LOW-005 — Docker base images use :latest (dev Dockerfiles) +**Fichiers** : `veza-backend-api/Dockerfile:28`, `veza-stream-server/Dockerfile:29` -**Description** : `docker/setup-buildx-action@v3` non épinglé par SHA dans le workflow de staging. - -**Remédiation** : Épingler par SHA comme les autres actions. +### LOW-006 — JWT_SECRET hardcoded fallback in docker-compose +**Fichier** : `docker-compose.yml:171` — `dev-secret-key-minimum-32-characters-long` --- +## INFO FINDINGS + ### INFO-001 — Binary database dump dans le repo +**Fichier** : `veza-backend-api/veza_back_api_db/` -| Champ | Valeur | -|-------|--------| -| **Sévérité** | INFO | -| **Fichier** | `veza-backend-api/veza_back_api_db/` | +### INFO-002 — hash_gen/create_test_user tools use bcrypt.DefaultCost +**Fichiers** : `cmd/tools/hash_gen/main.go:11`, `cmd/tools/create_test_user/main.go:54,72` -**Description** : Un dump de base de données binaire est présent dans le repo. Bien que les données semblent être de développement, des informations sensibles (hashes, tokens) peuvent s'y trouver. - -**Remédiation** : Ajouter ce répertoire au `.gitignore` et supprimer de l'historique git avec `git filter-branch` ou BFG Repo-Cleaner. - ---- - -### INFO-002 — hash_gen tool utilise bcrypt.DefaultCost - -| Champ | Valeur | -|-------|--------| -| **Sévérité** | INFO | -| **Fichier** | `veza-backend-api/cmd/tools/hash_gen/main.go:11` | - -**Description** : L'outil CLI `hash_gen` utilise `bcrypt.DefaultCost`. Comme c'est un outil de développement, l'impact est nul, mais il serait bon d'aligner pour la cohérence. - ---- - -### INFO-003 — create_test_user tool utilise bcrypt.DefaultCost - -| Champ | Valeur | -|-------|--------| -| **Sévérité** | INFO | -| **Fichier** | `veza-backend-api/cmd/tools/create_test_user/main.go:54,72` | - -**Description** : Même constat que INFO-002 pour l'outil de création d'utilisateurs test. +### INFO-003 — No JWT key rotation mechanism +**Fichier** : `services/jwt_service.go` — pas de `kid` header, pas de support multi-clés --- @@ -273,36 +342,43 @@ Contrairement au modèle `Track` qui a correctement `json:"-"` sur `PlayCount` e | # | Contrôle | Statut | Preuve | |---|----------|--------|--------| -| P01 | Injection SQL | ✅ PASS | Toutes les requêtes db.Raw() utilisent des placeholders `?`. Aucune concaténation de chaînes dans les requêtes SQL. | -| P02 | Injection de commande | ✅ PASS | Aucun usage de `exec.Command`, `os/exec`, ou équivalent avec input utilisateur. | -| P03 | JWT RS256 en production | ✅ PASS | `jwt_service.go` : RS256 primary, HS256 fallback dev only. | -| P04 | Secrets non exposés | ✅ PASS | PasswordHash, tokens, secrets MFA ont `json:"-"`. API keys hashées. | -| P05 | crypto/rand (pas math/rand) | ✅ PASS | 19 fichiers utilisent `crypto/rand`. Aucun usage de `math/rand` pour la sécurité. | -| P06 | Webhook HMAC-SHA512 | ✅ PASS | Vérification HMAC correcte avec `hmac.Equal()` (timing-safe). | -| P07 | Race condition prevention | ✅ PASS | `SELECT FOR UPDATE` utilisé pour les opérations concurrentes sur les soldes. | -| P08 | Cookie HttpOnly | ✅ PASS | `CookieHttpOnly: true` pour access_token et refresh_token. | -| P09 | Cookie Secure auto-detect | ✅ PASS | `getCookieSecure()` retourne true en production automatiquement. | -| P10 | Track metrics hidden (backend) | ✅ PASS | `track.go:42-43` : PlayCount et LikeCount ont `json:"-"`. | -| P11 | Rust safety | ✅ PASS | Aucun bloc `unsafe` dans `veza-stream-server/src/`. | -| P12 | GDPR export | ✅ PASS | `gdpr_export_handler.go` + tests E2E. | -| P13 | GDPR deletion | ✅ PASS | `account_deletion_handler.go` : anonymisation + hard delete 30j. | -| P14 | Password validation | ✅ PASS | Longueur min, complexité, historique des 5 derniers, max 72 bytes (bcrypt limit). | -| P15 | Rate limiting | ✅ PASS | Middleware rate limit configuré sur les endpoints sensibles. | -| P16 | Security headers | ✅ PASS | HSTS, CSP, X-Frame-Options, COEP/COOP configurés. | -| P17 | Container scanning | ✅ PASS | Trivy scans dans CI/CD (cd.yml, container-scan.yml). | -| P18 | Secret scanning | ✅ PASS | Gitleaks action dans security-scan.yml. | +| P01 | Injection SQL | ✅ PASS | Toutes les requêtes utilisent des placeholders. ORDER BY protégé par whitelist. | +| P02 | Injection de commande | ✅ PASS | ClamAV et FFmpeg via `exec.CommandContext` avec arguments séparés. | +| P03 | JWT RS256 en production | ✅ PASS | RS256 primary, HS256 dev-only fallback. Algorithm validation empêche `alg: none`. | +| P04 | Secrets non exposés | ✅ PASS | `json:"-"` sur passwords, tokens, secrets MFA, API keys hashées. | +| P05 | crypto/rand (pas math/rand) | ✅ PASS | 19 fichiers utilisent `crypto/rand`. | +| P06 | Webhook HMAC-SHA512 | ✅ PASS | `hmac.Equal()` timing-safe. | +| P07 | Cookie HttpOnly | ✅ PASS | `CookieHttpOnly: true`. | +| P08 | Cookie Secure auto-detect | ✅ PASS | `getCookieSecure()` retourne true en production. | +| P09 | Track metrics hidden (backend) | ✅ PASS | `PlayCount` et `LikeCount` ont `json:"-"` sur le modèle Track. | +| P10 | Rust memory safety | ✅ PASS | Aucun bloc `unsafe`. | +| P11 | GDPR export | ✅ PASS | Endpoint fonctionnel, rate limité (3/24h). | +| P12 | GDPR deletion flow | ✅ PASS | Anonymisation + hard delete 30j. | +| P13 | Password validation | ✅ PASS | Complexité, historique 5 derniers, max 72 bytes. | +| P14 | Account lockout | ✅ PASS | 5 attempts → 30min lockout, Redis + fallback in-memory. | +| P15 | CSRF protection | ✅ PASS | 32-byte tokens, `subtle.ConstantTimeCompare`, Redis storage. | +| P16 | Security headers (backend) | ✅ PASS | HSTS, CSP, X-Frame-Options, COEP/COOP. | +| P17 | Container scanning | ✅ PASS | Trivy scans dans CI/CD. | +| P18 | Secret scanning | ✅ PASS | Gitleaks action. | +| P19 | OAuth PKCE | ✅ PASS | RFC 7636 implémenté (code_verifier + S256 challenge). | +| P20 | OAuth state parameter | ✅ PASS | Généré avec crypto/rand, validé et consommé. | +| P21 | OAuth redirect whitelist | ✅ PASS | URL validée contre whitelist configurée. | +| P22 | Chat room membership | ✅ PASS | `CanRead()`, `CanSend()`, `CanJoin()` vérifient l'appartenance. | +| P23 | Chat XSS protection | ✅ PASS | DOMPurify (frontend) + `html.EscapeString` (backend). | +| P24 | Chat rate limiting | ✅ PASS | Redis sliding window, 10 msg/sec, fallback in-memory. | --- ## Méthodologie -### Outils et techniques -- **SAST** : Analyse statique manuelle du code source (Go, Rust, TypeScript) -- **Pattern matching** : Recherche de patterns vulnérables (SQL concat, exec.Command, math/rand, unsafe, etc.) -- **Configuration review** : Analyse des fichiers .env, Docker, CI/CD, CORS -- **Architecture review** : Vérification de l'isolation des services, des contrôles d'accès, du flux de données -- **Dependency analysis** : Revue des actions GitHub, crates Rust, packages npm -- **ORIGIN compliance** : Vérification de la conformité avec les spécifications ORIGIN (éthique, sécurité, business logic) +### Agents d'audit spécialisés +6 agents parallèles ont couvert les domaines suivants : +1. **Auth & JWT** — JWT, bcrypt, sessions, cookies, OAuth, MFA, lockout +2. **API IDOR & Access Control** — Authorization bypass, ownership checks, RBAC +3. **Input Validation & Injection** — SQL, XSS, command, path traversal, file upload +4. **Infrastructure & Docker** — Dockerfiles, docker-compose, CI/CD, secrets, CORS +5. **Business Logic & Payments** — Payments, payouts, GDPR, subscriptions, marketplace +6. **WebSocket, Rust & Dependencies** — WS security, memory safety, SCA ### Scope couvert | Composant | Fichiers analysés | @@ -311,10 +387,11 @@ Contrairement au modèle `Track` qui a correctement `json:"-"` sur `PlayCount` e | Rust stream server | ~15 fichiers .rs | | React frontend | ~50 composants clés | | CI/CD | 11 workflows GitHub Actions | -| Docker | 3 Dockerfiles, 3 docker-compose | +| Docker | 5 Dockerfiles, 5 docker-compose | | Migrations | 30+ fichiers SQL | -| Configuration | .env.*, configs/ | +| Configuration | .env.*, configs/, nginx | --- *Rapport généré le 2026-03-13 — VEZA v0.12.6 Pentest Security Audit* +*Auditeur : Claude Opus 4.6 + 6 agents spécialisés* diff --git a/docs/REMEDIATION_MATRIX_v0.12.6.md b/docs/REMEDIATION_MATRIX_v0.12.6.md index 4190b7767..822240fc7 100644 --- a/docs/REMEDIATION_MATRIX_v0.12.6.md +++ b/docs/REMEDIATION_MATRIX_v0.12.6.md @@ -1,7 +1,7 @@ # Matrice de Remédiation — VEZA v0.12.6 **Date** : 2026-03-13 -**Référence** : PENTEST_REPORT_VEZA_v0.12.6.md +**Référence** : PENTEST_REPORT_VEZA_v0.12.6.md (36 findings) --- @@ -11,16 +11,36 @@ Les actions sont classées par **priorité d'implémentation** (combinaison sév --- +## Actions Critiques (P0 — Immédiat, avant tout déploiement) + +| ID | Finding | Sévérité | Fichier(s) | Action | Effort | +|----|---------|----------|------------|--------|--------| +| REM-001 | CRIT-001 | CRITICAL | `chat_websocket_handler.go:50`, `playback_websocket_handler.go:106`, `co_listening_websocket_handler.go:105` | Supprimer `InsecureSkipVerify: true`. Implémenter `OriginPatterns` avec whitelist des domaines autorisés. | 1h | +| REM-002 | CRIT-002 | CRITICAL | `marketplace/payout.go:175-195` | Déplacer `GetSellerBalance` DANS la transaction avec `SELECT FOR UPDATE` (`clause.Locking{Strength: "UPDATE"}`) avant validation du montant. | 2h | +| REM-003 | CRIT-003 | CRITICAL | `marketplace/service.go:1136-1189` | Wrapper lecture + validation + refund dans une seule transaction avec `SELECT FOR UPDATE` sur l'order. Vérifier `order.Status != "refunded"` atomiquement. | 2h | +| REM-004 | CRIT-004 | CRITICAL | `analytics/handler.go:727-777` | Ajouter vérification `track.CreatorID == userID` avant de retourner les analytics. Retourner 403 sinon. | 30min | +| REM-005 | CRIT-005 | CRITICAL | `handlers/marketplace.go:285-290` | Remplacer `filepath.Join(previewDir, file.Filename)` par `filepath.Join(previewDir, filepath.Base(file.Filename))` ou générer un UUID. | 15min | + +**Effort total P0** : ~6h + +--- + ## Actions Prioritaires (P1 — Avant release v1.0.0) -| ID | Finding | Sévérité | Fichier(s) | Action | Effort | Responsable | -|----|---------|----------|------------|--------|--------|-------------| -| REM-001 | HIGH-001 | HIGH | `handlers/common.go:601-610` | Remplacer `c.GetHeader("X-Forwarded-For")` par `c.ClientIP()` + configurer `SetTrustedProxies()` | 1h | Backend | -| REM-002 | HIGH-002 | HIGH | `user_service.go:75`, `social_service.go:321`, `types/stats.go:31` | Supprimer `followers_count`/`following_count` de `PublicUserResponse` et `SuggestionUser`. Exposer uniquement dans le dashboard privé. | 2h | Backend | -| REM-003 | MEDIUM-001 | MEDIUM | `auth/service.go:156, :986` | Remplacer `bcrypt.DefaultCost` par `bcryptCost` (12) importé depuis `password_service.go` ou une constante partagée | 30min | Backend | -| REM-004 | MEDIUM-004 | MEDIUM | `social/models.go:32` | Ajouter `json:"-"` sur `LikeCount` du modèle `Post` ou créer une DTO publique sans ce champ | 30min | Backend | +| ID | Finding | Sévérité | Fichier(s) | Action | Effort | +|----|---------|----------|------------|--------|--------| +| REM-006 | HIGH-001 | HIGH | `handlers/common.go:601-610` | Remplacer `c.GetHeader("X-Forwarded-For")` par `c.ClientIP()` + configurer `SetTrustedProxies()` | 1h | +| REM-007 | HIGH-002 | HIGH | `user_service.go:75`, `social_service.go:321`, `types/stats.go:31`, `social/models.go:32` | Supprimer `followers_count`/`following_count` de `PublicUserResponse` et `SuggestionUser`. Ajouter `json:"-"` sur `LikeCount` du modèle `Post`. | 2h | +| REM-008 | HIGH-003 | HIGH | `api/user/handler.go:75-95`, `api/user/service.go:188-267` | Filtrer les champs autorisés par rôle dans `UpdateMe()`. Seuls les admins peuvent modifier `role`, `is_verified`, `is_active`. | 1h | +| REM-009 | HIGH-004 | HIGH | `handlers/live_stream_callback.go:26-84` | Implémenter HMAC-SHA256 avec timestamp + body dans la signature RTMP callback. | 2h | +| REM-010 | HIGH-005 | HIGH | `auth/service.go:653-707` | Combiner validate + rotate du refresh token dans une seule transaction DB atomique. | 2h | +| REM-011 | HIGH-006 | HIGH | `marketplace/service.go:464, 764-787` | Effectuer validation et incrémentation `used_count` du promo code dans la même transaction avec `SELECT FOR UPDATE`. | 1h | +| REM-012 | HIGH-007 | HIGH | `veza-stream-server/src/auth/token_validator.rs:100-138` | Ajouter un nonce ou stocker les tokens consommés dans Redis avec TTL pour empêcher le replay. | 3h | +| REM-013 | HIGH-008 | HIGH | `handlers/account_deletion_handler.go:75-135` | Ajouter nettoyage GDPR pour orders (buyer), licenses, seller payouts, seller balance, seller transfers. Anonymiser plutôt que supprimer. | 3h | +| REM-014 | HIGH-009 | HIGH | `marketplace/service.go:610-676` | Dans `ProcessPaymentWebhook()`, comparer `order.TotalAmount` avec le montant reçu du webhook avant traitement. | 1h | +| REM-015 | HIGH-010 | HIGH | `subscription/service.go:237-251` | Déplacer la vérification du trial gratuit (`previousTrialCount`) dans la transaction avec `SELECT FOR UPDATE` sur le user. | 1h | -**Effort total P1** : ~4h +**Effort total P1** : ~17h --- @@ -28,13 +48,20 @@ Les actions sont classées par **priorité d'implémentation** (combinaison sév | ID | Finding | Sévérité | Fichier(s) | Action | Effort | |----|---------|----------|------------|--------|--------| -| REM-005 | MEDIUM-002 | MEDIUM | `sast.yml`, `security-scan.yml`, `cd.yml`, `stream-ci.yml`, `container-scan.yml`, `staging-validation.yml` | Épingler les 12+ GitHub Actions par SHA. Configurer Dependabot pour les mises à jour. | 2h | -| REM-006 | MEDIUM-003 | MEDIUM | `.env.production:40` | Renommer en `.env.local` ou `.env.development`. Mettre les valeurs HTTPS par défaut. Ajouter validation au startup. | 1h | -| REM-007 | MEDIUM-005 | MEDIUM | `handlers/dashboard.go:149-150` | Créer `TrackPreviewPublic` (sans métriques) et `TrackPreviewPrivate` (avec métriques). Utiliser le bon type selon le contexte. | 1h | -| REM-008 | LOW-001 | LOW | `config/config.go:138` | Passer `CookieSameSite` de `lax` à `strict` si pas de flow OAuth redirect cross-site | 30min | -| REM-009 | LOW-003 | LOW | `utils/utils.go:57` | Aligner `HashPassword()` sur `bcryptCost = 12` ou la supprimer si non utilisée en prod | 15min | +| REM-016 | MEDIUM-001 | MEDIUM | `auth/service.go:156, :986` | Remplacer `bcrypt.DefaultCost` par `bcryptCost` (12) importé depuis `password_service.go` ou une constante partagée. | 30min | +| REM-017 | MEDIUM-002 | MEDIUM | `sast.yml`, `security-scan.yml`, `cd.yml`, `stream-ci.yml`, `container-scan.yml`, `staging-validation.yml` | Épingler les 12+ GitHub Actions par SHA. Configurer Dependabot pour les mises à jour. | 2h | +| REM-018 | MEDIUM-003 | MEDIUM | `.env.production:40` | Renommer en `.env.local` ou `.env.development`. Mettre les valeurs HTTPS par défaut. Ajouter validation au startup. | 1h | +| REM-019 | MEDIUM-004 | MEDIUM | `docker-compose.yml:296` | Supprimer `mc anonymous set download`. Configurer des politiques MinIO par bucket avec accès authenticated uniquement. | 30min | +| REM-020 | MEDIUM-005 | MEDIUM | `api/user/handler.go`, `core/admin/handler.go`, ~15 handlers | Ajouter `max(limit, 100)` sur tous les endpoints paginés. Créer un helper `SanitizePagination()`. | 2h | +| REM-021 | MEDIUM-006 | MEDIUM | `handlers/marketplace.go:270-289` | Ajouter vérification `file.Size` avant sauvegarde dans le handler marketplace upload. | 15min | +| REM-022 | MEDIUM-007 | MEDIUM | `infra/nginx-rtmp/nginx.conf:46` | Remplacer `Access-Control-Allow-Origin: *` par la whitelist de domaines autorisés. | 15min | +| REM-023 | MEDIUM-008 | MEDIUM | `docker-compose.yml:35-56` | Ajouter `--requirepass ${REDIS_PASSWORD}` même en développement. | 15min | +| REM-024 | MEDIUM-009 | MEDIUM | `apps/web/nginx.production.conf:11-14` | Ajouter `Strict-Transport-Security` header dans la config nginx production frontend. | 15min | +| REM-025 | MEDIUM-010 | MEDIUM | `playback_websocket_handler.go:138-152` | Ajouter `conn.SetReadLimit(maxMessageSize)` pour limiter la taille des messages WebSocket. | 15min | +| REM-026 | MEDIUM-011 | MEDIUM | `marketplace/service.go:806-840` | Vérifier `license.ExpiresAt` avant de servir le téléchargement. Retourner 403 si expirée. | 30min | +| REM-027 | MEDIUM-012 | MEDIUM | `cmd/api/main.go:8` | Conditionner `import _ "net/http/pprof"` derrière un build tag `//go:build debug` ou un flag d'environnement. | 30min | -**Effort total P2** : ~5h +**Effort total P2** : ~8h --- @@ -42,12 +69,17 @@ Les actions sont classées par **priorité d'implémentation** (combinaison sév | ID | Finding | Sévérité | Fichier(s) | Action | Effort | |----|---------|----------|------------|--------|--------| -| REM-010 | LOW-002 | LOW | `migrations/*.sql` | Auditer les 30+ CASCADE DELETE. Remplacer par RESTRICT/SET NULL pour orders, payments, subscriptions | 4h | -| REM-011 | INFO-001 | INFO | `veza_back_api_db/` | Ajouter au `.gitignore`, purger de l'historique git avec BFG | 1h | -| REM-012 | INFO-002/003 | INFO | `cmd/tools/hash_gen/main.go`, `cmd/tools/create_test_user/main.go` | Aligner sur bcryptCost 12 | 15min | -| REM-013 | LOW-004 | LOW | `staging-validation.yml:35` | Épingler `docker/setup-buildx-action` par SHA | 10min | +| REM-028 | LOW-001 | LOW | `config/config.go:138` | Passer `CookieSameSite` de `lax` à `strict` si pas de flow OAuth redirect cross-site. | 30min | +| REM-029 | LOW-002 | LOW | `migrations/*.sql` | Auditer les 30+ CASCADE DELETE. Remplacer par RESTRICT/SET NULL pour orders, payments, subscriptions. | 4h | +| REM-030 | LOW-003 | LOW | `utils/utils.go:57` | Aligner `HashPassword()` sur `bcryptCost = 12` ou la supprimer si non utilisée en prod. | 15min | +| REM-031 | LOW-004 | LOW | `staging-validation.yml:35` | Épingler `docker/setup-buildx-action` par SHA. | 10min | +| REM-032 | LOW-005 | LOW | `veza-backend-api/Dockerfile:28`, `veza-stream-server/Dockerfile:29` | Remplacer `:latest` par des tags de version spécifiques dans les Dockerfiles dev. | 15min | +| REM-033 | LOW-006 | LOW | `docker-compose.yml:171` | Remplacer le JWT_SECRET hardcodé par une variable d'environnement obligatoire, même en dev. | 15min | +| REM-034 | INFO-001 | INFO | `veza_back_api_db/` | Ajouter au `.gitignore`, purger de l'historique git avec BFG. | 1h | +| REM-035 | INFO-002 | INFO | `cmd/tools/hash_gen/main.go:11`, `cmd/tools/create_test_user/main.go:54,72` | Aligner sur `bcryptCost = 12`. | 15min | +| REM-036 | INFO-003 | INFO | `services/jwt_service.go` | Ajouter `kid` header dans les JWT et support multi-clés pour faciliter la rotation. | 3h | -**Effort total P3** : ~5.5h +**Effort total P3** : ~10h --- @@ -55,22 +87,38 @@ Les actions sont classées par **priorité d'implémentation** (combinaison sév | Priorité | Findings | Effort total | |----------|----------|--------------| -| P1 (Bloquant v1.0.0) | 4 | ~4h | -| P2 (Sprint suivant) | 5 | ~5h | -| P3 (Backlog) | 4 | ~5.5h | -| **Total** | **13** | **~14.5h** | +| P0 (Immédiat — bloquant) | 5 CRITICAL | ~6h | +| P1 (Avant v1.0.0) | 10 HIGH | ~17h | +| P2 (Sprint suivant) | 12 MEDIUM | ~8h | +| P3 (Backlog) | 9 LOW/INFO | ~10h | +| **Total** | **36** | **~41h** | --- ## Workflow de remédiation ``` -1. REM-001 (IP spoofing fix) → Test: vérifier rate limit avec X-Forwarded-For forgé -2. REM-002 (followers_count) → Test: vérifier que /api/v1/users/:id ne retourne plus followers_count -3. REM-003 (bcrypt cost) → Test: vérifier que le hash créé a un cost de 12 -4. REM-004 (Post like_count) → Test: vérifier que les posts API n'exposent pas like_count -5. REM-005 (GitHub Actions SHA) → Test: vérifier que tous les `uses:` contiennent un SHA -6. REM-006 (.env.production) → Test: vérifier que le startup refuse HTTP origins en production +── P0 CRITICAL (Jour 1) ────────────────────────────────── +1. REM-005 (Path traversal) → Test: upload fichier avec ../ dans le nom +2. REM-004 (Analytics IDOR) → Test: accéder aux analytics d'une track d'un autre user +3. REM-001 (WebSocket CSWSH) → Test: connexion WS depuis un domaine non autorisé +4. REM-002 (Payout race) → Test: 2 payouts simultanés, vérifier solde cohérent +5. REM-003 (Refund race) → Test: 2 refunds simultanés, vérifier un seul traité + +── P1 HIGH (Semaine 1) ─────────────────────────────────── +6. REM-006 (IP spoofing) → Test: rate limit avec X-Forwarded-For forgé +7. REM-007 (Popularity metrics) → Test: /api/v1/users/:id ne retourne plus followers_count +8. REM-008 (Mass assignment) → Test: PUT /api/v1/users/me avec {"role":"admin"} = rejeté +9. REM-010 (Refresh token race) → Test: 2 refresh simultanés, un seul réussit +10. REM-014 (Webhook amount) → Test: webhook avec montant différent = rejeté +11. REM-011 (Promo code race) → Test: 2 uses simultanées, max_uses respecté +12. REM-015 (Free trial race) → Test: 2 souscriptions trial simultanées, une seule OK +13. REM-009 (RTMP auth) → Test: callback avec signature invalide = rejeté +14. REM-012 (Stream replay) → Test: URL rejouée après consommation = rejetée +15. REM-013 (GDPR financier) → Test: suppression compte, vérifier données financières anonymisées + +── P2 MEDIUM (Sprint suivant) ──────────────────────────── +16-27. REM-016 à REM-027 → Tests spécifiques par finding ``` --- @@ -79,14 +127,18 @@ Les actions sont classées par **priorité d'implémentation** (combinaison sév | Métrique | Valeur actuelle | Cible v1.0.0 | |----------|----------------|---------------| -| Findings CRITICAL | 0 | 0 | -| Findings HIGH | 2 | 0 | -| Findings MEDIUM | 5 | 0 | -| Findings LOW | 4 | ≤ 2 | +| Findings CRITICAL | 5 | 0 | +| Findings HIGH | 10 | 0 | +| Findings MEDIUM | 12 | 0 | +| Findings LOW | 6 | ≤ 2 | +| Findings INFO | 3 | ≤ 3 | +| Race conditions financières | 4 (payout, refund, promo, trial) | 0 | +| WebSocket origin validation | 0/3 handlers | 3/3 handlers | | Actions pinned by SHA | ~60% | 100% | | bcrypt cost consistency | 2 valeurs (10, 12) | 1 valeur (12) | | Public popularity metrics | 3 endpoints | 0 endpoints | +| IDOR vulnerabilities | 1 (analytics) | 0 | --- -*Matrice générée le 2026-03-13 — VEZA v0.12.6* +*Matrice générée le 2026-03-13 — VEZA v0.12.6 — 36 findings*