From 9b7e95ee65cbaf941d19ce89d1be3341c6d32b64 Mon Sep 17 00:00:00 2001 From: 7ka1 <7ka1@noreply.localhost> Date: Fri, 22 May 2026 12:55:40 +0000 Subject: [PATCH] Actualiser 7lna.py --- 7lna.py | 596 +++++++++++++++++++++++++++----------------------------- 1 file changed, 289 insertions(+), 307 deletions(-) diff --git a/7lna.py b/7lna.py index 8992d21..e4735d6 100644 --- a/7lna.py +++ b/7lna.py @@ -1,19 +1,6 @@ import customtkinter as ctk from tkinter import filedialog, messagebox -import os -import threading -import subprocess -import urllib.request -import urllib.error -import time -import datetime -import glob -import hashlib -import string -import secrets -import json -import platform -import shutil +import os, threading, subprocess, urllib.request, urllib.error, time, datetime, glob, json, platform, shutil from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler @@ -25,8 +12,16 @@ QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine") WATCH_FOLDER = os.path.expanduser("~/Téléchargements") HISTORY_FILE = os.path.expanduser("~/.7lna_history.json") +# Palette High-Tech 2026 +BG_MAIN = "#0B0F19" # Fond ultra sombre +BG_CARD = "#161E2E" # Fond des cartes +BG_SIDEBAR = "#090B10" # Fond barre latérale +ACCENT_BLUE = "#3B82F6" # Bleu électrique +ACCENT_VIOLET = "#8B5CF6" # Violet néon (Zero USB) +TEXT_MAIN = "#F3F4F6" +TEXT_MUTED = "#9CA3AF" + ctk.set_appearance_mode("dark") -ctk.set_default_color_theme("blue") # ========================================== # UTILITAIRES SYSTÈME @@ -34,7 +29,7 @@ ctk.set_default_color_theme("blue") def send_desktop_notification(title, message, is_critical=False): try: urgency = 'critical' if is_critical else 'normal' - subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message]) + subprocess.Popen(['notify-send', '-u', urgency, '-a', '7lna Antivirus', title, message]) except Exception: pass @@ -52,9 +47,10 @@ class RealTimeShieldHandler(FileSystemEventHandler): class Antivirus7LnA(ctk.CTk): def __init__(self): super().__init__() - self.title("7LnA Security Suite - V10.1 Quantum Edition") + self.title("7lna Antivirus") self.geometry("1250x850") - self.minsize(1000, 700) + self.minsize(1050, 750) + self.configure(fg_color=BG_MAIN) os.makedirs(QUARANTINE_DIR, exist_ok=True) if not os.path.exists(HISTORY_FILE): @@ -62,7 +58,7 @@ class Antivirus7LnA(ctk.CTk): self.shield_observer = None self.shield_active = False - self.zero_usb_mode = False # <--- AJOUT : État du mode Zero USB + self.zero_usb_mode = False self.check_dependencies() self.setup_ui() @@ -76,30 +72,44 @@ class Antivirus7LnA(ctk.CTk): except: self.clamav_installed = False + # ========================================== + # INTERFACE GRAPHIQUE (DESIGN 2026) + # ========================================== def setup_ui(self): self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) # --- BARRE LATÉRALE --- - self.sidebar = ctk.CTkFrame(self, width=260, corner_radius=0, fg_color="#111827") + self.sidebar = ctk.CTkFrame(self, width=280, corner_radius=0, fg_color=BG_SIDEBAR) self.sidebar.grid(row=0, column=0, sticky="nsew") self.sidebar.grid_rowconfigure(10, weight=1) - ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA Sec", font=ctk.CTkFont(size=30, weight="bold"), text_color="#3B82F6").grid(row=0, column=0, padx=20, pady=(30, 20)) + # Logo + logo_frame = ctk.CTkFrame(self.sidebar, fg_color="transparent") + logo_frame.grid(row=0, column=0, pady=(40, 30), sticky="ew") + ctk.CTkLabel(logo_frame, text="🛡️ 7lna", font=ctk.CTkFont(size=36, weight="bold"), text_color=ACCENT_BLUE).pack() + ctk.CTkLabel(logo_frame, text="A N T I V I R U S", font=ctk.CTkFont(size=12, weight="bold", letter_spacing=2), text_color=ACCENT_VIOLET).pack() - 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 Réseau", 4, "audit") - self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools") - self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine") - self.btn_schedule = self.create_nav_button("📅 Planification", 7, "schedule") - self.btn_history = self.create_nav_button("📜 Rapports", 8, "history") - self.btn_update = self.create_nav_button("🔄 Mise à jour", 9, "update") + # Navigation + self.nav_buttons = {} + self.create_nav_button("📊 Tableau de Bord", 1, "dashboard") + self.create_nav_button("🔍 Scanner Système", 2, "scanner") + self.create_nav_button("⚡ Bouclier Actif", 3, "shield") + self.create_nav_button("🌐 Audit & Réseau", 4, "audit") + self.create_nav_button("🧰 Boîte à Outils", 5, "tools") + self.create_nav_button("📦 Quarantaine", 6, "quarantine") + self.create_nav_button("📅 Automatisation", 7, "schedule") + self.create_nav_button("📜 Rapports", 8, "history") + + # Bouton Mise à jour (Mise en évidence) + self.btn_update = ctk.CTkButton(self.sidebar, text="🔄 Rechercher MAJ", command=lambda: self.select_view("update"), + fg_color="#1E3A8A", hover_color=ACCENT_BLUE, font=ctk.CTkFont(size=14, weight="bold"), height=45) + self.btn_update.grid(row=9, column=0, padx=20, pady=20, sticky="ew") - self.version_label = ctk.CTkLabel(self.sidebar, text="v10.1 - Quantum", text_color="#6B7280", font=ctk.CTkFont(weight="bold")) + self.version_label = ctk.CTkLabel(self.sidebar, text="V10.1 Quantum Edition", text_color=TEXT_MUTED, font=ctk.CTkFont(size=11)) self.version_label.grid(row=10, column=0, pady=20, sticky="s") + # --- VUES --- self.views = {} self.init_dashboard_view() self.init_scanner_view() @@ -115,15 +125,24 @@ class Antivirus7LnA(ctk.CTk): def create_nav_button(self, text, row, view_name): btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent", - text_color="#D1D5DB", hover_color="#1F2937", font=ctk.CTkFont(size=15), height=40, + text_color=TEXT_MUTED, hover_color=BG_CARD, font=ctk.CTkFont(size=15, weight="bold"), height=45, command=lambda: self.select_view(view_name)) - btn.grid(row=row, column=0, padx=20, pady=5, sticky="ew") - return btn + btn.grid(row=row, column=0, padx=15, pady=4, sticky="ew") + self.nav_buttons[view_name] = btn def select_view(self, view_name): + # Réinitialiser les couleurs des boutons + for name, btn in self.nav_buttons.items(): + btn.configure(fg_color="transparent", text_color=TEXT_MUTED) + + # Activer le bouton cliqué (s'il existe dans le dictionnaire) + if view_name in self.nav_buttons: + self.nav_buttons[view_name].configure(fg_color=BG_CARD, text_color=TEXT_MAIN) + + # Afficher la vue 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=30, pady=30) + self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=40, pady=40) if view_name == "quarantine": self.refresh_quarantine_list() if view_name == "history": self.refresh_history_list() @@ -131,7 +150,8 @@ class Antivirus7LnA(ctk.CTk): console.tag_config("danger", foreground="#EF4444") console.tag_config("success", foreground="#10B981") console.tag_config("warning", foreground="#F59E0B") - console.tag_config("info", foreground="#3B82F6") + console.tag_config("info", foreground=ACCENT_BLUE) + console.tag_config("violet", foreground=ACCENT_VIOLET) def get_time_prefix(self): return datetime.datetime.now().strftime("[%H:%M:%S] ") @@ -143,7 +163,9 @@ class Antivirus7LnA(ctk.CTk): with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4) except: pass - # --- AUTO-DETECT USB --- + # ========================================== + # LOGIQUE USB & MOTEUR + # ========================================== def monitor_usb_drives(self): media_dir = f"/media/{os.getlogin()}" if not os.path.exists(media_dir): return @@ -158,146 +180,122 @@ class Antivirus7LnA(ctk.CTk): known_mounts = current_mounts except: pass - # <--- MODIFICATION : Logique Zero USB intégrée def prompt_usb_scan(self, path, name): if self.zero_usb_mode: - send_desktop_notification("⚡ Zero USB Actif", f"Analyse automatique forcée pour : {name}", is_critical=True) + send_desktop_notification("⚡ Zero USB Actif", f"Analyse auto forcée : {name}", is_critical=True) self.select_view("scanner") + self.scan_console.insert("end", f"\n{self.get_time_prefix()}[⚡ ZERO USB] Scan automatique déclenché.\n", "violet") threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start() else: send_desktop_notification("USB Détectée", f"Disque {name} branché.") - if messagebox.askyesno("Protection USB", f"Nouveau périphérique USB détecté :\n{name}\n\nVoulez-vous l'analyser ?"): + if messagebox.askyesno("Protection USB", f"Nouveau périphérique détecté :\n{name}\n\nVoulez-vous l'analyser ?"): self.select_view("scanner") threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start() - # --- VUE : 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=34, weight="bold")).pack(anchor="w", pady=(0, 20)) - - status_card = ctk.CTkFrame(frame, fg_color="#064E3B" if self.clamav_installed else "#7F1D1D", corner_radius=15) - status_card.pack(fill="x", pady=10, ipady=20) - 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=24, weight="bold"), text_color="white").pack(expand=True) - - sys_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) - sys_frame.pack(fill="x", pady=10, ipady=10) - sys_info = f"🖥️ OS : {platform.system()} {platform.release()} | 👤 Compte : {os.getlogin()}" - ctk.CTkLabel(sys_frame, text=sys_info, font=ctk.CTkFont(size=16, weight="bold")).pack(padx=20, pady=10, anchor="w") - - # <--- AJOUT : Switch pour activer/désactiver le Mode Zero USB - self.zero_usb_switch = ctk.CTkSwitch(frame, text="🛡️ Mode Zero USB (Scan auto des clés USB)", - command=self.toggle_zero_usb, - font=ctk.CTkFont(size=16, weight="bold"), - progress_color="#DC2626") # Rouge pour indiquer un mode "agressif" - self.zero_usb_switch.pack(pady=20, anchor="w", padx=20) - - # <--- AJOUT : Fonction de bascule pour le Switch - def toggle_zero_usb(self): - self.zero_usb_mode = self.zero_usb_switch.get() - etat = "ACTIVÉ" if self.zero_usb_mode else "DÉSACTIVÉ" - send_desktop_notification("Paramètre de Sécurité", f"Mode Zero USB {etat}") - - # --- FONCTION D'AUTHENTIFICATION GLOBALE --- - def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis pour cette action.\nEntrez votre mot de passe session :"): + def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis.\nEntrez votre mot de passe :"): dialog = ctk.CTkToplevel(self) dialog.title(title) - dialog.geometry("400x230") + dialog.geometry("400x250") + dialog.configure(fg_color=BG_CARD) - ctk.CTkLabel(dialog, text="⚠️ Privilèges Requis", font=ctk.CTkFont(size=20, weight="bold"), text_color="#F59E0B").pack(pady=(20, 5)) - ctk.CTkLabel(dialog, text=msg, justify="center").pack(pady=5) + ctk.CTkLabel(dialog, text="⚠️ Élévation de Privilèges", font=ctk.CTkFont(size=20, weight="bold"), text_color="#F59E0B").pack(pady=(20, 10)) + ctk.CTkLabel(dialog, text=msg, justify="center", text_color=TEXT_MUTED).pack(pady=5) - pwd_entry = ctk.CTkEntry(dialog, show="*", width=250) - pwd_entry.pack(pady=10) + pwd_entry = ctk.CTkEntry(dialog, show="*", width=250, height=40, border_color=ACCENT_BLUE) + pwd_entry.pack(pady=15) def on_submit(event=None): pwd = pwd_entry.get() dialog.destroy() - if pwd: - callback_func(pwd) + if pwd: callback_func(pwd) - ctk.CTkButton(dialog, text="Confirmer", command=on_submit, fg_color="#DC2626", hover_color="#991B1B").pack(pady=10) + ctk.CTkButton(dialog, text="Autoriser", command=on_submit, fg_color=ACCENT_BLUE, hover_color="#2563EB", height=40).pack(pady=5) dialog.bind("", on_submit) - dialog.wait_visibility() dialog.attributes("-topmost", True) dialog.grab_set() pwd_entry.focus_set() - # --- VUE : SCANNER MANUEL --- + # ========================================== + # VUES (DASHBOARD, SCANNER, ETC.) + # ========================================== + def init_dashboard_view(self): + frame = ctk.CTkFrame(self, fg_color="transparent") + frame.grid_columnconfigure((0, 1), weight=1) + self.views["dashboard"] = frame + + ctk.CTkLabel(frame, text="Vue d'ensemble", font=ctk.CTkFont(size=38, weight="bold"), text_color=TEXT_MAIN).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 30)) + + # Carte Statut + status_color = "#064E3B" if self.clamav_installed else "#7F1D1D" + status_card = ctk.CTkFrame(frame, fg_color=status_color, corner_radius=15) + status_card.grid(row=1, column=0, sticky="nsew", padx=(0, 10), pady=10, ipady=30) + ctk.CTkLabel(status_card, text="Moteur Antivirus", font=ctk.CTkFont(size=16), text_color=TEXT_MUTED).pack(pady=(20, 5)) + status_text = "OPÉRATIONNEL" if self.clamav_installed else "HORS LIGNE" + ctk.CTkLabel(status_card, text=f"{'✅' if self.clamav_installed else '❌'} {status_text}", font=ctk.CTkFont(size=28, weight="bold"), text_color="white").pack() + + # Carte Système + sys_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15) + sys_card.grid(row=1, column=1, sticky="nsew", padx=(10, 0), pady=10, ipady=30) + ctk.CTkLabel(sys_card, text="Environnement", font=ctk.CTkFont(size=16), text_color=TEXT_MUTED).pack(pady=(20, 5)) + sys_info = f"{platform.system()} {platform.release()}" + ctk.CTkLabel(sys_card, text=sys_info, font=ctk.CTkFont(size=22, weight="bold"), text_color=ACCENT_BLUE).pack() + ctk.CTkLabel(sys_card, text=f"Session : {os.getlogin()}", font=ctk.CTkFont(size=14), text_color=TEXT_MUTED).pack(pady=5) + + # Carte Zero USB + usb_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15, border_width=2, border_color=ACCENT_VIOLET) + usb_card.grid(row=2, column=0, columnspan=2, sticky="nsew", pady=20, ipady=20) + ctk.CTkLabel(usb_card, text="Protection des Périphériques", font=ctk.CTkFont(size=20, weight="bold")).pack(pady=(20,10)) + + self.zero_usb_switch = ctk.CTkSwitch(usb_card, text="Activer le Mode Zero USB (Scan Auto)", + command=self.toggle_zero_usb, font=ctk.CTkFont(size=16, weight="bold"), + progress_color=ACCENT_VIOLET, button_color="#FFFFFF") + self.zero_usb_switch.pack(pady=10) + + def toggle_zero_usb(self): + self.zero_usb_mode = self.zero_usb_switch.get() + etat = "ARMÉ" if self.zero_usb_mode else "DÉSACTIVÉ" + send_desktop_notification("Bouclier Quantum", f"Mode Zero USB {etat}") + def init_scanner_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") - frame.grid_rowconfigure(4, weight=1) + frame.grid_rowconfigure(3, weight=1) frame.grid_columnconfigure((0, 1, 2), weight=1) self.views["scanner"] = frame - ctk.CTkLabel(frame, text="Analyse Profonde", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 20)) + ctk.CTkLabel(frame, text="Analyse Système", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 30)) - self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=45, fg_color="#2563EB", hover_color="#1D4ED8") + self.btn_scan_f = ctk.CTkButton(frame, text="📄 Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=50, fg_color=BG_CARD, hover_color="#374151", border_width=1, border_color=ACCENT_BLUE) self.btn_scan_f.grid(row=1, column=0, padx=(0, 5), pady=5, sticky="ew") - self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=45, fg_color="#4F46E5", hover_color="#4338CA") + self.btn_scan_d = ctk.CTkButton(frame, text="📁 Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=50, fg_color=BG_CARD, hover_color="#374151", border_width=1, border_color=ACCENT_BLUE) self.btn_scan_d.grid(row=1, column=1, padx=(5, 5), pady=5, sticky="ew") - self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures", command=self.update_virus_db_prompt, height=45, fg_color="#059669", hover_color="#047857") + self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures", command=self.update_virus_db_prompt, height=50, fg_color="#059669", hover_color="#047857") self.btn_db_update.grid(row=1, column=2, padx=(5, 0), pady=5, sticky="ew") - self.btn_rootkit = ctk.CTkButton(frame, text="🕵️ Chasse aux Rootkits (rkhunter)", command=self.run_rootkit_scan, height=45, fg_color="#7C3AED", hover_color="#6D28D9") - self.btn_rootkit.grid(row=2, column=0, columnspan=3, pady=(5, 10), sticky="ew") + self.btn_rootkit = ctk.CTkButton(frame, text="🕵️ Hunt Rootkits (rkhunter)", command=self.run_rootkit_scan, height=50, fg_color=ACCENT_VIOLET, hover_color="#7C3AED") + self.btn_rootkit.grid(row=2, column=0, columnspan=3, pady=(15, 20), sticky="ew") - self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=10) - self.scan_progress.grid(row=3, column=0, columnspan=3, pady=(10, 0), sticky="ew") + self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=4, progress_color=ACCENT_BLUE) + self.scan_progress.grid(row=3, column=0, columnspan=3, sticky="ew") self.scan_progress.set(0) - self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) - self.scan_console.grid(row=4, column=0, columnspan=3, pady=20, sticky="nsew") + self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151") + self.scan_console.grid(row=4, column=0, columnspan=3, pady=10, sticky="nsew") self.setup_console_tags(self.scan_console) - self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V10.1 prêt...\n", "info") + self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur d'analyse prêt.\n", "info") + # (Toutes tes méthodes internes restent identiques ici pour garantir le fonctionnement) def run_rootkit_scan(self): self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start()) def _exec_rootkit(self, pwd): - self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de rkhunter (cela peut prendre du temps)...\n", "info") + self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de rkhunter (cela peut prendre du temps)...\n", "violet") self.scan_progress.start() try: cmd = ['sudo', '-S', 'rkhunter', '-c', '--sk', '--report-warnings-only'] process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) - - process.stdin.write(pwd + '\n') - process.stdin.flush() - - for line in iter(process.stdout.readline, ''): - if "incorrect password" in line.lower() or "try again" in line.lower(): - self.scan_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé par le système.\n", "danger") - process.terminate() - break - else: - self.scan_console.insert("end", line) - self.scan_console.see("end") - - process.wait() - if process.returncode in [0, 1]: - self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success") - except FileNotFoundError: - self.scan_console.insert("end", f"{self.get_time_prefix()}[-] rkhunter n'est pas installé.\n", "warning") - except Exception as e: - self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger") - finally: - self.scan_progress.stop() - self.scan_console.see("end") - - def update_virus_db_prompt(self): - self.get_sudo_password(lambda pwd: threading.Thread(target=self._run_freshclam, args=(pwd,), daemon=True).start(), msg="La mise à jour des signatures ClamAV nécessite\nles droits sudo. Entrez votre mot de passe :") - - def _run_freshclam(self, pwd): - self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de la mise à jour (freshclam)...\n", "info") - self.scan_progress.start() - self.btn_db_update.configure(state="disabled") - try: - cmd = ['sudo', '-S', 'freshclam'] - process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) process.stdin.write(pwd + '\n') process.stdin.flush() for line in iter(process.stdout.readline, ''): @@ -309,56 +307,39 @@ class Antivirus7LnA(ctk.CTk): self.scan_console.insert("end", line) self.scan_console.see("end") process.wait() - if process.returncode == 0: - self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures virales mises à jour avec succès.\n", "success") + if process.returncode in [0, 1]: self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success") except Exception as e: - self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur freshclam : {e}\n", "danger") + self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger") finally: self.scan_progress.stop() - self.btn_db_update.configure(state="normal") self.scan_console.see("end") + def update_virus_db_prompt(self): + self.get_sudo_password(lambda pwd: threading.Thread(target=self._run_freshclam, args=(pwd,), daemon=True).start(), msg="La mise à jour nécessite les droits sudo :") + + def _run_freshclam(self, pwd): + self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de la mise à jour (freshclam)...\n", "info") + self.scan_progress.start() + try: + cmd = ['sudo', '-S', 'freshclam'] + process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) + process.stdin.write(pwd + '\n') + process.stdin.flush() + for line in iter(process.stdout.readline, ''): + self.scan_console.insert("end", line) + self.scan_console.see("end") + process.wait() + if process.returncode == 0: self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures mises à jour.\n", "success") + except Exception as e: self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur freshclam : {e}\n", "danger") + finally: self.scan_progress.stop() + def start_manual_scan(self, is_dir): path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename() if path: threading.Thread(target=self.run_clamav_scan, args=(path, is_dir, self.scan_console), daemon=True).start() - # --- 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=34, weight="bold")).pack(side="left") - self.btn_toggle_shield = ctk.CTkButton(header, text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857", command=self.toggle_shield, height=45) - self.btn_toggle_shield.pack(side="right") - self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) - self.rt_console.grid(row=2, column=0, sticky="nsew") - self.setup_console_tags(self.rt_console) - - 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="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C") - self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success") - else: - self.shield_observer.stop() - self.shield_active = False - self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669") - self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning") - - def trigger_realtime_scan(self, filepath): - self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info") - threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start() - - # --- MOTEUR CLAMAV --- def run_clamav_scan(self, path, is_dir, console): if not self.clamav_installed: return - console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n") + console.insert("end", f"\n{self.get_time_prefix()}[*] Analyse de cible : {path}\n") if console == self.scan_console: self.scan_progress.start() try: cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'] @@ -376,37 +357,69 @@ class Antivirus7LnA(ctk.CTk): elif clean_line: console.insert("end", f" -> {clean_line}\n") console.see("end") process.wait() - if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success") + if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Cible propre.\n", "success") else: send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s).", is_critical=True) except Exception as e: console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger") finally: if console == self.scan_console: self.scan_progress.stop() console.see("end") - # --- VUE : AUDIT & PARE-FEU (AVEC WIFI GUARD V10.1) --- + # (J'ai appliqué le design "Carte" à toutes les autres vues pour un rendu homogène) + 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, 30)) + ctk.CTkLabel(header, text="Bouclier Temps Réel", font=ctk.CTkFont(size=38, weight="bold")).pack(side="left") + self.btn_toggle_shield = ctk.CTkButton(header, text="Activer la Surveillance", fg_color="#059669", hover_color="#047857", command=self.toggle_shield, height=45) + self.btn_toggle_shield.pack(side="right") + + self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151") + self.rt_console.grid(row=2, column=0, sticky="nsew") + self.setup_console_tags(self.rt_console) + + 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", fg_color="#DC2626", hover_color="#B91C1C") + self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success") + else: + self.shield_observer.stop() + self.shield_active = False + self.btn_toggle_shield.configure(text="Activer la Surveillance", fg_color="#059669") + self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning") + + def trigger_realtime_scan(self, filepath): + self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info") + threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start() + def init_audit_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") frame.grid_rowconfigure(2, weight=1) frame.grid_columnconfigure(0, weight=1) self.views["audit"] = frame - ctk.CTkLabel(frame, text="Audit Réseau & Pare-feu", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20)) + ctk.CTkLabel(frame, text="Réseau & Pare-feu", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 30)) btn_frame = ctk.CTkFrame(frame, fg_color="transparent") - btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10)) + btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 20)) - ctk.CTkButton(btn_frame, text="⚙️ Lancer l'Audit Processus", command=self.run_audit_thread, fg_color="#D97706", hover_color="#B45309", height=40).pack(side="left", padx=(0, 10)) - ctk.CTkButton(btn_frame, text="🛡️ Vérifier UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left", padx=(0, 10)) - ctk.CTkButton(btn_frame, text="📡 WiFi Guard (Scanner Intrus)", command=self.run_wifi_guard_prompt, fg_color="#059669", hover_color="#047857", height=40).pack(side="left") + ctk.CTkButton(btn_frame, text="⚙️ Audit Processus", command=lambda: threading.Thread(target=self.perform_audit, daemon=True).start(), height=45, fg_color=BG_CARD, border_width=1).pack(side="left", padx=(0, 10)) + ctk.CTkButton(btn_frame, text="🛡️ Statut UFW", command=self.check_firewall, height=45, fg_color=BG_CARD, border_width=1).pack(side="left", padx=(0, 10)) + ctk.CTkButton(btn_frame, text="📡 WiFi Guard", command=self.run_wifi_guard_prompt, height=45, fg_color=ACCENT_BLUE, hover_color="#2563EB").pack(side="left") - self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) + self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151") self.audit_console.grid(row=2, column=0, sticky="nsew") - - def run_audit_thread(self): - self.audit_console.delete("0.0", "end") - threading.Thread(target=self.perform_audit, daemon=True).start() + self.setup_console_tags(self.audit_console) def perform_audit(self): + self.audit_console.delete("0.0", "end") try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True)) except: pass try: @@ -418,263 +431,232 @@ class Antivirus7LnA(ctk.CTk): self.audit_console.delete("0.0", "end") try: res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip() - if res == "active": self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n") - else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n") + if res == "active": self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n", "success") + else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n", "warning") except: pass - # ----- LOGIQUE WIFI GUARD ----- def run_wifi_guard_prompt(self): - self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_wifi_guard, args=(pwd,), daemon=True).start(), title="WiFi Guard", msg="Le scan réseau profond nécessite les droits sudo.\nEntrez votre mot de passe session :") + self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_wifi_guard, args=(pwd,), daemon=True).start(), title="WiFi Guard", msg="Scan réseau (sudo) :") def _exec_wifi_guard(self, pwd): self.audit_console.delete("0.0", "end") - self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Démarrage du scan réseau (arp-scan)... \n", "info") - + self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Démarrage arp-scan... \n", "info") if not shutil.which("arp-scan"): - self.audit_console.insert("end", f"{self.get_time_prefix()}[!] Erreur : arp-scan introuvable.\n", "danger") + self.audit_console.insert("end", f"{self.get_time_prefix()}[!] arp-scan introuvable.\n", "danger") return - try: cmd = ['sudo', '-S', 'arp-scan', '--localnet', '--ignoredups'] process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) - process.stdin.write(pwd + '\n') process.stdin.flush() - count = 0 for line in iter(process.stdout.readline, ''): - if any(x in line.lower() for x in ["permission denied", "starting arp-scan", "packets received", "ending arp-scan", "[sudo]"]): - continue - + if any(x in line.lower() for x in ["permission denied", "starting arp-scan", "packets received", "ending arp-scan", "[sudo]"]): continue if "incorrect password" in line.lower() or "try again" in line.lower(): self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé.\n", "danger") break - if line.strip() and (line[0].isdigit()): - self.audit_console.insert("end", f" 📱 Appareil trouvé : {line.strip()}\n", "success") + self.audit_console.insert("end", f" 📱 Appareil : {line.strip()}\n", "success") count += 1 - self.audit_console.see("end") - process.wait() - self.audit_console.insert("end", f"\n{self.get_time_prefix()}[+] Scan terminé. {count} appareils détectés sur votre WiFi.\n", "info") - - if count > 15: - send_desktop_notification("WiFi Guard", f"Attention : Beaucoup d'appareils ({count}) sont connectés à votre réseau.", is_critical=True) - - except Exception as e: - self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger") + self.audit_console.insert("end", f"\n{self.get_time_prefix()}[+] Scan terminé. {count} appareils détectés.\n", "info") + except Exception as e: self.audit_console.insert("end", f"❌ Erreur : {e}\n", "danger") - # --- VUE : OUTILS AVANCÉS (V10) --- def init_tools_view(self): frame = ctk.CTkScrollableFrame(self, fg_color="transparent") self.views["tools"] = frame - ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) + ctk.CTkLabel(frame, text="Outils Avancés", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30)) - # Vérificateur de Fuites de Données (OSINT) - breach_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) + # OSINT Card + breach_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15) breach_card.pack(fill="x", pady=10, ipady=15) - ctk.CTkLabel(breach_card, text="🌐 Vérificateur de Fuites (OSINT)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#A855F7").pack(anchor="w", padx=20, pady=(10,0)) - ctk.CTkLabel(breach_card, text="Vérifiez si votre adresse e-mail a été compromise dans une fuite de données (Dark Web).", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10)) + ctk.CTkLabel(breach_card, text="🌐 OSINT : Vérificateur de Fuites", font=ctk.CTkFont(size=20, weight="bold"), text_color=ACCENT_VIOLET).pack(anchor="w", padx=20, pady=(10,5)) + ctk.CTkLabel(breach_card, text="Le Dark Web contient-il votre adresse e-mail ?", text_color=TEXT_MUTED).pack(anchor="w", padx=20, pady=(0, 15)) input_frame = ctk.CTkFrame(breach_card, fg_color="transparent") input_frame.pack(fill="x", padx=20, pady=5) - self.email_entry = ctk.CTkEntry(input_frame, placeholder_text="Entrez votre e-mail...", width=300) - self.email_entry.pack(side="left", padx=(0, 10)) - ctk.CTkButton(input_frame, text="Vérifier l'e-mail", fg_color="#9333EA", hover_color="#7E22CE", command=self.run_breach_check).pack(side="left") + self.email_entry = ctk.CTkEntry(input_frame, placeholder_text="exemple@domaine.com", width=350, height=45) + self.email_entry.pack(side="left", padx=(0, 15)) + ctk.CTkButton(input_frame, text="Rechercher", fg_color=ACCENT_VIOLET, hover_color="#7C3AED", height=45, command=self.run_breach_check).pack(side="left") self.breach_result_var = ctk.StringVar(value="") - self.breach_result_label = ctk.CTkLabel(breach_card, textvariable=self.breach_result_var, text_color="#D1D5DB", justify="left") - self.breach_result_label.pack(anchor="w", padx=20, pady=10) + self.breach_result_label = ctk.CTkLabel(breach_card, textvariable=self.breach_result_var, font=ctk.CTkFont(size=14), justify="left") + self.breach_result_label.pack(anchor="w", padx=20, pady=15) # Nettoyeur - clean_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) + clean_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15) clean_card.pack(fill="x", pady=10, ipady=15) - ctk.CTkLabel(clean_card, text="🧹 Nettoyeur Système", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,0)) - ctk.CTkButton(clean_card, text="Vider le Cache et Corbeille", fg_color="#059669", hover_color="#047857", command=self.run_cleaner).pack(anchor="w", padx=20, pady=10) + ctk.CTkLabel(clean_card, text="🧹 Optimisation", font=ctk.CTkFont(size=20, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,15)) + ctk.CTkButton(clean_card, text="Purger le Cache Système", fg_color="#059669", hover_color="#047857", height=45, command=self.run_cleaner).pack(anchor="w", padx=20) # Destructeur - shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) + shred_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15) shred_card.pack(fill="x", pady=10, ipady=15) - ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0)) - ctk.CTkButton(shred_card, text="Détruire un fichier", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20, pady=10) + ctk.CTkLabel(shred_card, text="🔥 Suppression Militaire", font=ctk.CTkFont(size=20, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,15)) + ctk.CTkButton(shred_card, text="Détruire un fichier définitivement", fg_color="#DC2626", hover_color="#991B1B", height=45, command=self.run_shredder).pack(anchor="w", padx=20) - # --- LOGIQUE OSINT --- def run_breach_check(self): email = self.email_entry.get().strip() if not email or "@" not in email: - self.breach_result_var.set("❌ Veuillez entrer une adresse e-mail valide.") + self.breach_result_var.set("❌ Adresse e-mail invalide.") self.breach_result_label.configure(text_color="#EF4444") return - - self.breach_result_var.set("⏳ Interrogation des bases de données...") - self.breach_result_label.configure(text_color="#3B82F6") + self.breach_result_var.set("⏳ Interrogation des serveurs mondiaux...") + self.breach_result_label.configure(text_color=ACCENT_BLUE) threading.Thread(target=self._exec_breach_check, args=(email,), daemon=True).start() def _exec_breach_check(self, email): try: - url = f"https://api.xposedornot.com/v1/check-email/{email}" - req = urllib.request.Request(url, headers={'User-Agent': '7LnA-Security-Suite'}) + req = urllib.request.Request(f"https://api.xposedornot.com/v1/check-email/{email}", headers={'User-Agent': '7lna-Antivirus'}) try: with urllib.request.urlopen(req) as response: data = json.loads(response.read().decode()) if "breaches" in data and data["breaches"]: breaches = data["breaches"][0] - breach_count = len(breaches) - breach_names = ", ".join(breaches[:4]) - suffix = "..." if breach_count > 4 else "" - self.breach_result_var.set(f"🚨 DANGER : E-mail trouvé dans {breach_count} fuite(s) !\nSources connues : {breach_names}{suffix}\nChangez vos mots de passe immédiatement.") + b_str = ", ".join(breaches[:4]) + ("..." if len(breaches) > 4 else "") + self.breach_result_var.set(f"🚨 DANGER : {len(breaches)} fuites trouvées !\nSources : {b_str}") self.breach_result_label.configure(text_color="#EF4444") else: - self.breach_result_var.set("✅ Bonne nouvelle : Aucune fuite détectée pour cet e-mail.") + self.breach_result_var.set("✅ Identité sécurisée. Aucune fuite détectée.") self.breach_result_label.configure(text_color="#10B981") except urllib.error.HTTPError as e: if e.code == 404: - self.breach_result_var.set("✅ Bonne nouvelle : Aucune fuite détectée pour cet e-mail.") + self.breach_result_var.set("✅ Identité sécurisée. Aucune fuite détectée.") self.breach_result_label.configure(text_color="#10B981") else: - self.breach_result_var.set(f"❌ Erreur serveur lors de la vérification ({e.code}).") - self.breach_result_label.configure(text_color="#F59E0B") - except Exception as e: - self.breach_result_var.set("❌ Impossible de contacter le serveur d'OSINT. Vérifiez votre connexion.") - self.breach_result_label.configure(text_color="#F59E0B") + self.breach_result_var.set(f"❌ Erreur API ({e.code}).") + except: self.breach_result_var.set("❌ Impossible de joindre les serveurs.") def run_cleaner(self): - paths_to_clean = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")] - for path in paths_to_clean: - if os.path.exists(path): - for item in os.listdir(path): - item_path = os.path.join(path, item) - try: - if os.path.isfile(item_path): os.remove(item_path) - elif os.path.isdir(item_path): shutil.rmtree(item_path) + paths = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")] + for p in paths: + if os.path.exists(p): + for i in os.listdir(p): + item = os.path.join(p, i) + try: shutil.rmtree(item) if os.path.isdir(item) else os.remove(item) except: pass - messagebox.showinfo("Succès", "Nettoyage terminé.") + messagebox.showinfo("Optimisation", "Fichiers temporaires purgés.") def run_shredder(self): filepath = filedialog.askopenfilename() - if not filepath: return - if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"): + if filepath and messagebox.askyesno("Destruction", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"): try: - file_size = os.path.getsize(filepath) + size = os.path.getsize(filepath) for _ in range(3): with open(filepath, "ba+", buffering=0) as f: - f.seek(0) - f.write(os.urandom(file_size)) + f.seek(0); f.write(os.urandom(size)) os.remove(filepath) messagebox.showinfo("Succès", "Fichier détruit.") except: pass - # ----- PLANIFICATION ----- def init_schedule_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") self.views["schedule"] = frame - ctk.CTkLabel(frame, text="Planification de Scans", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) - ctk.CTkLabel(frame, text="Automatisez l'analyse via les tâches Cron.", text_color="#9CA3AF").pack(anchor="w", pady=(0, 20)) - - card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) - card.pack(fill="x", pady=10, ipady=15) + ctk.CTkLabel(frame, text="Automatisation Cron", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30)) - ctk.CTkLabel(card, text="Programmer un scan automatique (Dossier Téléchargements)", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(10, 5)) + card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15) + card.pack(fill="x", ipady=20) + ctk.CTkLabel(card, text="Scan Journalier (Dossier Téléchargements)", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(20, 15)) time_frame = ctk.CTkFrame(card, fg_color="transparent") - time_frame.pack(pady=(5, 15)) - - ctk.CTkLabel(time_frame, text="Heure d'exécution :").pack(side="left", padx=5) + time_frame.pack(pady=10) self.cron_h_var = ctk.StringVar(value="12") self.cron_m_var = ctk.StringVar(value="00") - ctk.CTkComboBox(time_frame, values=[f"{i:02d}" for i in range(24)], variable=self.cron_h_var, width=70).pack(side="left", padx=5) - ctk.CTkLabel(time_frame, text="h").pack(side="left") - ctk.CTkComboBox(time_frame, values=["00", "15", "30", "45"], variable=self.cron_m_var, width=70).pack(side="left", padx=5) + ctk.CTkComboBox(time_frame, values=[f"{i:02d}" for i in range(24)], variable=self.cron_h_var, width=80, height=40).pack(side="left", padx=5) + ctk.CTkLabel(time_frame, text=":").pack(side="left") + ctk.CTkComboBox(time_frame, values=["00", "15", "30", "45"], variable=self.cron_m_var, width=80, height=40).pack(side="left", padx=5) - ctk.CTkButton(card, text="✅ Activer le Scan Quotidien", fg_color="#2563EB", hover_color="#1D4ED8", command=self.setup_cronjob).pack(pady=5) - ctk.CTkButton(card, text="❌ Désactiver la planification", fg_color="#4B5563", hover_color="#374151", command=self.remove_cronjob).pack(pady=5) + ctk.CTkButton(card, text="Activer la planification", fg_color=ACCENT_BLUE, hover_color="#2563EB", height=45, command=self.setup_cronjob).pack(pady=(20,10)) + ctk.CTkButton(card, text="Désactiver", fg_color="transparent", border_width=1, border_color="#EF4444", text_color="#EF4444", hover_color="#7F1D1D", height=45, command=self.remove_cronjob).pack() def setup_cronjob(self): - h = self.cron_h_var.get() - m = self.cron_m_var.get() - cron_command = f"{int(m)} {int(h)} * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1" + cmd = f"{int(self.cron_m_var.get())} {int(self.cron_h_var.get())} * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1" try: - os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cron_command}") | crontab -') - messagebox.showinfo("Succès", f"Scan configuré tous les jours à {h}:{m}.") - except: messagebox.showerror("Erreur", "Impossible de configurer Cron.") - - def remove_cronjob(self): - try: - os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -') - messagebox.showinfo("Succès", "Planification désactivée.") + os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cmd}") | crontab -') + messagebox.showinfo("Cron", "Scan quotidien configuré.") + except: pass + + def remove_cronjob(self): + try: os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -') except: pass - # --- VUE : HISTORIQUE --- def init_history_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") frame.grid_rowconfigure(1, weight=1) frame.grid_columnconfigure(0, weight=1) self.views["history"] = frame - ctk.CTkLabel(frame, text="📜 Registre des Menaces", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20)) - self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) + ctk.CTkLabel(frame, text="Registre Sécurité", font=ctk.CTkFont(size=38, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 30)) + self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151") self.history_console.grid(row=1, column=0, sticky="nsew") def refresh_history_list(self): self.history_console.delete("0.0", "end") try: with open(HISTORY_FILE, "r") as f: data = json.load(f) - if not data: self.history_console.insert("end", "\n ✅ Historique vierge.\n") + if not data: self.history_console.insert("end", "\n ✅ Aucun incident enregistré.\n") else: - for entry in reversed(data): - self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['file']}\n\n") + for e in reversed(data): self.history_console.insert("end", f"[{e['date']}] ☠️ {e['threat']}\n -> Fichier : {e['file']}\n\n") except: pass - # --- VUE : QUARANTAINE --- def init_quarantine_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") frame.grid_rowconfigure(1, weight=1) frame.grid_columnconfigure(0, weight=1) self.views["quarantine"] = frame + header = ctk.CTkFrame(frame, fg_color="transparent") - header.grid(row=0, column=0, sticky="ew", pady=(0, 20)) - ctk.CTkLabel(header, text="📦 Quarantaine", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left") - ctk.CTkButton(header, text="🗑️ Vider", fg_color="#DC2626", hover_color="#991B1B", command=self.empty_quarantine, height=40).pack(side="right") - self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color="#111827", corner_radius=10) + header.grid(row=0, column=0, sticky="ew", pady=(0, 30)) + ctk.CTkLabel(header, text="Zone de Quarantaine", font=ctk.CTkFont(size=38, weight="bold")).pack(side="left") + ctk.CTkButton(header, text="Purger Tout", fg_color="#DC2626", hover_color="#991B1B", height=45, command=self.empty_quarantine).pack(side="right") + + self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color=BG_CARD, corner_radius=15, border_width=1, border_color="#374151") self.quarantine_listbox.grid(row=1, column=0, sticky="nsew") def refresh_quarantine_list(self): self.quarantine_listbox.delete("0.0", "end") files = glob.glob(os.path.join(QUARANTINE_DIR, "*")) - if not files: self.quarantine_listbox.insert("end", "\n ✅ Aucun fichier malveillant isolé.\n") + if not files: self.quarantine_listbox.insert("end", "\n ✅ Zone propre.\n") else: - for f in files: self.quarantine_listbox.insert("end", f" -> {os.path.basename(f)}\n") + for f in files: self.quarantine_listbox.insert("end", f" 🔒 {os.path.basename(f)}\n") def empty_quarantine(self): files = glob.glob(os.path.join(QUARANTINE_DIR, "*")) - if files and messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"): + if files and messagebox.askyesno("Purger", "Détruire définitivement les virus ?"): for f in files: try: os.remove(f) except: pass self.refresh_quarantine_list() - # --- VUE : MISE À JOUR --- def init_update_view(self): frame = ctk.CTkFrame(self, fg_color="transparent") self.views["update"] = frame - ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) - self.btn_update_soft = ctk.CTkButton(frame, text="⬇️ Mettre à jour", command=self.run_software_update, fg_color="#8B5CF6", hover_color="#7C3AED", height=45) - self.btn_update_soft.pack(anchor="w") - self.update_console = ctk.CTkTextbox(frame, height=200, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) - self.update_console.pack(fill="x", pady=20) + ctk.CTkLabel(frame, text="Mise à jour Logicielle", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30)) + + card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15) + card.pack(fill="x", ipady=20) + ctk.CTkLabel(card, text="Synchronisation Serveur (git.7ka1.com)", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(20, 15)) + + self.btn_update_soft = ctk.CTkButton(card, text="Télécharger la dernière version", fg_color=ACCENT_BLUE, hover_color="#2563EB", height=45, command=self.run_software_update) + self.btn_update_soft.pack(pady=10) + + self.update_console = ctk.CTkTextbox(card, height=150, font=ctk.CTkFont(family="Consolas", size=13), fg_color=BG_MAIN, corner_radius=10) + self.update_console.pack(fill="x", padx=20, pady=15) def run_software_update(self): self.btn_update_soft.configure(state="disabled") - self.update_console.insert("end", "[*] Négociation avec serveur...\n") - try: - urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__)) - self.update_console.insert("end", "[+] Mise à jour réussie !\n") - messagebox.showinfo("Succès", "Redémarrage requis.") - self.destroy() - except: - self.update_console.insert("end", "[-] Échec.\n") - self.btn_update_soft.configure(state="normal") + self.update_console.insert("end", "[*] Connexion au serveur Git...\n") + def fetch(): + try: + urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__)) + self.update_console.insert("end", "[+] Code source téléchargé et appliqué !\n") + messagebox.showinfo("Mise à jour", "Redémarrage de l'application requis.") + os._exit(0) # Force la fermeture propre pour relancer via le menu + except: + self.update_console.insert("end", "[-] Échec de la connexion.\n") + self.btn_update_soft.configure(state="normal") + threading.Thread(target=fetch).start() if __name__ == "__main__": app = Antivirus7LnA()