Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | /** * Validation des chemins de fichiers — Protection contre le path traversal. * * Vérifie qu'un chemin fourni par l'utilisateur (ou Claude) est sûr * avant de l'utiliser dans une commande SSH. * * @module utils/pathValidator */ const path = require('node:path'); /** * Caractères et séquences interdits dans un chemin de fichier. * Ces patterns pourraient être exploités pour de l'injection de commande * ou du path traversal si insérés dans une commande shell. */ const FORBIDDEN_PATTERNS = [ /\0/, // Null byte — troncature de chaîne dans certains systèmes /\.\.\//, // Path traversal avec ../ /\.\.\\/, // Path traversal Windows-style /^~\//, // Expansion tilde (pourrait résoudre vers /root) /\$\(/, // Command substitution $(...) /`/, // Backtick command substitution /\$\{/, // Variable expansion ${...} /\n/, // Newline — injection de commande /\r/, // Carriage return ]; /** * Valide qu'un chemin de fichier est sûr pour une utilisation SSH. * * @param {string} filePath — Le chemin à valider * @returns {{ valid: boolean, reason?: string }} */ function validatePath(filePath) { if (!filePath || typeof filePath !== 'string') { return { valid: false, reason: 'Chemin vide ou invalide.' }; } // Le chemin doit être absolu if (!filePath.startsWith('/')) { return { valid: false, reason: 'Le chemin doit être absolu (commencer par /).' }; } // Longueur maximale (Linux PATH_MAX = 4096) if (filePath.length > 4096) { return { valid: false, reason: 'Chemin trop long (max 4096 caractères).' }; } // Vérifier les patterns interdits for (const pattern of FORBIDDEN_PATTERNS) { if (pattern.test(filePath)) { return { valid: false, reason: `Chemin contient un pattern interdit : ${pattern.source}` }; } } // Normaliser et vérifier que le chemin ne sort pas de la racine const normalized = path.posix.normalize(filePath); if (normalized.includes('..')) { return { valid: false, reason: 'Path traversal détecté après normalisation.' }; } // Chemins sensibles interdits en écriture const SENSITIVE_PATHS = ['/etc/shadow', '/etc/passwd', '/etc/sudoers']; if (SENSITIVE_PATHS.includes(normalized)) { return { valid: false, reason: 'Chemin système sensible — accès interdit.' }; } return { valid: true }; } /** * Valide un nom de container Docker (alphanumérique + tirets + underscores + points). * * @param {string} name — Le nom ou ID du container * @returns {{ valid: boolean, reason?: string }} */ function validateContainerName(name) { if (!name || typeof name !== 'string') { return { valid: false, reason: 'Nom de container vide.' }; } if (name.length > 128) { return { valid: false, reason: 'Nom de container trop long.' }; } if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) { return { valid: false, reason: 'Nom de container invalide — caractères autorisés : a-z, 0-9, _, -, .' }; } return { valid: true }; } module.exports = { validatePath, validateContainerName }; |