# 🐛 Liste des Bugs Intentionnels - BricoLoc Legacy **Application** : BricoLoc Legacy (RĂ©plique monolithique) **Objectif** : DĂ©montrer les problĂ©matiques d'une architecture legacy mal maintenue **Statut** : Bugs intentionnels pour dĂ©monstration pĂ©dagogique --- ## 📊 RĂ©sumĂ© des Bugs | CatĂ©gorie | Nombre | Impact Critique | Impact ÉlevĂ© | Impact Moyen | |-----------|--------|-----------------|--------------|--------------| | **SĂ©curitĂ©** | 7 | 5 | 2 | 0 | | **Validation** | 2 | 1 | 1 | 0 | | **Logique MĂ©tier** | 4 | 3 | 1 | 0 | | **Performance** | 2 | 0 | 2 | 0 | | **UX** | 2 | 0 | 0 | 2 | | **Code Quality** | 3 | 2 | 1 | 0 | | **TOTAL** | **20** | **11** | **7** | **2** | --- ## 🔮 Bugs Critiques (Impact SĂ©curitĂ©/MĂ©tier) ### BUG-001 : Mots de passe stockĂ©s en clair **CatĂ©gorie** : SĂ©curitĂ© 🔮 CRITIQUE **Fichier** : `src/server.js` (ligne ~90) **Description** : Les mots de passe sont stockĂ©s en texte brut dans la base de donnĂ©es sans aucun hachage. **Code problĂ©matique** : ```javascript await run( `INSERT INTO users (email, password, nom, prenom, ...) VALUES (?, ?, ?, ?, ...)`, [email, password, nom, prenom, ...] // ❌ Mot de passe en clair ) ``` **Reproduction** : 1. CrĂ©er un compte avec mot de passe "test123" 2. Ouvrir la base SQLite : `sqlite3 data/bricoloc.db` 3. ExĂ©cuter : `SELECT email, password FROM users;` 4. Le mot de passe "test123" est visible en clair **Impact** : - Violation RGPD - Si la DB est compromise, tous les mots de passe sont exposĂ©s - Les utilisateurs rĂ©utilisant leurs mots de passe sont vulnĂ©rables **Correction attendue** : ```javascript const bcrypt = require('bcrypt'); const hashedPassword = await bcrypt.hash(password, 10); await run(`INSERT INTO users (..., password, ...) VALUES (..., ?, ...)`, [..., hashedPassword, ...]); ``` --- ### BUG-002 : Absence de protection CSRF **CatĂ©gorie** : SĂ©curitĂ© 🔮 CRITIQUE **Fichier** : `src/server.js` (configuration middleware) **Description** : Aucun token CSRF n'est gĂ©nĂ©rĂ© ni vĂ©rifiĂ© sur les formulaires. **Code problĂ©matique** : ```javascript // Configuration sessions app.use(session({ secret: 'bricoloc-secret-key', // ❌ Aussi : secret en dur // ❌ Pas de middleware CSRF })) ``` **Reproduction** : 1. Se connecter sur le site 2. CrĂ©er une page HTML malveillante : ```html
``` 3. Ouvrir cette page dans le mĂȘme navigateur 4. La rĂ©servation est créée sans consentement **Impact** : Attaque CSRF permettant des actions non autorisĂ©es **Correction attendue** : ```javascript const csrf = require('csurf'); app.use(csrf()); ``` --- ### BUG-003 : Injections SQL multiples **CatĂ©gorie** : SĂ©curitĂ© 🔮 CRITIQUE **Fichiers** : `src/server.js` (routes `/outils`, `/recherche`, `/admin/reservations`, `/admin/stocks`) **Description** : ConcatĂ©nation directe de paramĂštres utilisateur dans les requĂȘtes SQL. **Code problĂ©matique** : ```javascript // Route /outils if (categorie) { sql += ` AND o.categorie_id = ${categorie}` // ❌ SQL Injection } // Route /recherche const sql = `SELECT * FROM outils WHERE nom LIKE '%${q}%'` // ❌ SQL Injection ``` **Reproduction** : ```bash # Injection dans la recherche curl "http://localhost:3000/recherche?q=test' OR '1'='1" # Injection dans les filtres curl "http://localhost:3000/outils?categorie=1' OR '1'='1" ``` **Impact** : - Extraction complĂšte de la base de donnĂ©es - Modification/suppression de donnĂ©es - ÉlĂ©vation de privilĂšges possible **Correction attendue** : ```javascript // Utiliser des paramĂštres prĂ©parĂ©s const params = []; if (categorie) { sql += ` AND o.categorie_id = ?`; params.push(categorie); } const outils = await query(sql, params); ``` --- ### BUG-004 : Absence de vĂ©rification des rĂŽles admin **CatĂ©gorie** : SĂ©curitĂ© 🔮 CRITIQUE **Fichier** : `src/server.js` (toutes les routes `/admin/*`) **Description** : Pas de middleware `requireAdmin`, vĂ©rification manuelle `is_admin` rĂ©pĂ©tĂ©e. **Code problĂ©matique** : ```javascript app.get('/admin', requireAuth, async (req, res) => { // ❌ VĂ©rification manuelle dans chaque route if (!res.locals.user.is_admin) { return res.redirect('/') } // ... }) ``` **Reproduction** : 1. Se connecter avec un compte utilisateur standard 2. Modifier dans DevTools : `document.cookie = "userId=1"` (si l'admin est ID=1) 3. Ou modifier directement la DB : `UPDATE users SET is_admin=1 WHERE id=2` 4. AccĂ©der Ă  `/admin` **Impact** : N'importe quel utilisateur peut devenir admin **Correction attendue** : ```javascript function requireAdmin(req, res, next) { if (!req.session.userId) return res.redirect('/login'); if (!res.locals.user || !res.locals.user.is_admin) { return res.status(403).send('AccĂšs refusĂ©'); } next(); } app.get('/admin', requireAdmin, async (req, res) => { ... }); ``` --- ### BUG-005 : Upload fichiers non sĂ©curisĂ© **CatĂ©gorie** : SĂ©curitĂ© 🟠 ÉLEVÉ **Fichier** : `src/server.js` (configuration multer) **Description** : Aucune validation du type MIME ni de l'extension des fichiers uploadĂ©s. **Code problĂ©matique** : ```javascript const upload = multer({ storage: storage // ❌ Pas de fileFilter, pas de limite de taille }) ``` **Reproduction** : 1. Aller sur `/admin/outils` 2. CrĂ©er un outil avec un fichier `.php` ou `.exe` 3. Le fichier est acceptĂ© et stockĂ© dans `/uploads` 4. Potentiellement exĂ©cutable si le serveur est mal configurĂ© **Impact** : Upload de fichiers malveillants (webshell, virus) **Correction attendue** : ```javascript const upload = multer({ storage: storage, limits: { fileSize: 5 * 1024 * 1024 }, // 5MB max fileFilter: (req, file, cb) => { if (!file.mimetype.startsWith('image/')) { return cb(new Error('Seulement les images sont autorisĂ©es')); } cb(null, true); } }); ``` --- ### BUG-007 : Prix calculĂ© cĂŽtĂ© client **CatĂ©gorie** : Validation 🔮 CRITIQUE **Fichiers** : `src/views/nouvelle-reservation.ejs`, `src/public/js/main.js` **Description** : Le prix total de la rĂ©servation est calculĂ© en JavaScript cĂŽtĂ© client et envoyĂ© au serveur. **Code problĂ©matique** : ```javascript // Client-side (manipulable via DevTools) const prixTotal = (prixJour * diffDays).toFixed(2); document.getElementById('prix_total').value = prixTotal; // ❌ EnvoyĂ© tel quel au serveur ``` **Reproduction** : 1. Aller sur `/outils/1/reserver` 2. SĂ©lectionner des dates (ex: 5 jours, prix = 250€) 3. Ouvrir DevTools Console 4. ExĂ©cuter : `document.getElementById('prix_total').value = '0.01'` 5. Soumettre le formulaire 6. RĂ©servation créée pour 0.01€ **Impact** : Perte financiĂšre importante, fraude **Correction attendue** : ```javascript // Serveur (server.js) app.post('/reservations', requireAuth, async (req, res) => { const { outil_id, date_debut, date_fin } = req.body; // Recalculer le prix cĂŽtĂ© serveur const outil = await get('SELECT prix_jour FROM outils WHERE id = ?', [outil_id]); const nbJours = calculerNbJours(date_debut, date_fin); const prixTotal = outil.prix_jour * nbJours; await run(`INSERT INTO reservations (..., prix_total) VALUES (..., ?)`, [..., prixTotal]); }); ``` --- ### BUG-008 : Race condition sur les rĂ©servations **CatĂ©gorie** : Logique MĂ©tier 🔮 CRITIQUE **Fichier** : `src/server.js` (route `POST /reservations`) **Description** : Pas de transaction SQL lors de la crĂ©ation d'une rĂ©servation. **Code problĂ©matique** : ```javascript // VĂ©rification stock const stock = await get('SELECT quantite_disponible FROM stocks WHERE ...'); if (stock.quantite_disponible <= 0) { ... } // ❌ Pas de transaction, un autre utilisateur peut rĂ©server entre-temps // CrĂ©er rĂ©servation await run('INSERT INTO reservations ...'); // DĂ©crĂ©menter stock await run('UPDATE stocks SET quantite_disponible = quantite_disponible - 1 ...'); ``` **Reproduction** : 1. Ouvrir 2 onglets du site 2. Dans les 2 onglets, aller sur le mĂȘme outil avec 1 seul stock disponible 3. Soumettre les 2 formulaires en mĂȘme temps (clic simultanĂ©) 4. Les 2 rĂ©servations sont créées (double-booking) **Impact** : Surbooking, promesses non tenues **Correction attendue** : ```javascript await db.run('BEGIN TRANSACTION'); try { const stock = await get('SELECT quantite_disponible FROM stocks WHERE ... FOR UPDATE'); if (stock.quantite_disponible <= 0) throw new Error('Stock insuffisant'); await run('INSERT INTO reservations ...'); await run('UPDATE stocks SET quantite_disponible = quantite_disponible - 1 ...'); await db.run('COMMIT'); } catch (err) { await db.run('ROLLBACK'); throw err; } ``` --- ### BUG-009 : VĂ©rification de disponibilitĂ© incomplĂšte **CatĂ©gorie** : Logique MĂ©tier 🔮 CRITIQUE **Fichier** : `src/server.js` (route `POST /reservations`) **Description** : Ne vĂ©rifie pas les conflits de dates avec les rĂ©servations existantes. **Code problĂ©matique** : ```javascript // VĂ©rifie seulement le stock global const stock = await get('SELECT quantite_disponible FROM stocks WHERE ...'); // ❌ Ne vĂ©rifie PAS si une autre rĂ©servation existe pour ces dates ``` **Reproduction** : 1. CrĂ©er une rĂ©servation du 1er au 5 dĂ©cembre 2. CrĂ©er une 2Ăšme rĂ©servation du 3 au 7 dĂ©cembre (mĂȘme outil, mĂȘme entrepĂŽt) 3. Les 2 rĂ©servations sont acceptĂ©es (chevauchement) **Impact** : Double-booking, conflit de disponibilitĂ© **Correction attendue** : ```javascript const conflits = await query(` SELECT COUNT(*) as count FROM reservations WHERE outil_id = ? AND entrepot_id = ? AND statut IN ('en_attente', 'confirmee') AND ( (date_debut <= ? AND date_fin >= ?) OR (date_debut <= ? AND date_fin >= ?) OR (date_debut >= ? AND date_fin <= ?) ) `, [outil_id, entrepot_id, date_debut, date_debut, date_fin, date_fin, date_debut, date_fin]); if (conflits.count > 0) { throw new Error('Dates non disponibles'); } ``` --- ### BUG-010 : Annulation ne libĂšre pas le stock **CatĂ©gorie** : Logique MĂ©tier 🟠 ÉLEVÉ **Fichier** : `src/server.js` (route `POST /reservations/:id/annuler`) **Description** : Lors de l'annulation, le stock n'est pas remis Ă  jour. **Code problĂ©matique** : ```javascript app.post('/reservations/:id/annuler', requireAuth, async (req, res) => { await run('UPDATE reservations SET statut = ? WHERE id = ?', ['annulee', reservationId]); // ❌ BUG : Ne remet pas Ă  jour stocks.quantite_disponible // Devrait faire : UPDATE stocks SET quantite_disponible = quantite_disponible + 1 }); ``` **Reproduction** : 1. RĂ©server un outil (stock passe de 5 Ă  4) 2. Annuler la rĂ©servation 3. Le stock reste Ă  4 au lieu de revenir Ă  5 **Impact** : IncohĂ©rence des stocks, perte de disponibilitĂ© --- ## 🟠 Bugs ÉlevĂ©s (Performance/Architecture) ### BUG-011 : Absence de pagination serveur **CatĂ©gorie** : Performance 🟠 ÉLEVÉ **Fichier** : `src/server.js` (route `/outils`) **Description** : Tous les outils sont chargĂ©s en mĂ©moire sans LIMIT ni pagination. **Code problĂ©matique** : ```javascript const outils = await query(sql); // ❌ Pas de LIMIT // Charge 1000+ outils en mĂ©moire ``` **Impact** : Lenteur avec beaucoup de donnĂ©es, consommation mĂ©moire --- ### BUG-012 : RequĂȘtes N+1 **CatĂ©gorie** : Performance 🟠 ÉLEVÉ **Fichier** : `src/server.js` (routes `/outils`, `/mes-reservations`) **Description** : Boucles avec requĂȘtes SQL imbriquĂ©es. **Code problĂ©matique** : ```javascript const outils = await query('SELECT * FROM outils'); for (const outil of outils) { const stocks = await query('SELECT * FROM stocks WHERE outil_id = ?', [outil.id]); // ❌ N requĂȘtes outil.stocks = stocks; } ``` **Impact** : 1 + N requĂȘtes au lieu d'1 seule requĂȘte avec JOIN --- ### BUG-013 : Messages d'erreur trop dĂ©taillĂ©s **CatĂ©gorie** : UX/SĂ©curitĂ© 🟡 MOYEN **Fichier** : `src/server.js` (route `POST /login`) **Description** : Les messages rĂ©vĂšlent si un email existe dans la base. **Code problĂ©matique** : ```javascript if (!user) { req.flash('error', 'Aucun compte trouvĂ© avec cette adresse email'); // ❌ RĂ©vĂšle l'existence return res.redirect('/login'); } if (user.password !== password) { req.flash('error', 'Mot de passe incorrect'); // ❌ RĂ©vĂšle que l'email existe return res.redirect('/login'); } ``` **Impact** : ÉnumĂ©ration d'emails, fuite d'informations --- ### BUG-014 : ProblĂšmes de timezone **CatĂ©gorie** : UX 🟡 MOYEN **Fichiers** : `src/views/reservations.ejs`, `src/views/nouvelle-reservation.ejs` **Description** : Les dates sont affichĂ©es/manipulĂ©es sans gestion cohĂ©rente des timezones. **Code problĂ©matique** : ```javascript new Date(reservation.date_debut).toLocaleDateString('fr-FR') // ❌ DĂ©calage possible ``` **Impact** : Dates incorrectes d'un jour selon le timezone --- ## 🟣 Bugs Architecturaux ### BUG-015 : Code dupliquĂ© partout **CatĂ©gorie** : Code Quality 🔮 CRITIQUE **Fichier** : `src/server.js` (tout le fichier) **Description** : MĂȘme logique rĂ©pĂ©tĂ©e (vĂ©rification admin, requĂȘtes SQL, etc.). **Impact** : Maintenance difficile, risque d'oubli lors de modifications --- ### BUG-016 : Pas de gestion d'erreurs **CatĂ©gorie** : Code Quality 🟠 ÉLEVÉ **Fichier** : `src/server.js`, `src/public/js/main.js` **Description** : Pas de try/catch global, erreurs non gĂ©rĂ©es. **Impact** : Crash de l'app en production --- ### BUG-017 : Logique mĂ©tier dans les vues **CatĂ©gorie** : Architecture 🔮 CRITIQUE **Fichiers** : `src/views/outils.ejs`, `src/views/reservations.ejs` **Description** : Calculs et logique dans les templates EJS. **Code problĂ©matique** : ```ejs <% outil.total_disponible = stocks.reduce((sum, s) => sum + s.quantite_disponible, 0) %> ``` **Impact** : Impossible Ă  tester, difficile Ă  maintenir --- ### BUG-018 : Monolithe de 780+ lignes **CatĂ©gorie** : Architecture 🔮 CRITIQUE **Fichier** : `src/server.js` **Description** : Tout le code dans un seul fichier. **Impact** : Impossible Ă  maintenir, conflits Git constants --- ## 📈 Statistiques Finales - **Total bugs identifiĂ©s** : 20 - **Fichiers affectĂ©s** : 8 - **Lignes de code vulnĂ©rables** : ~200 - **Score de sĂ©curitĂ© estimĂ©** : 15/100 ⚠ - **Dette technique** : ~40 jours/homme de refactoring --- ## 🎯 Plan de Correction (Non implĂ©mentĂ©) 1. **Phase 1 - SĂ©curitĂ© critique** (2 semaines) - BUG-001, 002, 003, 004, 007 2. **Phase 2 - Logique mĂ©tier** (1 semaine) - BUG-008, 009, 010 3. **Phase 3 - Architecture** (4 semaines) - Refactoring complet (services, controllers, middlewares) - BUG-015, 016, 017, 018 4. **Phase 4 - Performance & UX** (1 semaine) - BUG-011, 012, 013, 014 **OU : Migrer vers l'application moderne (recommandĂ©)** --- **DerniĂšre mise Ă  jour** : 17 Novembre 2025 **Statut** : Documentation complĂšte âœ