import customtkinter as ctk from tkinter import filedialog, messagebox import os import threading import subprocess from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler import time # --- Configuration du Design --- ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") 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) self.app.trigger_realtime_scan(event.src_path) class Antivirus7LnA(ctk.CTk): def __init__(self): super().__init__() self.title("7LnA Security Suite - Real-Time Edition") self.geometry("1000x650") self.minsize(800, 500) # --- Configuration Système --- self.quarantine_dir = os.path.expanduser("~/.7lna_quarantine") os.makedirs(self.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() def check_dependencies(self): try: subprocess.run(['clamscan', '--version'], capture_output=True, check=True) 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 --- 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)) 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_audit = self.create_nav_button("⚙️ Audit Système", 4, "audit") self.btn_update = self.create_nav_button("🔄 Mise à jour Base", 5, "update") self.version_label = ctk.CTkLabel(self.sidebar, text="Version 5.0 - Active Shield", text_color="gray") self.version_label.grid(row=6, column=0, pady=20, sticky="s") self.views = {} self.init_dashboard_view() self.init_scanner_view() self.init_realtime_view() self.init_audit_view() self.init_update_view() self.select_view("dashboard") def create_nav_button(self, text, row, view_name): btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent", text_color=("gray10", "#DCE4EE"), hover_color=("gray70", "gray30"), command=lambda: self.select_view(view_name)) btn.grid(row=row, column=0, padx=20, pady=10, sticky="ew") return btn def select_view(self, view_name): for view in self.views.values(): view.grid_forget() 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 # ========================================== def init_dashboard_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") self.views["dashboard"] = frame ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=24, weight="bold")).pack(anchor="w", pady=(0, 20)) 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" 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) # ========================================== # MODULE : SCANNER MANUEL # ========================================== def init_scanner_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") frame.grid_rowconfigure(2, weight=1) frame.grid_columnconfigure((0, 1), weight=1) self.views["scanner"] = frame 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.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.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") def start_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() def run_clamav_engine(self, path, is_dir, console): console.insert("end", f"\n🚀 Lancement de l'analyse sur : {path}\n") console.see("end") try: cmd = ['clamscan', '-i', '--no-summary', f'--move={self.quarantine_dir}'] if is_dir: cmd.append('-r') cmd.append(path) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) infected = 0 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") infected += 1 else: console.insert("end", clean_line + "\n") console.see("end") process.wait() if infected == 0: console.insert("end", "✅ Analyse terminée. Système propre.\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.see("end") # ========================================== # MODULE : BOUCLIER TEMPS RÉEL (NOUVEAU) # ========================================== 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["realtime"] = 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") 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.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 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() 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) 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) if __name__ == "__main__": app = Antivirus7LnA() app.mainloop()