package config import ( "fmt" "os" ) // SecretsProvider définit l'interface pour les fournisseurs de secrets (T0037) type SecretsProvider interface { GetSecret(name string) (string, error) IsSecret(name string) bool } // EnvSecretsProvider récupère les secrets depuis les variables d'environnement (T0037) type EnvSecretsProvider struct { secretKeys map[string]bool } // NewEnvSecretsProvider crée un nouveau fournisseur de secrets depuis l'environnement func NewEnvSecretsProvider(secretKeys []string) *EnvSecretsProvider { keysMap := make(map[string]bool) for _, key := range secretKeys { keysMap[key] = true } return &EnvSecretsProvider{secretKeys: keysMap} } // GetSecret récupère un secret depuis les variables d'environnement (T0037) func (p *EnvSecretsProvider) GetSecret(name string) (string, error) { value := os.Getenv(name) if value == "" { return "", fmt.Errorf("secret %s not found", name) } return value, nil } // IsSecret vérifie si une clé est un secret (T0037) func (p *EnvSecretsProvider) IsSecret(name string) bool { return p.secretKeys[name] } // MaskSecret masque un secret pour l'affichage dans les logs (T0037) // Masque les 4 premiers et 4 derniers caractères, remplace le reste par "****" func MaskSecret(secret string) string { if secret == "" { return "" } if len(secret) <= 8 { return "****" } return secret[:4] + "****" + secret[len(secret)-4:] } // MaskConfigValue masque une valeur si c'est un secret (T0037) func MaskConfigValue(key, value string, provider SecretsProvider) string { if provider != nil && provider.IsSecret(key) { return MaskSecret(value) } return value } // DefaultSecretKeys retourne la liste des clés considérées comme secrets (T0037) // MOD-P0-002: Always mask secrets in logs, even in DEBUG mode // BE-SEC-014: Comprehensive list of all secret keys func DefaultSecretKeys() []string { return []string{ "JWT_SECRET", "CHAT_JWT_SECRET", "DB_PASSWORD", "DATABASE_PASSWORD", "DATABASE_URL", // Contains password in connection string "REDIS_PASSWORD", "REDIS_URL", // May contain password in connection string "RABBITMQ_URL", // May contain password in connection string "AWS_SECRET_ACCESS_KEY", "AWS_ACCESS_KEY_ID", "STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET", "SMTP_PASSWORD", "OAUTH_CLIENT_SECRET", "SENTRY_DSN", // May contain sensitive information } } // RequiredSecretKeys retourne la liste des secrets requis en production (BE-SEC-014) func RequiredSecretKeys() []string { return []string{ "JWT_SECRET", "DATABASE_URL", } } // ValidateRequiredSecrets valide que tous les secrets requis sont présents (BE-SEC-014) func ValidateRequiredSecrets(env string) error { requiredSecrets := RequiredSecretKeys() var missingSecrets []string for _, secretKey := range requiredSecrets { value := os.Getenv(secretKey) if value == "" { missingSecrets = append(missingSecrets, secretKey) } } if len(missingSecrets) > 0 { return fmt.Errorf("required secrets are missing: %v", missingSecrets) } // BE-SEC-014: In production, also validate RabbitMQ URL if RabbitMQ is enabled if env == "production" || env == "prod" { rabbitMQEnable := os.Getenv("RABBITMQ_ENABLE") if rabbitMQEnable != "false" { rabbitMQURL := os.Getenv("RABBITMQ_URL") if rabbitMQURL == "" { return fmt.Errorf("RABBITMQ_URL is required in production when RabbitMQ is enabled") } } } return nil }