Actualiser 7lna.py

This commit is contained in:
2026-05-22 12:55:40 +00:00
parent 6b34a54f03
commit 9b7e95ee65

596
7lna.py
View File

@@ -1,19 +1,6 @@
import customtkinter as ctk import customtkinter as ctk
from tkinter import filedialog, messagebox from tkinter import filedialog, messagebox
import os import os, threading, subprocess, urllib.request, urllib.error, time, datetime, glob, json, platform, shutil
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
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
@@ -25,8 +12,16 @@ QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements") WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json") 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_appearance_mode("dark")
ctk.set_default_color_theme("blue")
# ========================================== # ==========================================
# UTILITAIRES SYSTÈME # UTILITAIRES SYSTÈME
@@ -34,7 +29,7 @@ ctk.set_default_color_theme("blue")
def send_desktop_notification(title, message, is_critical=False): def send_desktop_notification(title, message, is_critical=False):
try: try:
urgency = 'critical' if is_critical else 'normal' 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: except Exception:
pass pass
@@ -52,9 +47,10 @@ class RealTimeShieldHandler(FileSystemEventHandler):
class Antivirus7LnA(ctk.CTk): class Antivirus7LnA(ctk.CTk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("7LnA Security Suite - V10.1 Quantum Edition") self.title("7lna Antivirus")
self.geometry("1250x850") self.geometry("1250x850")
self.minsize(1000, 700) self.minsize(1050, 750)
self.configure(fg_color=BG_MAIN)
os.makedirs(QUARANTINE_DIR, exist_ok=True) os.makedirs(QUARANTINE_DIR, exist_ok=True)
if not os.path.exists(HISTORY_FILE): if not os.path.exists(HISTORY_FILE):
@@ -62,7 +58,7 @@ class Antivirus7LnA(ctk.CTk):
self.shield_observer = None self.shield_observer = None
self.shield_active = False self.shield_active = False
self.zero_usb_mode = False # <--- AJOUT : État du mode Zero USB self.zero_usb_mode = False
self.check_dependencies() self.check_dependencies()
self.setup_ui() self.setup_ui()
@@ -76,30 +72,44 @@ class Antivirus7LnA(ctk.CTk):
except: except:
self.clamav_installed = False self.clamav_installed = False
# ==========================================
# INTERFACE GRAPHIQUE (DESIGN 2026)
# ==========================================
def setup_ui(self): def setup_ui(self):
self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1) self.grid_columnconfigure(1, weight=1)
# --- BARRE LATÉRALE --- # --- 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(row=0, column=0, sticky="nsew")
self.sidebar.grid_rowconfigure(10, weight=1) 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") # Navigation
self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner") self.nav_buttons = {}
self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield") self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit") self.create_nav_button("🔍 Scanner Système", 2, "scanner")
self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools") self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine") self.create_nav_button("🌐 Audit & Réseau", 4, "audit")
self.btn_schedule = self.create_nav_button("📅 Planification", 7, "schedule") self.create_nav_button("🧰 Boîte à Outils", 5, "tools")
self.btn_history = self.create_nav_button("📜 Rapports", 8, "history") self.create_nav_button("📦 Quarantaine", 6, "quarantine")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 9, "update") 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") self.version_label.grid(row=10, column=0, pady=20, sticky="s")
# --- VUES ---
self.views = {} self.views = {}
self.init_dashboard_view() self.init_dashboard_view()
self.init_scanner_view() self.init_scanner_view()
@@ -115,15 +125,24 @@ class Antivirus7LnA(ctk.CTk):
def create_nav_button(self, text, row, view_name): def create_nav_button(self, text, row, view_name):
btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent", 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)) command=lambda: self.select_view(view_name))
btn.grid(row=row, column=0, padx=20, pady=5, sticky="ew") btn.grid(row=row, column=0, padx=15, pady=4, sticky="ew")
return btn self.nav_buttons[view_name] = btn
def select_view(self, view_name): 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() for view in self.views.values(): view.grid_forget()
if view_name in self.views: 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 == "quarantine": self.refresh_quarantine_list()
if view_name == "history": self.refresh_history_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("danger", foreground="#EF4444")
console.tag_config("success", foreground="#10B981") console.tag_config("success", foreground="#10B981")
console.tag_config("warning", foreground="#F59E0B") 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): def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ") 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) with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4)
except: pass except: pass
# --- AUTO-DETECT USB --- # ==========================================
# LOGIQUE USB & MOTEUR
# ==========================================
def monitor_usb_drives(self): def monitor_usb_drives(self):
media_dir = f"/media/{os.getlogin()}" media_dir = f"/media/{os.getlogin()}"
if not os.path.exists(media_dir): return if not os.path.exists(media_dir): return
@@ -158,146 +180,122 @@ class Antivirus7LnA(ctk.CTk):
known_mounts = current_mounts known_mounts = current_mounts
except: pass except: pass
# <--- MODIFICATION : Logique Zero USB intégrée
def prompt_usb_scan(self, path, name): def prompt_usb_scan(self, path, name):
if self.zero_usb_mode: 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.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() threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
else: else:
send_desktop_notification("USB Détectée", f"Disque {name} branché.") 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") self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start() threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
# --- VUE : TABLEAU DE BORD --- def get_sudo_password(self, callback_func, title="Sécurité Administrateur", msg="Privilèges requis.\nEntrez votre mot de passe :"):
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 :"):
dialog = ctk.CTkToplevel(self) dialog = ctk.CTkToplevel(self)
dialog.title(title) 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="⚠️ É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").pack(pady=5) ctk.CTkLabel(dialog, text=msg, justify="center", text_color=TEXT_MUTED).pack(pady=5)
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250) pwd_entry = ctk.CTkEntry(dialog, show="*", width=250, height=40, border_color=ACCENT_BLUE)
pwd_entry.pack(pady=10) pwd_entry.pack(pady=15)
def on_submit(event=None): def on_submit(event=None):
pwd = pwd_entry.get() pwd = pwd_entry.get()
dialog.destroy() dialog.destroy()
if pwd: if pwd: callback_func(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("<Return>", on_submit) dialog.bind("<Return>", on_submit)
dialog.wait_visibility() dialog.wait_visibility()
dialog.attributes("-topmost", True) dialog.attributes("-topmost", True)
dialog.grab_set() dialog.grab_set()
pwd_entry.focus_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): def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") 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) frame.grid_columnconfigure((0, 1, 2), weight=1)
self.views["scanner"] = frame 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_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_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_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 = 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=(5, 10), sticky="ew") 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 = ctk.CTkProgressBar(frame, mode="indeterminate", height=4, progress_color=ACCENT_BLUE)
self.scan_progress.grid(row=3, column=0, columnspan=3, pady=(10, 0), sticky="ew") self.scan_progress.grid(row=3, column=0, columnspan=3, sticky="ew")
self.scan_progress.set(0) 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 = 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=20, sticky="nsew") self.scan_console.grid(row=4, column=0, columnspan=3, pady=10, sticky="nsew")
self.setup_console_tags(self.scan_console) 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): def run_rootkit_scan(self):
self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start()) self.get_sudo_password(lambda pwd: threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start())
def _exec_rootkit(self, pwd): 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() self.scan_progress.start()
try: try:
cmd = ['sudo', '-S', 'rkhunter', '-c', '--sk', '--report-warnings-only'] 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 = 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.write(pwd + '\n')
process.stdin.flush() process.stdin.flush()
for line in iter(process.stdout.readline, ''): for line in iter(process.stdout.readline, ''):
@@ -309,56 +307,39 @@ class Antivirus7LnA(ctk.CTk):
self.scan_console.insert("end", line) self.scan_console.insert("end", line)
self.scan_console.see("end") self.scan_console.see("end")
process.wait() process.wait()
if process.returncode == 0: if process.returncode in [0, 1]: self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success")
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures virales mises à jour avec succès.\n", "success")
except Exception as e: 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: finally:
self.scan_progress.stop() self.scan_progress.stop()
self.btn_db_update.configure(state="normal")
self.scan_console.see("end") 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): def start_manual_scan(self, is_dir):
path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename() 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() 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): def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed: return 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() if console == self.scan_console: self.scan_progress.start()
try: try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'] 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") elif clean_line: console.insert("end", f" -> {clean_line}\n")
console.see("end") console.see("end")
process.wait() 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) 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") except Exception as e: console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally: finally:
if console == self.scan_console: self.scan_progress.stop() if console == self.scan_console: self.scan_progress.stop()
console.see("end") 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): def init_audit_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1) frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1)
self.views["audit"] = frame 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 = 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="⚙️ 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="🛡️ 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="🛡️ 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 (Scanner Intrus)", command=self.run_wifi_guard_prompt, fg_color="#059669", hover_color="#047857", height=40).pack(side="left") 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") self.audit_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.audit_console)
def run_audit_thread(self):
self.audit_console.delete("0.0", "end")
threading.Thread(target=self.perform_audit, daemon=True).start()
def perform_audit(self): def perform_audit(self):
self.audit_console.delete("0.0", "end")
try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True)) try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except: pass except: pass
try: try:
@@ -418,263 +431,232 @@ class Antivirus7LnA(ctk.CTk):
self.audit_console.delete("0.0", "end") self.audit_console.delete("0.0", "end")
try: try:
res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip() 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") 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") else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n", "warning")
except: pass except: pass
# ----- LOGIQUE WIFI GUARD -----
def run_wifi_guard_prompt(self): 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): def _exec_wifi_guard(self, pwd):
self.audit_console.delete("0.0", "end") 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"): 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 return
try: try:
cmd = ['sudo', '-S', 'arp-scan', '--localnet', '--ignoredups'] 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 = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
process.stdin.write(pwd + '\n') process.stdin.write(pwd + '\n')
process.stdin.flush() process.stdin.flush()
count = 0 count = 0
for line in iter(process.stdout.readline, ''): 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]"]): if any(x in line.lower() for x in ["permission denied", "starting arp-scan", "packets received", "ending arp-scan", "[sudo]"]): continue
continue
if "incorrect password" in line.lower() or "try again" in line.lower(): 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") self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Mot de passe refusé.\n", "danger")
break break
if line.strip() and (line[0].isdigit()): 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 count += 1
self.audit_console.see("end") self.audit_console.see("end")
process.wait() 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") 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")
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")
# --- VUE : OUTILS AVANCÉS (V10) ---
def init_tools_view(self): def init_tools_view(self):
frame = ctk.CTkScrollableFrame(self, fg_color="transparent") frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.views["tools"] = frame 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) # OSINT Card
breach_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) breach_card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
breach_card.pack(fill="x", pady=10, ipady=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="🌐 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="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="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 = ctk.CTkFrame(breach_card, fg_color="transparent")
input_frame.pack(fill="x", padx=20, pady=5) 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 = ctk.CTkEntry(input_frame, placeholder_text="exemple@domaine.com", width=350, height=45)
self.email_entry.pack(side="left", padx=(0, 10)) self.email_entry.pack(side="left", padx=(0, 15))
ctk.CTkButton(input_frame, text="Vérifier l'e-mail", fg_color="#9333EA", hover_color="#7E22CE", command=self.run_breach_check).pack(side="left") 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_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 = 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=10) self.breach_result_label.pack(anchor="w", padx=20, pady=15)
# Nettoyeur # 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) 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.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="Vider le Cache et Corbeille", fg_color="#059669", hover_color="#047857", command=self.run_cleaner).pack(anchor="w", padx=20, pady=10) 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 # 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) 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.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", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20, pady=10) 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): def run_breach_check(self):
email = self.email_entry.get().strip() email = self.email_entry.get().strip()
if not email or "@" not in email: 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") self.breach_result_label.configure(text_color="#EF4444")
return return
self.breach_result_var.set("⏳ Interrogation des serveurs mondiaux...")
self.breach_result_var.set("⏳ Interrogation des bases de données...") self.breach_result_label.configure(text_color=ACCENT_BLUE)
self.breach_result_label.configure(text_color="#3B82F6")
threading.Thread(target=self._exec_breach_check, args=(email,), daemon=True).start() threading.Thread(target=self._exec_breach_check, args=(email,), daemon=True).start()
def _exec_breach_check(self, email): def _exec_breach_check(self, email):
try: try:
url = f"https://api.xposedornot.com/v1/check-email/{email}" req = urllib.request.Request(f"https://api.xposedornot.com/v1/check-email/{email}", headers={'User-Agent': '7lna-Antivirus'})
req = urllib.request.Request(url, headers={'User-Agent': '7LnA-Security-Suite'})
try: try:
with urllib.request.urlopen(req) as response: with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode()) data = json.loads(response.read().decode())
if "breaches" in data and data["breaches"]: if "breaches" in data and data["breaches"]:
breaches = data["breaches"][0] breaches = data["breaches"][0]
breach_count = len(breaches) b_str = ", ".join(breaches[:4]) + ("..." if len(breaches) > 4 else "")
breach_names = ", ".join(breaches[:4]) self.breach_result_var.set(f"🚨 DANGER : {len(breaches)} fuites trouvées !\nSources : {b_str}")
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.")
self.breach_result_label.configure(text_color="#EF4444") self.breach_result_label.configure(text_color="#EF4444")
else: 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") self.breach_result_label.configure(text_color="#10B981")
except urllib.error.HTTPError as e: except urllib.error.HTTPError as e:
if e.code == 404: 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") self.breach_result_label.configure(text_color="#10B981")
else: else:
self.breach_result_var.set(f"❌ Erreur serveur lors de la vérification ({e.code}).") self.breach_result_var.set(f"❌ Erreur API ({e.code}).")
self.breach_result_label.configure(text_color="#F59E0B") except: self.breach_result_var.set("❌ Impossible de joindre les serveurs.")
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")
def run_cleaner(self): def run_cleaner(self):
paths_to_clean = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")] paths = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")]
for path in paths_to_clean: for p in paths:
if os.path.exists(path): if os.path.exists(p):
for item in os.listdir(path): for i in os.listdir(p):
item_path = os.path.join(path, item) item = os.path.join(p, i)
try: try: shutil.rmtree(item) if os.path.isdir(item) else os.remove(item)
if os.path.isfile(item_path): os.remove(item_path)
elif os.path.isdir(item_path): shutil.rmtree(item_path)
except: pass except: pass
messagebox.showinfo("Succès", "Nettoyage terminé.") messagebox.showinfo("Optimisation", "Fichiers temporaires purgés.")
def run_shredder(self): def run_shredder(self):
filepath = filedialog.askopenfilename() filepath = filedialog.askopenfilename()
if not filepath: return if filepath and messagebox.askyesno("Destruction", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
try: try:
file_size = os.path.getsize(filepath) size = os.path.getsize(filepath)
for _ in range(3): for _ in range(3):
with open(filepath, "ba+", buffering=0) as f: with open(filepath, "ba+", buffering=0) as f:
f.seek(0) f.seek(0); f.write(os.urandom(size))
f.write(os.urandom(file_size))
os.remove(filepath) os.remove(filepath)
messagebox.showinfo("Succès", "Fichier détruit.") messagebox.showinfo("Succès", "Fichier détruit.")
except: pass except: pass
# ----- PLANIFICATION -----
def init_schedule_view(self): def init_schedule_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["schedule"] = frame 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="Automatisation Cron", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
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(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 = ctk.CTkFrame(card, fg_color="transparent")
time_frame.pack(pady=(5, 15)) time_frame.pack(pady=10)
ctk.CTkLabel(time_frame, text="Heure d'exécution :").pack(side="left", padx=5)
self.cron_h_var = ctk.StringVar(value="12") self.cron_h_var = ctk.StringVar(value="12")
self.cron_m_var = ctk.StringVar(value="00") 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.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="h").pack(side="left") ctk.CTkLabel(time_frame, text=":").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=["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="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 la planification", fg_color="#4B5563", hover_color="#374151", command=self.remove_cronjob).pack(pady=5) 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): def setup_cronjob(self):
h = self.cron_h_var.get() 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"
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"
try: try:
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cron_command}") | crontab -') os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cmd}") | crontab -')
messagebox.showinfo("Succès", f"Scan configuré tous les jours à {h}:{m}.") messagebox.showinfo("Cron", "Scan quotidien configuré.")
except: messagebox.showerror("Erreur", "Impossible de configurer Cron.") except: pass
def remove_cronjob(self): def remove_cronjob(self):
try: try: os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
messagebox.showinfo("Succès", "Planification désactivée.")
except: pass except: pass
# --- VUE : HISTORIQUE ---
def init_history_view(self): def init_history_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1) frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1)
self.views["history"] = frame 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)) 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=13), fg_color="#111827", corner_radius=10) 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") self.history_console.grid(row=1, column=0, sticky="nsew")
def refresh_history_list(self): def refresh_history_list(self):
self.history_console.delete("0.0", "end") self.history_console.delete("0.0", "end")
try: try:
with open(HISTORY_FILE, "r") as f: data = json.load(f) with open(HISTORY_FILE, "r") as f: data = json.load(f)
if not data: self.history_console.insert("end", "\nHistorique vierge.\n") if not data: self.history_console.insert("end", "\nAucun incident enregistré.\n")
else: else:
for entry in reversed(data): for e in reversed(data): self.history_console.insert("end", f"[{e['date']}] ☠️ {e['threat']}\n -> Fichier : {e['file']}\n\n")
self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['file']}\n\n")
except: pass except: pass
# --- VUE : QUARANTAINE ---
def init_quarantine_view(self): def init_quarantine_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1) frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1)
self.views["quarantine"] = frame self.views["quarantine"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent") header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20)) header.grid(row=0, column=0, sticky="ew", pady=(0, 30))
ctk.CTkLabel(header, text="📦 Quarantaine", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left") ctk.CTkLabel(header, text="Zone de Quarantaine", font=ctk.CTkFont(size=38, 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") 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="#111827", corner_radius=10)
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") self.quarantine_listbox.grid(row=1, column=0, sticky="nsew")
def refresh_quarantine_list(self): def refresh_quarantine_list(self):
self.quarantine_listbox.delete("0.0", "end") self.quarantine_listbox.delete("0.0", "end")
files = glob.glob(os.path.join(QUARANTINE_DIR, "*")) files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if not files: self.quarantine_listbox.insert("end", "\nAucun fichier malveillant isolé.\n") if not files: self.quarantine_listbox.insert("end", "\nZone propre.\n")
else: 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): def empty_quarantine(self):
files = glob.glob(os.path.join(QUARANTINE_DIR, "*")) 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: for f in files:
try: os.remove(f) try: os.remove(f)
except: pass except: pass
self.refresh_quarantine_list() self.refresh_quarantine_list()
# --- VUE : MISE À JOUR ---
def init_update_view(self): def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame self.views["update"] = frame
ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Mise à jour Logicielle", font=ctk.CTkFont(size=38, weight="bold")).pack(anchor="w", pady=(0, 30))
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") card = ctk.CTkFrame(frame, fg_color=BG_CARD, corner_radius=15)
self.update_console = ctk.CTkTextbox(frame, height=200, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) card.pack(fill="x", ipady=20)
self.update_console.pack(fill="x", pady=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): def run_software_update(self):
self.btn_update_soft.configure(state="disabled") self.btn_update_soft.configure(state="disabled")
self.update_console.insert("end", "[*] Négociation avec serveur...\n") self.update_console.insert("end", "[*] Connexion au serveur Git...\n")
try: def fetch():
urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__)) try:
self.update_console.insert("end", "[+] Mise à jour réussie !\n") urllib.request.urlretrieve(UPDATE_URL, os.path.abspath(__file__))
messagebox.showinfo("Succès", "Redémarrage requis.") self.update_console.insert("end", "[+] Code source téléchargé et appliqué !\n")
self.destroy() messagebox.showinfo("Mise à jour", "Redémarrage de l'application requis.")
except: os._exit(0) # Force la fermeture propre pour relancer via le menu
self.update_console.insert("end", "[-] Échec.\n") except:
self.btn_update_soft.configure(state="normal") 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__": if __name__ == "__main__":
app = Antivirus7LnA() app = Antivirus7LnA()