First-attempt commit3a5c6e184only captured the .gitignore change; the pre-commit hook silently dropped the 343 staged moves/deletes during lint-staged's "no matching task" path. This commit re-applies the intended J1 content on top ofbec75f143(which was pushed in parallel). Uses --no-verify because: - J1 only touches .md/.json/.log/.png/binaries — zero code that would benefit from lint-staged, typecheck, or vitest - The hook demonstrated it corrupts pure-rename commits in this repo - Explicitly authorized by user for this one commit Changes (343 total: 169 deletions + 174 renames): Binaries purged (~167 MB): - veza-backend-api/{server,modern-server,encrypt_oauth_tokens,seed,seed-v2} Generated reports purged: - 9 apps/web/lint_report*.json (~32 MB) - 8 apps/web/tsc_*.{log,txt} + ts_*.log (TS error snapshots) - 3 apps/web/storybook_*.json (1375+ stored errors) - apps/web/{build_errors*,build_output,final_errors}.txt - 70 veza-backend-api/coverage*.out + coverage_groups/ (~4 MB) - 3 veza-backend-api/internal/handlers/*.bak Root cleanup: - 54 audit-*.png (visual regression baselines, ~11 MB) - 9 stale MVP-era scripts (Jan 27, hardcoded v0.101): start_{iteration,mvp,recovery}.sh, test_{mvp_endpoints,protected_endpoints,user_journey}.sh, validate_v0101.sh, verify_logs_setup.sh, gen_hash.py Session docs archived (not deleted — preserved under docs/archive/): - 78 apps/web/*.md → docs/archive/frontend-sessions-2026/ - 43 veza-backend-api/*.md → docs/archive/backend-sessions-2026/ - 53 docs/{RETROSPECTIVE_V,SMOKE_TEST_V,PLAN_V0_,V0_*_RELEASE_SCOPE, AUDIT_,PLAN_ACTION_AUDIT,REMEDIATION_PROGRESS}*.md → docs/archive/v0-history/ README.md and CONTRIBUTING.md preserved in apps/web/ and veza-backend-api/. Note: The .gitignore rules preventing recurrence were already pushed in3a5c6e184and remain in place — this commit does not modify .gitignore. Refs: AUDIT_REPORT.md §11
7.1 KiB
✅ P1-RES-003 — PROTECTION c.MustGet() (Fail-Secure)
Date: 2025-12-12
Objectif: Remplacer tous les usages non protégés de c.MustGet("user_id") par un accès fail-secure pour éviter les panics
📋 RÉSUMÉ
✅ Helper fail-secure créé : getUserID() remplace c.MustGet()
✅ 16 occurrences remplacées : Toutes les utilisations de c.MustGet("user_id") dans TrackHandler
✅ Tests complets : 3 tests vérifient l'absence de panic et le retour 401
✅ Compilation réussie : Aucune erreur de compilation
📁 FICHIERS MODIFIÉS
1. internal/core/track/handler.go
- ✅ Helper
getUserID()créé (lignes 91-110) : Accès sécurisé avecc.Get()au lieu dec.MustGet() - ✅ 16 handlers modifiés : Tous utilisent maintenant
h.getUserID(c)au lieu dec.MustGet("user_id") - ✅ Standardisation : Toutes les réponses utilisent
response.Unauthorized()(même pattern que le reste du fichier)
Handlers modifiés :
UploadTrack(ligne 129)GetUploadStatus(ligne 244)InitiateChunkedUpload(ligne 298)UploadChunk(ligne 351)CompleteChunkedUpload(ligne 408)GetUploadQuota(lignes 597, 613)ResumeUpload(ligne 649)UpdateTrack(ligne 852)DeleteTrack(ligne 934)BatchDeleteTracks(ligne 999)BatchUpdateTracks(ligne 1055)LikeTrack(ligne 1109)UnlikeTrack(ligne 1142)CreateShare(ligne 1459)RevokeShare(ligne 1552)
2. internal/core/track/handler_mustget_test.go (nouveau)
- ✅
TestTrackHandler_NoPanicWhenUserIDMissing: Teste 13 handlers pour vérifier l'absence de panic - ✅
TestTrackHandler_GetUserID_Returns401WhenMissing: Teste le helper directement - ✅
TestTrackHandler_GetUserID_Returns401WhenInvalidType: Teste la gestion des types invalides
🔍 IMPLÉMENTATION
Helper fail-secure
// getUserID récupère l'ID utilisateur du contexte de manière sécurisée (fail-secure)
// MOD-P1-RES-003: Remplace c.MustGet() pour éviter les panics
// Retourne false si user_id est absent ou invalide (répond déjà avec 401)
func (h *TrackHandler) getUserID(c *gin.Context) (uuid.UUID, bool) {
userIDInterface, exists := c.Get("user_id")
if !exists {
response.Unauthorized(c, "unauthorized")
return uuid.Nil, false
}
userID, ok := userIDInterface.(uuid.UUID)
if !ok {
response.Unauthorized(c, "unauthorized")
return uuid.Nil, false
}
if userID == uuid.Nil {
response.Unauthorized(c, "unauthorized")
return uuid.Nil, false
}
return userID, true
}
Pattern de remplacement
Avant (peut panic) :
userID := c.MustGet("user_id").(uuid.UUID)
if userID == uuid.Nil {
response.Unauthorized(c, "unauthorized")
return
}
Après (fail-secure) :
// MOD-P1-RES-003: Utiliser helper fail-secure au lieu de c.MustGet()
userID, ok := h.getUserID(c)
if !ok {
return // Erreur déjà envoyée par getUserID
}
🧪 PREUVES (TESTS)
Tests unitaires
go test ./internal/core/track -run TestTrackHandler_NoPanicWhenUserIDMissing -v -count=1
Résultat : ✅ Tous les tests passent (13/13)
Test 1 : Aucun handler ne panic si user_id absent
TestTrackHandler_NoPanicWhenUserIDMissing
- ✅ Teste 13 handlers différents
- ✅ Vérifie qu'aucun panic ne se produit
- ✅ Vérifie que la réponse est 401 Unauthorized
Test 2 : Helper retourne 401 si user_id absent
TestTrackHandler_GetUserID_Returns401WhenMissing
- ✅ Vérifie que
getUserID()ne panic pas - ✅ Vérifie que
okestfalse - ✅ Vérifie que la réponse est 401
Test 3 : Helper gère les types invalides
TestTrackHandler_GetUserID_Returns401WhenInvalidType
- ✅ Vérifie que
getUserID()gère les types invalides (string au lieu de UUID) - ✅ Vérifie que la réponse est 401
Tests complets
go test ./internal/core/track -v -count=1
Résultat : ✅ Tous les tests passent (tests existants + nouveaux tests)
📊 COMPARAISON AVANT/APRÈS
Avant (Risque de panic)
userID := c.MustGet("user_id").(uuid.UUID) // ❌ Panic si clé absente
if userID == uuid.Nil {
response.Unauthorized(c, "unauthorized")
return
}
Problèmes :
- ❌
c.MustGet()peut panic si la clé n'existe pas - ❌ Type assertion peut panic si le type est incorrect
- ❌ Pas de message d'erreur clair pour debugging
Après (Fail-secure)
userID, ok := h.getUserID(c) // ✅ Ne panic jamais
if !ok {
return // Erreur déjà envoyée par getUserID
}
Avantages :
- ✅ Aucun panic possible
- ✅ Gestion explicite des erreurs
- ✅ Réponse structurée 401 si user_id absent/invalide
- ✅ Code plus lisible et maintenable
✅ VALIDATION
Compilation
go build ./internal/core/track/...
Résultat : ✅ Compilation réussie
Tests unitaires
go test ./internal/core/track -run TestTrackHandler_NoPanicWhenUserIDMissing -v
Résultat : ✅ Tous les tests passent (13/13)
Tests complets
go test ./internal/core/track -v -count=1
Résultat : ✅ Tous les tests passent
Vérification absence de c.MustGet()
grep -r "c.MustGet(" internal/core/track/
Résultat : ✅ Aucune occurrence de c.MustGet("user_id") restante
🎯 OBJECTIFS ATTEINTS
- ✅ Toutes les occurrences remplacées : 16 handlers utilisent maintenant
getUserID() - ✅ Aucun panic possible : Helper fail-secure avec gestion explicite des erreurs
- ✅ Tests complets : 3 tests vérifient l'absence de panic et le comportement correct
- ✅ Pattern standardisé : Toutes les réponses utilisent
response.Unauthorized() - ✅ Aucun changement fonctionnel : Comportement identique, mais sans risque de panic
📋 COMMANDES DE VALIDATION
Compilation
go build ./internal/core/track/...
Tests spécifiques
go test ./internal/core/track -run TestTrackHandler_NoPanicWhenUserIDMissing -v -count=1
go test ./internal/core/track -run TestTrackHandler_GetUserID -v -count=1
Tests complets
go test ./internal/core/track -v -count=1
Vérification absence de c.MustGet()
grep -r "c.MustGet(" internal/core/track/
📝 DÉTAILS TECHNIQUES
Cas gérés par le helper
-
Clé absente :
c.Get("user_id")retourneexists = false- → Retourne
uuid.Nil, falseet répond avec 401
- → Retourne
-
Type invalide :
userIDInterface.(uuid.UUID)échoue- → Retourne
uuid.Nil, falseet répond avec 401
- → Retourne
-
Valeur nil :
userID == uuid.Nil- → Retourne
uuid.Nil, falseet répond avec 401
- → Retourne
-
Valeur valide : Toutes les vérifications passent
- → Retourne
userID, true
- → Retourne
Handlers où userID n'est pas utilisé
Dans certains handlers (GetUploadStatus, UploadChunk), userID n'est récupéré que pour vérifier l'authentification mais n'est pas utilisé ensuite. Dans ces cas, on utilise _ :
if _, ok := h.getUserID(c); !ok {
return // Erreur déjà envoyée par getUserID
}
Statut final : ✅ P1-RES-003 IMPLÉMENTÉ ET VALIDÉ