diff --git a/7lna.py b/7lna.py index d4ace3d..855ad93 100644 --- a/7lna.py +++ b/7lna.py @@ -3,43 +3,51 @@ from tkinter import filedialog, messagebox import os import threading import subprocess +import urllib.request +import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler -import time -# --- Configuration du Design --- +# ========================================== +# CONFIGURATION GLOBALE +# ========================================== +# Lien RAW officiel de ton dépôt Gitea +UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py" + +QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine") +WATCH_FOLDER = os.path.expanduser("~/Téléchargements") + +# Design global ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") +# ========================================== +# GESTIONNAIRE DU BOUCLIER TEMPS RÉEL +# ========================================== class RealTimeShieldHandler(FileSystemEventHandler): - """Gère les événements système : quand un fichier est créé.""" def __init__(self, app_instance): self.app = app_instance def on_created(self, event): - # On ignore les dossiers, on ne scanne que les fichiers if not event.is_directory: - # Petit délai pour laisser le temps au téléchargement de se terminer - time.sleep(1) + time.sleep(1.5) # Laisse le temps au téléchargement de se terminer self.app.trigger_realtime_scan(event.src_path) +# ========================================== +# APPLICATION PRINCIPALE +# ========================================== class Antivirus7LnA(ctk.CTk): def __init__(self): super().__init__() - self.title("7LnA Security Suite - Real-Time Edition") - self.geometry("1000x650") - self.minsize(800, 500) + self.title("7LnA Security Suite") + self.geometry("1050x650") + self.minsize(850, 550) - # --- Configuration Système --- - self.quarantine_dir = os.path.expanduser("~/.7lna_quarantine") - os.makedirs(self.quarantine_dir, exist_ok=True) + # Création du dossier de quarantaine + os.makedirs(QUARANTINE_DIR, exist_ok=True) - # Variables du Bouclier Temps Réel self.shield_observer = None self.shield_active = False - self.watch_folder = os.path.expanduser("~/Téléchargements") # Par défaut sur Ubuntu FR - if not os.path.exists(self.watch_folder): - self.watch_folder = os.path.expanduser("~/Downloads") # Fallback en anglais self.check_dependencies() self.setup_ui() @@ -50,32 +58,28 @@ class Antivirus7LnA(ctk.CTk): self.clamav_installed = True except (subprocess.CalledProcessError, FileNotFoundError): self.clamav_installed = False - messagebox.showwarning("Alerte Moteur", "ClamAV n'est pas détecté. Installez-le avec : sudo apt install clamav") - # ========================================== - # ARCHITECTURE DE L'INTERFACE - # ========================================== def setup_ui(self): self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) - # --- Menu Latéral --- + # --- BARRE LATÉRALE --- self.sidebar = ctk.CTkFrame(self, width=220, corner_radius=0) self.sidebar.grid(row=0, column=0, sticky="nsew") self.sidebar.grid_rowconfigure(6, weight=1) - self.logo = ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA", font=ctk.CTkFont(size=28, weight="bold")) - self.logo.grid(row=0, column=0, padx=20, pady=(30, 30)) + ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA", font=ctk.CTkFont(size=28, weight="bold")).grid(row=0, column=0, padx=20, pady=30) - self.btn_dashboard = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard") - self.btn_scanner = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner") - self.btn_realtime = self.create_nav_button("⚡ Bouclier Temps Réel", 3, "realtime") + self.btn_dash = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard") + self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner") + self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield") self.btn_audit = self.create_nav_button("⚙️ Audit Système", 4, "audit") - self.btn_update = self.create_nav_button("🔄 Mise à jour Base", 5, "update") + self.btn_update = self.create_nav_button("🔄 Mise à jour", 5, "update") - self.version_label = ctk.CTkLabel(self.sidebar, text="Version 5.0 - Active Shield", text_color="gray") + self.version_label = ctk.CTkLabel(self.sidebar, text="Version 5.0 - Ultimate", text_color="gray") self.version_label.grid(row=6, column=0, pady=20, sticky="s") + # --- CONTENEUR DES VUES --- self.views = {} self.init_dashboard_view() self.init_scanner_view() @@ -98,9 +102,7 @@ class Antivirus7LnA(ctk.CTk): if view_name in self.views: self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=20, pady=20) - # ========================================== - # MODULE : TABLEAU DE BORD - # ========================================== + # --- VUE : TABLEAU DE BORD --- def init_dashboard_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") self.views["dashboard"] = frame @@ -110,12 +112,12 @@ class Antivirus7LnA(ctk.CTk): status_card = ctk.CTkFrame(frame, fg_color="#1E8449" if self.clamav_installed else "#C0392B", corner_radius=15) status_card.pack(fill="x", pady=10, ipady=20) - status_text = "Moteur ClamAV Connecté" if self.clamav_installed else "Moteur Hors Ligne" + status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable" ctk.CTkLabel(status_card, text=f"{'✅' if self.clamav_installed else '❌'} {status_text}", font=ctk.CTkFont(size=18, weight="bold"), text_color="white").pack(expand=True) + + ctk.CTkLabel(frame, text=f"📂 Dossier de quarantaine : {QUARANTINE_DIR}").pack(anchor="w", pady=10) - # ========================================== - # MODULE : SCANNER MANUEL - # ========================================== + # --- VUE : SCANNER MANUEL --- def init_scanner_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") frame.grid_rowconfigure(2, weight=1) @@ -124,26 +126,70 @@ class Antivirus7LnA(ctk.CTk): ctk.CTkLabel(frame, text="Analyse Antivirus Manuelle", font=ctk.CTkFont(size=24, weight="bold")).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 20)) - self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser un Fichier", command=lambda: self.start_scan(is_dir=False), height=40) + self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser un Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=40) self.btn_scan_f.grid(row=1, column=0, padx=(0, 10), sticky="ew") - self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser un Dossier", command=lambda: self.start_scan(is_dir=True), height=40, fg_color="#2E86C1") + self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser un Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=40, fg_color="#2E86C1") self.btn_scan_d.grid(row=1, column=1, padx=(10, 0), sticky="ew") self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13)) self.scan_console.grid(row=2, column=0, columnspan=2, pady=20, sticky="nsew") - self.scan_console.insert("end", "[*] Prêt pour une analyse manuelle.\n") + self.scan_console.insert("end", "[*] Prêt. Les menaces seront automatiquement isolées.\n") - def start_scan(self, is_dir): + def start_manual_scan(self, is_dir): path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename() if path: - threading.Thread(target=self.run_clamav_engine, args=(path, is_dir, self.scan_console), daemon=True).start() + threading.Thread(target=self.run_clamav_scan, args=(path, is_dir, self.scan_console), daemon=True).start() - def run_clamav_engine(self, path, is_dir, console): - console.insert("end", f"\n🚀 Lancement de l'analyse sur : {path}\n") + # --- VUE : BOUCLIER TEMPS RÉEL --- + def init_realtime_view(self): + frame = ctk.CTkFrame(self, fg_color="transparent") + frame.grid_rowconfigure(2, weight=1) + frame.grid_columnconfigure(0, weight=1) + self.views["shield"] = frame + + header = ctk.CTkFrame(frame, fg_color="transparent") + header.grid(row=0, column=0, sticky="ew", pady=(0, 20)) + + ctk.CTkLabel(header, text="⚡ Bouclier Actif", font=ctk.CTkFont(size=24, weight="bold")).pack(side="left") + self.btn_toggle_shield = ctk.CTkButton(header, text="Activer le Bouclier", fg_color="#27AE60", hover_color="#1E8449", command=self.toggle_shield) + self.btn_toggle_shield.pack(side="right") + + ctk.CTkLabel(frame, text=f"Surveillance en direct de : {WATCH_FOLDER}\nLes nouveaux fichiers seront scannés instantanément.", justify="left").grid(row=1, column=0, sticky="w", pady=(0, 10)) + + self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13)) + self.rt_console.grid(row=2, column=0, sticky="nsew") + self.rt_console.insert("end", "[-] Bouclier désactivé. Cliquez sur le bouton pour démarrer.\n") + + def toggle_shield(self): + if not self.shield_active: + self.shield_observer = Observer() + self.shield_observer.schedule(RealTimeShieldHandler(self), WATCH_FOLDER, recursive=False) + self.shield_observer.start() + self.shield_active = True + self.btn_toggle_shield.configure(text="Désactiver le Bouclier", fg_color="#C0392B", hover_color="#922B21") + self.rt_console.insert("end", f"\n[+] BOUCLIER ACTIVÉ sur {WATCH_FOLDER}\n[*] Analyse automatique prête...\n") + else: + self.shield_observer.stop() + self.shield_active = False + self.btn_toggle_shield.configure(text="Activer le Bouclier", fg_color="#27AE60", hover_color="#1E8449") + self.rt_console.insert("end", "\n[-] Bouclier DÉSACTIVÉ.\n") + self.rt_console.see("end") + + def trigger_realtime_scan(self, filepath): + self.rt_console.insert("end", f"\n⚡ Détection auto : {os.path.basename(filepath)}\n") + threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start() + + # --- MOTEUR CLAMAV (Commun au Manuel et au Temps Réel) --- + def run_clamav_scan(self, path, is_dir, console): + if not self.clamav_installed: + console.insert("end", "❌ Erreur : ClamAV n'est pas installé.\n") + return + + console.insert("end", f"[*] Début de l'analyse : {path}\n") console.see("end") try: - cmd = ['clamscan', '-i', '--no-summary', f'--move={self.quarantine_dir}'] + cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'] if is_dir: cmd.append('-r') cmd.append(path) @@ -152,7 +198,7 @@ class Antivirus7LnA(ctk.CTk): for line in process.stdout: clean_line = line.strip() if "FOUND" in clean_line: - console.insert("end", f"☠️ DANGER : {clean_line}\n🛡️ Fichier isolé en quarantaine.\n") + console.insert("end", f"☠️ DANGER : {clean_line}\n🛡️ Isolé en quarantaine.\n") infected += 1 else: console.insert("end", clean_line + "\n") @@ -160,82 +206,79 @@ class Antivirus7LnA(ctk.CTk): process.wait() if infected == 0: - console.insert("end", "✅ Analyse terminée. Système propre.\n") + console.insert("end", "[+] Analyse terminée. Fichier(s) propre(s).\n") else: console.insert("end", f"🚨 {infected} menace(s) neutralisée(s) !\n") except Exception as e: - console.insert("end", f"❌ Erreur : {e}\n") + console.insert("end", f"❌ Erreur système : {e}\n") console.see("end") - # ========================================== - # MODULE : BOUCLIER TEMPS RÉEL (NOUVEAU) - # ========================================== - def init_realtime_view(self): + # --- VUE : AUDIT SYSTÈME --- + def init_audit_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") - frame.grid_rowconfigure(2, weight=1) + frame.grid_rowconfigure(1, weight=1) frame.grid_columnconfigure(0, weight=1) - self.views["realtime"] = frame + self.views["audit"] = frame header = ctk.CTkFrame(frame, fg_color="transparent") header.grid(row=0, column=0, sticky="ew", pady=(0, 20)) - ctk.CTkLabel(header, text="⚡ Bouclier Actif (Temps Réel)", font=ctk.CTkFont(size=24, weight="bold")).pack(side="left") - - self.btn_toggle_shield = ctk.CTkButton(header, text="Activer le Bouclier", fg_color="#27AE60", hover_color="#1E8449", command=self.toggle_shield) - self.btn_toggle_shield.pack(side="right") + ctk.CTkLabel(header, text="Vérification des Vulnérabilités", font=ctk.CTkFont(size=24, weight="bold")).pack(side="left") + ctk.CTkButton(header, text="Lancer l'Audit", command=self.run_audit_thread).pack(side="right") - info_text = f"Dossier surveillé en permanence : {self.watch_folder}\nTout nouveau fichier sera scanné et neutralisé automatiquement." - ctk.CTkLabel(frame, text=info_text, justify="left").grid(row=1, column=0, sticky="w", pady=(0, 10)) + self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13)) + self.audit_console.grid(row=1, column=0, sticky="nsew") - self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13)) - self.rt_console.grid(row=2, column=0, sticky="nsew") - self.rt_console.insert("end", "[-] Bouclier désactivé. Cliquez sur le bouton pour l'activer.\n") + def run_audit_thread(self): + self.audit_console.delete("0.0", "end") + threading.Thread(target=self.perform_audit, daemon=True).start() - def toggle_shield(self): - if not self.shield_active: - # Activer - event_handler = RealTimeShieldHandler(self) - self.shield_observer = Observer() - self.shield_observer.schedule(event_handler, self.watch_folder, recursive=False) - self.shield_observer.start() + def perform_audit(self): + self.audit_console.insert("end", "[*] Ports réseaux ouverts (Recherche de Backdoors)...\n") + try: + self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True)) + except Exception: + self.audit_console.insert("end", "[-] Impossible de lire le réseau.\n") - self.shield_active = True - self.btn_toggle_shield.configure(text="Désactiver le Bouclier", fg_color="#C0392B", hover_color="#922B21") - self.rt_console.insert("end", f"\n[+] Bouclier ACTIVÉ sur {self.watch_folder}\n[*] En attente de nouveaux fichiers...\n") - else: - # Désactiver - self.shield_observer.stop() - self.shield_observer.join() - self.shield_active = False - self.btn_toggle_shield.configure(text="Activer le Bouclier", fg_color="#27AE60", hover_color="#1E8449") - self.rt_console.insert("end", "\n[-] Bouclier DÉSACTIVÉ.\n") - self.rt_console.see("end") - - def trigger_realtime_scan(self, filepath): - """Appelé automatiquement quand un nouveau fichier apparaît.""" - self.rt_console.insert("end", f"\n⚡ Nouveau fichier détecté : {os.path.basename(filepath)}\n") - self.rt_console.insert("end", "[*] Analyse instantanée en cours...\n") - self.rt_console.see("end") - - # On lance le scan dans un thread pour ne pas bloquer l'interface - threading.Thread(target=self.run_clamav_engine, args=(filepath, False, self.rt_console), daemon=True).start() - - # ========================================== - # MODULES : AUDIT & UPDATE (Abregés pour l'exemple) - # ========================================== - def init_audit_view(self): - frame = ctk.CTkFrame(self, fg_color="transparent") - self.views["audit"] = frame - ctk.CTkLabel(frame, text="Audit Système", font=ctk.CTkFont(size=24, weight="bold")).pack(anchor="w") - ctk.CTkLabel(frame, text="Interface d'audit maintenue...").pack(pady=20) + self.audit_console.insert("end", "\n[*] Top 10 des processus gourmands...\n") + try: + res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True) + self.audit_console.insert("end", "\n".join(res.split('\n')[:11]) + "\n") + except Exception: + self.audit_console.insert("end", "[-] Impossible de lire les processus.\n") + # --- VUE : MISE À JOUR (OTA) --- def init_update_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") self.views["update"] = frame - ctk.CTkLabel(frame, text="Mise à jour (FreshClam)", font=ctk.CTkFont(size=24, weight="bold")).pack(anchor="w") - ctk.CTkLabel(frame, text="Utilisez 'sudo freshclam' dans le terminal.").pack(pady=20) + + ctk.CTkLabel(frame, text="Mises à jour de 7LnA", font=ctk.CTkFont(size=24, weight="bold")).pack(anchor="w", pady=(0, 20)) + ctk.CTkLabel(frame, text="Vérifiez si une nouvelle version est disponible sur le dépôt officiel.\nL'application se mettra à jour automatiquement.", justify="left").pack(anchor="w", pady=10) + + self.btn_update = ctk.CTkButton(frame, text="⬇️ Télécharger la dernière version", command=self.run_software_update, fg_color="#8E44AD", hover_color="#732D91") + self.btn_update.pack(anchor="w", pady=20) + + self.update_console = ctk.CTkTextbox(frame, height=150, font=ctk.CTkFont(family="Consolas", size=13)) + self.update_console.pack(fill="x", pady=10) + + def run_software_update(self): + self.btn_update.configure(state="disabled") + self.update_console.insert("end", "[*] Connexion au serveur Git...\n") + + try: + # os.path.abspath(__file__) trouve automatiquement le chemin exact du fichier en cours d'exécution + current_file_path = os.path.abspath(__file__) + urllib.request.urlretrieve(UPDATE_URL, current_file_path) + + self.update_console.insert("end", "[+] Mise à jour téléchargée et appliquée !\n") + messagebox.showinfo("Mise à jour", "7LnA a été mis à jour avec succès. L'application va se fermer pour appliquer les changements.") + self.destroy() + + except Exception as e: + self.update_console.insert("end", f"[-] Échec de la mise à jour : {e}\n") + self.btn_update.configure(state="normal") if __name__ == "__main__": app = Antivirus7LnA() - app.mainloop() + app.mainloop() \ No newline at end of file