Files
2026-03-02 08:24:46 +00:00

284 lines
13 KiB
Python

import customtkinter as ctk
from tkinter import filedialog, messagebox
import os
import threading
import subprocess
import urllib.request
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# ==========================================
# CONFIGURATION GLOBALE
# ==========================================
# Lien RAW officiel de ton dépôt Gitea
UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py"
QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
# Design global
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
# ==========================================
# GESTIONNAIRE DU BOUCLIER TEMPS RÉEL
# ==========================================
class RealTimeShieldHandler(FileSystemEventHandler):
def __init__(self, app_instance):
self.app = app_instance
def on_created(self, event):
if not event.is_directory:
time.sleep(1.5) # Laisse le temps au téléchargement de se terminer
self.app.trigger_realtime_scan(event.src_path)
# ==========================================
# APPLICATION PRINCIPALE
# ==========================================
class Antivirus7LnA(ctk.CTk):
def __init__(self):
super().__init__()
self.title("7LnA Security Suite")
self.geometry("1050x650")
self.minsize(850, 550)
# Création du dossier de quarantaine
os.makedirs(QUARANTINE_DIR, exist_ok=True)
self.shield_observer = None
self.shield_active = False
self.check_dependencies()
self.setup_ui()
def check_dependencies(self):
try:
subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
self.clamav_installed = True
except (subprocess.CalledProcessError, FileNotFoundError):
self.clamav_installed = False
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=220, corner_radius=0)
self.sidebar.grid(row=0, column=0, sticky="nsew")
self.sidebar.grid_rowconfigure(6, weight=1)
ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA", font=ctk.CTkFont(size=28, weight="bold")).grid(row=0, column=0, padx=20, pady=30)
self.btn_dash = self.create_nav_button("📊 Tableau de Bord", 1, "dashboard")
self.btn_scan = self.create_nav_button("🔍 Scanner Manuel", 2, "scanner")
self.btn_shield = self.create_nav_button("⚡ Bouclier Actif", 3, "shield")
self.btn_audit = self.create_nav_button("⚙️ Audit Système", 4, "audit")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 5, "update")
self.version_label = ctk.CTkLabel(self.sidebar, text="Version 5.0 - Ultimate", text_color="gray")
self.version_label.grid(row=6, column=0, pady=20, sticky="s")
# --- CONTENEUR DES VUES ---
self.views = {}
self.init_dashboard_view()
self.init_scanner_view()
self.init_realtime_view()
self.init_audit_view()
self.init_update_view()
self.select_view("dashboard")
def create_nav_button(self, text, row, view_name):
btn = ctk.CTkButton(self.sidebar, text=text, anchor="w", fg_color="transparent",
text_color=("gray10", "#DCE4EE"), hover_color=("gray70", "gray30"),
command=lambda: self.select_view(view_name))
btn.grid(row=row, column=0, padx=20, pady=10, sticky="ew")
return btn
def select_view(self, view_name):
for view in self.views.values():
view.grid_forget()
if view_name in self.views:
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=20, pady=20)
# --- 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=24, weight="bold")).pack(anchor="w", pady=(0, 20))
status_card = ctk.CTkFrame(frame, fg_color="#1E8449" if self.clamav_installed else "#C0392B", corner_radius=15)
status_card.pack(fill="x", pady=10, ipady=20)
status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable"
ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=18, weight="bold"), text_color="white").pack(expand=True)
ctk.CTkLabel(frame, text=f"📂 Dossier de quarantaine : {QUARANTINE_DIR}").pack(anchor="w", pady=10)
# --- VUE : SCANNER MANUEL ---
def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure((0, 1), weight=1)
self.views["scanner"] = frame
ctk.CTkLabel(frame, text="Analyse Antivirus Manuelle", font=ctk.CTkFont(size=24, weight="bold")).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 20))
self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser un Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=40)
self.btn_scan_f.grid(row=1, column=0, padx=(0, 10), sticky="ew")
self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser un Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=40, fg_color="#2E86C1")
self.btn_scan_d.grid(row=1, column=1, padx=(10, 0), sticky="ew")
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13))
self.scan_console.grid(row=2, column=0, columnspan=2, pady=20, sticky="nsew")
self.scan_console.insert("end", "[*] Prêt. Les menaces seront automatiquement isolées.\n")
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=24, weight="bold")).pack(side="left")
self.btn_toggle_shield = ctk.CTkButton(header, text="Activer le Bouclier", fg_color="#27AE60", hover_color="#1E8449", command=self.toggle_shield)
self.btn_toggle_shield.pack(side="right")
ctk.CTkLabel(frame, text=f"Surveillance en direct de : {WATCH_FOLDER}\nLes nouveaux fichiers seront scannés instantanément.", justify="left").grid(row=1, column=0, sticky="w", pady=(0, 10))
self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13))
self.rt_console.grid(row=2, column=0, sticky="nsew")
self.rt_console.insert("end", "[-] Bouclier désactivé. Cliquez sur le bouton pour démarrer.\n")
def toggle_shield(self):
if not self.shield_active:
self.shield_observer = Observer()
self.shield_observer.schedule(RealTimeShieldHandler(self), WATCH_FOLDER, recursive=False)
self.shield_observer.start()
self.shield_active = True
self.btn_toggle_shield.configure(text="Désactiver le Bouclier", fg_color="#C0392B", hover_color="#922B21")
self.rt_console.insert("end", f"\n[+] BOUCLIER ACTIVÉ sur {WATCH_FOLDER}\n[*] Analyse automatique prête...\n")
else:
self.shield_observer.stop()
self.shield_active = False
self.btn_toggle_shield.configure(text="Activer le Bouclier", fg_color="#27AE60", hover_color="#1E8449")
self.rt_console.insert("end", "\n[-] Bouclier DÉSACTIVÉ.\n")
self.rt_console.see("end")
def trigger_realtime_scan(self, filepath):
self.rt_console.insert("end", f"\n⚡ Détection auto : {os.path.basename(filepath)}\n")
threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start()
# --- MOTEUR CLAMAV (Commun au Manuel et au Temps Réel) ---
def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed:
console.insert("end", "❌ Erreur : ClamAV n'est pas installé.\n")
return
console.insert("end", f"[*] Début de l'analyse : {path}\n")
console.see("end")
try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}']
if is_dir: cmd.append('-r')
cmd.append(path)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
infected = 0
for line in process.stdout:
clean_line = line.strip()
if "FOUND" in clean_line:
console.insert("end", f"☠️ DANGER : {clean_line}\n🛡️ Isolé en quarantaine.\n")
infected += 1
else:
console.insert("end", clean_line + "\n")
console.see("end")
process.wait()
if infected == 0:
console.insert("end", "[+] Analyse terminée. Fichier(s) propre(s).\n")
else:
console.insert("end", f"🚨 {infected} menace(s) neutralisée(s) !\n")
except Exception as e:
console.insert("end", f"❌ Erreur système : {e}\n")
console.see("end")
# --- VUE : AUDIT SYSTÈME ---
def init_audit_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["audit"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="Vérification des Vulnérabilités", font=ctk.CTkFont(size=24, weight="bold")).pack(side="left")
ctk.CTkButton(header, text="Lancer l'Audit", command=self.run_audit_thread).pack(side="right")
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13))
self.audit_console.grid(row=1, 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()
def perform_audit(self):
self.audit_console.insert("end", "[*] Ports réseaux ouverts (Recherche de Backdoors)...\n")
try:
self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except Exception:
self.audit_console.insert("end", "[-] Impossible de lire le réseau.\n")
self.audit_console.insert("end", "\n[*] Top 10 des processus gourmands...\n")
try:
res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True)
self.audit_console.insert("end", "\n".join(res.split('\n')[:11]) + "\n")
except Exception:
self.audit_console.insert("end", "[-] Impossible de lire les processus.\n")
# --- VUE : MISE À JOUR (OTA) ---
def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame
ctk.CTkLabel(frame, text="Mises à jour de 7LnA", font=ctk.CTkFont(size=24, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Vérifiez si une nouvelle version est disponible sur le dépôt officiel.\nL'application se mettra à jour automatiquement.", justify="left").pack(anchor="w", pady=10)
self.btn_update = ctk.CTkButton(frame, text="⬇️ Télécharger la dernière version", command=self.run_software_update, fg_color="#8E44AD", hover_color="#732D91")
self.btn_update.pack(anchor="w", pady=20)
self.update_console = ctk.CTkTextbox(frame, height=150, font=ctk.CTkFont(family="Consolas", size=13))
self.update_console.pack(fill="x", pady=10)
def run_software_update(self):
self.btn_update.configure(state="disabled")
self.update_console.insert("end", "[*] Connexion au serveur Git...\n")
try:
# os.path.abspath(__file__) trouve automatiquement le chemin exact du fichier en cours d'exécution
current_file_path = os.path.abspath(__file__)
urllib.request.urlretrieve(UPDATE_URL, current_file_path)
self.update_console.insert("end", "[+] Mise à jour téléchargée et appliquée !\n")
messagebox.showinfo("Mise à jour", "7LnA a été mis à jour avec succès. L'application va se fermer pour appliquer les changements.")
self.destroy()
except Exception as e:
self.update_console.insert("end", f"[-] Échec de la mise à jour : {e}\n")
self.btn_update.configure(state="normal")
if __name__ == "__main__":
app = Antivirus7LnA()
app.mainloop()