250 lines
7.4 KiB
Markdown
250 lines
7.4 KiB
Markdown
# ✅ P1-001 — IMPLÉMENTATION CLAMAV SCAN (FAIL-SECURE)
|
|
|
|
**Date**: 2025-12-12
|
|
**Objectif**: Scanner chaque fichier uploadé AVANT toute persistance avec ClamAV
|
|
|
|
---
|
|
|
|
## 📋 RÉSUMÉ
|
|
|
|
✅ **Implémentation complète** : Le scan ClamAV est maintenant intégré et se fait **AVANT toute persistance** (DB, FS, S3).
|
|
✅ **Fail-secure** : Si ClamAV est indisponible, tous les uploads sont rejetés (HTTP 503).
|
|
✅ **Codes HTTP appropriés** : 422 pour virus détecté, 503 pour ClamAV indisponible.
|
|
✅ **Tests** : Tests unitaires + tests d'intégration optionnels avec build tag.
|
|
✅ **Documentation** : Guide complet dans `docs/CLAMAV_SETUP.md`.
|
|
|
|
---
|
|
|
|
## 📁 FICHIERS MODIFIÉS
|
|
|
|
### 1. `internal/services/upload_validator.go`
|
|
- ✅ Ajout de `context.Context` dans `ValidateFile()` pour timeout strict
|
|
- ✅ Amélioration de `scanWithClamAV()` avec timeout de 30 secondes via `context.WithTimeout()`
|
|
- ✅ Gestion d'erreurs améliorée : `clamav_unavailable`, `clamav_infected`, `clamav_scan_error`
|
|
- ✅ Logs structurés pour tous les scans
|
|
|
|
### 2. `internal/core/track/handler.go`
|
|
- ✅ Ajout de `uploadValidator` comme dépendance du `TrackHandler`
|
|
- ✅ Méthode `SetUploadValidator()` pour injection de dépendance
|
|
- ✅ Scan ClamAV dans `UploadTrack()` **AVANT** l'appel à `trackService.UploadTrack()`
|
|
- ✅ Codes HTTP appropriés : 422 pour virus, 503 pour ClamAV indisponible
|
|
|
|
### 3. `internal/handlers/upload.go`
|
|
- ✅ Mise à jour pour utiliser `context.Context` dans `ValidateFile()`
|
|
- ✅ Amélioration de la gestion d'erreurs ClamAV avec codes HTTP appropriés
|
|
|
|
### 4. `internal/api/router.go`
|
|
- ✅ Injection de `UploadValidator` dans `TrackHandler` lors de la configuration des routes
|
|
|
|
### 5. `internal/services/upload_validator_test.go`
|
|
- ✅ Mise à jour des tests existants pour utiliser `context.Context`
|
|
- ✅ Tests passent avec la nouvelle signature
|
|
|
|
### 6. `internal/services/upload_validator_integration_test.go` (nouveau)
|
|
- ✅ Tests d'intégration optionnels avec build tag `clamav`
|
|
- ✅ Test avec fichier EICAR (test virus standard)
|
|
- ✅ Test avec fichier propre
|
|
|
|
### 7. `docs/CLAMAV_SETUP.md` (nouveau)
|
|
- ✅ Documentation complète : installation, configuration, tests, dépannage
|
|
|
|
---
|
|
|
|
## 🔒 PREUVE : SCAN AVANT PERSISTANCE
|
|
|
|
### Ordre d'exécution garanti
|
|
|
|
```
|
|
1. Handler reçoit le fichier uploadé
|
|
↓
|
|
2. Handler appelle ValidateFile(ctx, fileHeader, "audio")
|
|
↓
|
|
3. ValidateFile() fait le scan ClamAV (timeout 30s)
|
|
↓
|
|
4a. Si scan OK → result.Valid = true → Handler continue
|
|
4b. Si scan FAIL → erreur retournée → Handler retourne immédiatement (STOP)
|
|
↓
|
|
5. Si scan OK → Handler appelle trackService.UploadTrack()
|
|
↓
|
|
6. UploadTrack() sauvegarde le fichier (ligne 182)
|
|
↓
|
|
7. UploadTrack() crée l'enregistrement DB (ligne 213)
|
|
```
|
|
|
|
**Si le scan échoue à l'étape 3, les étapes 5-7 ne sont JAMAIS exécutées.**
|
|
|
|
### Preuve dans le code
|
|
|
|
#### `internal/core/track/handler.go:117-160`
|
|
```go
|
|
// MOD-P1-001: Scanner le fichier avec ClamAV AVANT toute persistance
|
|
if h.uploadValidator != nil {
|
|
validationResult, err := h.uploadValidator.ValidateFile(c.Request.Context(), fileHeader, "audio")
|
|
if err != nil {
|
|
// Si erreur → retour immédiat, pas d'appel à trackService.UploadTrack()
|
|
c.JSON(http.StatusServiceUnavailable, ...)
|
|
return // ← STOP ICI, pas de persistance
|
|
}
|
|
if !validationResult.Valid || validationResult.Quarantined {
|
|
// Si invalide → retour immédiat
|
|
c.JSON(http.StatusUnprocessableEntity, ...)
|
|
return // ← STOP ICI, pas de persistance
|
|
}
|
|
}
|
|
|
|
// Upload track (validation et quota sont vérifiés dans le service)
|
|
// MOD-P1-001: Le scan ClamAV a été fait ci-dessus, maintenant on peut persister
|
|
track, err := h.trackService.UploadTrack(c.Request.Context(), userID, fileHeader)
|
|
```
|
|
|
|
**Conclusion** : `trackService.UploadTrack()` n'est appelé **QUE SI** le scan ClamAV a réussi.
|
|
|
|
---
|
|
|
|
## 🧪 TESTS
|
|
|
|
### Tests unitaires (sans ClamAV requis)
|
|
|
|
```bash
|
|
go test ./internal/services -run TestUploadValidator -v -count=1
|
|
```
|
|
|
|
**Résultat** : ✅ **PASS**
|
|
- `TestUploadValidator_ClamAVDown_RejectsUploads` — PASS
|
|
- `TestUploadValidator_ClamAVDisabled_AllowsUploads` — PASS
|
|
|
|
### Tests d'intégration (ClamAV requis)
|
|
|
|
```bash
|
|
# Avec ClamAV démarré
|
|
go test -tags=clamav ./internal/services -run TestUploadValidator_ClamAV -v
|
|
```
|
|
|
|
**Tests disponibles** :
|
|
- `TestUploadValidator_ClamAV_EICAR_Rejected` — Test avec fichier EICAR (virus test standard)
|
|
- `TestUploadValidator_ClamAV_CleanFile_Accepted` — Test avec fichier propre
|
|
|
|
### Tests complets
|
|
|
|
```bash
|
|
go test ./... -count=1
|
|
```
|
|
|
|
**Résultat** : Tests unitaires P1-001 passent. Les tests qui échouent sont préexistants (database, handlers nécessitant DB).
|
|
|
|
---
|
|
|
|
## 📊 CODES HTTP
|
|
|
|
| Situation | Code HTTP | Message | Code erreur |
|
|
|-----------|-----------|---------|-------------|
|
|
| ClamAV indisponible (enabled mais down) | 503 | "Virus scanning service is temporarily unavailable" | `SERVICE_UNAVAILABLE` |
|
|
| Virus détecté | 422 | "File rejected: virus detected" | `VIRUS_DETECTED` |
|
|
| Erreur de scan (timeout, connexion) | 503 | "Virus scan failed" | `SCAN_ERROR` |
|
|
| Fichier propre | 201 | Upload réussi | - |
|
|
|
|
---
|
|
|
|
## ⚙️ CONFIGURATION
|
|
|
|
### Variables d'environnement
|
|
|
|
```bash
|
|
# Activer/désactiver ClamAV (par défaut: true)
|
|
CLAMAV_ENABLED=true
|
|
|
|
# Adresse du daemon ClamAV (par défaut: localhost:3310)
|
|
CLAMAV_ADDRESS=localhost:3310
|
|
```
|
|
|
|
### Comportement fail-secure
|
|
|
|
- ✅ **Serveur démarre** même si ClamAV est down
|
|
- ❌ **Tous les uploads sont rejetés** si ClamAV est requis mais indisponible
|
|
- 📝 **Warning logué** : "ClamAV is enabled but unavailable - uploads will be rejected"
|
|
|
|
---
|
|
|
|
## 📚 DOCUMENTATION
|
|
|
|
Documentation complète disponible dans : **`docs/CLAMAV_SETUP.md`**
|
|
|
|
Contenu :
|
|
- Installation ClamAV (Linux, Docker, macOS)
|
|
- Configuration des variables d'environnement
|
|
- Exécution des tests (unitaires et d'intégration)
|
|
- Dépannage
|
|
- Preuve que le scan se fait avant persistance
|
|
|
|
---
|
|
|
|
## ✅ VALIDATION
|
|
|
|
### Compilation
|
|
|
|
```bash
|
|
go build ./...
|
|
```
|
|
|
|
**Résultat** : ✅ **Compilation réussie**
|
|
|
|
### Tests unitaires
|
|
|
|
```bash
|
|
go test ./internal/services -run TestUploadValidator -v
|
|
```
|
|
|
|
**Résultat** : ✅ **Tous les tests passent**
|
|
|
|
### Tests d'intégration (optionnel)
|
|
|
|
```bash
|
|
# Nécessite ClamAV démarré
|
|
go test -tags=clamav ./internal/services -run TestUploadValidator_ClamAV -v
|
|
```
|
|
|
|
**Résultat** : Tests disponibles, nécessitent ClamAV en cours d'exécution
|
|
|
|
---
|
|
|
|
## 🎯 OBJECTIFS ATTEINTS
|
|
|
|
- ✅ **Scan AVANT persistance** : Garanti par l'ordre d'exécution dans le handler
|
|
- ✅ **Fail-secure** : Uploads rejetés si ClamAV indisponible
|
|
- ✅ **Timeouts stricts** : 30 secondes max via `context.WithTimeout()`
|
|
- ✅ **Codes HTTP appropriés** : 422 pour virus, 503 pour ClamAV down
|
|
- ✅ **Tests unitaires** : Tests avec mocks (pas de ClamAV requis)
|
|
- ✅ **Tests d'intégration** : Tests optionnels avec build tag `clamav`
|
|
- ✅ **Documentation** : Guide complet dans `docs/CLAMAV_SETUP.md`
|
|
|
|
---
|
|
|
|
## 📝 COMMANDES DE VALIDATION
|
|
|
|
### Tests unitaires (sans ClamAV)
|
|
```bash
|
|
go test ./internal/services -run TestUploadValidator -v -count=1
|
|
```
|
|
|
|
### Tests d'intégration (avec ClamAV)
|
|
```bash
|
|
# 1. Démarrer ClamAV
|
|
sudo systemctl start clamav-daemon
|
|
|
|
# 2. Exécuter les tests
|
|
go test -tags=clamav ./internal/services -run TestUploadValidator_ClamAV -v
|
|
```
|
|
|
|
### Compilation
|
|
```bash
|
|
go build ./...
|
|
```
|
|
|
|
### Tests complets
|
|
```bash
|
|
go test ./... -count=1
|
|
```
|
|
|
|
---
|
|
|
|
**Statut final** : ✅ **P1-001 IMPLÉMENTÉ ET VALIDÉ**
|