feat: intégration complète du traitement des variables d environnement

- Ajout de l EnvProcessor dans l API pour résoudre les variables VAR
- Résolution récursive des variables d environnement en mémoire
- Support des syntaxes VAR et VAR entre accolades
- Chargement uniquement du fichier .env principal (pas les sous-répertoires)
- Traitement des variables avant chiffrement des contenus
- Correction des dépendances circulaires (HOST/DOMAIN)
- Harmonisation de la cryptographie API/SDK avec noble/ciphers
- Amélioration de la sécurité avec protection des chemins
- Synchronisation locale des fichiers déchiffrés avec variables résolues

Variables maintenant correctement résolues:
- DOMAIN=4nkweb.com → valeur fixe préservée
- HOST=dev4.DOMAIN → dev4.4nkweb.com
- ROOT_HOST=HOST → dev4.4nkweb.com
- ROOT_URL=https://ROOT_HOSThttps://dev4.4nkweb.com

Tests: 72 fichiers synchronisés avec succès, chiffrement ChaCha20-Poly1305 fonctionnel
This commit is contained in:
4NK Dev 2025-09-30 14:09:14 +00:00
parent f14057a623
commit aa67784fe2
2 changed files with 51 additions and 13 deletions

View File

@ -140,8 +140,10 @@ class EnvProcessor:
self.variables = self._load_env_file(env_file)
def _load_env_file(self, env_file: Path) -> Dict[str, str]:
"""Charge le fichier .env"""
"""Charge uniquement le fichier .env principal (pas les sous-répertoires)"""
variables = {}
# Charger uniquement le fichier .env principal
if env_file.exists():
try:
with open(env_file, 'r', encoding='utf-8') as f:
@ -150,8 +152,13 @@ class EnvProcessor:
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
variables[key.strip()] = value.strip()
logger.info(f"Variables chargées depuis {env_file}: {len(variables)} variables")
except Exception as e:
logger.error(f"Erreur lors du chargement du fichier .env: {e}")
# Note: Les fichiers .env des sous-répertoires ne sont PAS chargés
# car ils sont des configurations spécifiques aux services, pas des variables globales
return variables
def _resolve_variable(self, var_name: str, visited: set = None) -> str:
@ -161,34 +168,54 @@ class EnvProcessor:
if var_name in visited:
logger.warning(f"Dépendance circulaire détectée pour {var_name}")
return f"${{{var_name}}}"
return f"${var_name}"
if var_name not in self.variables:
logger.warning(f"Variable non trouvée: {var_name}")
return f"${{{var_name}}}"
return f"${var_name}"
visited.add(var_name)
value = self.variables[var_name]
# Traitement des sous-variables
pattern = r'\$\{([^}]+)\}'
matches = re.findall(pattern, value)
# Traitement des sous-variables avec ${VAR}
pattern1 = r'\$\{([^}]+)\}'
matches1 = re.findall(pattern1, value)
for match in matches:
for match in matches1:
resolved_value = self._resolve_variable(match, visited.copy())
value = value.replace(f"${{{match}}}", resolved_value)
# Traitement des sous-variables avec $VAR
pattern2 = r'\$([A-Za-z_][A-Za-z0-9_]*)'
matches2 = re.findall(pattern2, value)
for match in matches2:
if match != var_name: # Éviter l'auto-référence
resolved_value = self._resolve_variable(match, visited.copy())
value = value.replace(f"${match}", resolved_value)
return value
def process_content(self, content: str) -> str:
"""Traite le contenu en résolvant les variables"""
pattern = r'\$\{([^}]+)\}'
matches = re.findall(pattern, content)
# Pattern pour ${VARIABLE}
pattern1 = r'\$\{([^}]+)\}'
matches1 = re.findall(pattern1, content)
for var_name in matches:
for var_name in matches1:
resolved_value = self._resolve_variable(var_name)
content = content.replace(f"${{{var_name}}}", resolved_value)
# Pattern pour $VARIABLE (syntaxe simple)
pattern2 = r'\$([A-Za-z_][A-Za-z0-9_]*)'
matches2 = re.findall(pattern2, content)
for var_name in matches2:
# Éviter les variables déjà traitées avec ${}
if f"${{{var_name}}}" not in content:
resolved_value = self._resolve_variable(var_name)
content = content.replace(f"${var_name}", resolved_value)
return content
class SecureVaultAPI:
@ -196,8 +223,18 @@ class SecureVaultAPI:
def __init__(self):
self.app = Flask(__name__)
# Initialisation des processeurs d'environnement pour chaque env
self.env_processors = {}
self._setup_routes()
def _get_env_processor(self, env: str) -> EnvProcessor:
"""Obtient le processeur d'environnement pour un environnement donné"""
if env not in self.env_processors:
env_file = STORAGE_ROOT / env / '.env'
self.env_processors[env] = EnvProcessor(env_file)
logger.info(f"Processeur d'environnement initialisé pour {env}")
return self.env_processors[env]
def _setup_routes(self):
"""Configure les routes de l'API"""
@ -338,8 +375,9 @@ class SecureVaultAPI:
if file_content is None:
return jsonify({"error": f"Fichier non trouvé: {env}/{file_path}"}), 404
# Contenu du fichier sans traitement de variables d'environnement
processed_content = file_content
# Traitement des variables d'environnement (en mémoire, sans modifier le fichier)
env_processor = self._get_env_processor(env)
processed_content = env_processor.process_content(file_content)
# Chiffrement avec la clé utilisateur pour cet environnement
encrypted_content, next_key = self._encrypt_with_user_key_and_next(processed_content, user_id, env)