Actualiser 7lna.py
This commit is contained in:
260
7lna.py
260
7lna.py
@@ -37,13 +37,9 @@ def send_desktop_notification(title, message, is_critical=False):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ==========================================
|
||||
# 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)
|
||||
@@ -61,8 +57,7 @@ class Antivirus7LnA(ctk.CTk):
|
||||
|
||||
os.makedirs(QUARANTINE_DIR, exist_ok=True)
|
||||
if not os.path.exists(HISTORY_FILE):
|
||||
with open(HISTORY_FILE, "w") as f:
|
||||
json.dump([], f)
|
||||
with open(HISTORY_FILE, "w") as f: json.dump([], f)
|
||||
|
||||
self.shield_observer = None
|
||||
self.shield_active = False
|
||||
@@ -70,14 +65,13 @@ class Antivirus7LnA(ctk.CTk):
|
||||
self.check_dependencies()
|
||||
self.setup_ui()
|
||||
|
||||
# Lancement de la surveillance USB en arrière-plan
|
||||
threading.Thread(target=self.monitor_usb_drives, daemon=True).start()
|
||||
|
||||
def check_dependencies(self):
|
||||
try:
|
||||
subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
|
||||
self.clamav_installed = True
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
except:
|
||||
self.clamav_installed = False
|
||||
|
||||
def setup_ui(self):
|
||||
@@ -97,14 +91,13 @@ class Antivirus7LnA(ctk.CTk):
|
||||
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") # NOUVEAU
|
||||
self.btn_history = self.create_nav_button("📜 Rapports", 8, "history") # NOUVEAU
|
||||
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")
|
||||
|
||||
self.version_label = ctk.CTkLabel(self.sidebar, text="v8.0 - Enterprise", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
|
||||
self.version_label = ctk.CTkLabel(self.sidebar, text="v8.1 - Enterprise", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
|
||||
self.version_label.grid(row=10, column=0, pady=20, sticky="s")
|
||||
|
||||
# --- CONTENEUR DES VUES ---
|
||||
self.views = {}
|
||||
self.init_dashboard_view()
|
||||
self.init_scanner_view()
|
||||
@@ -112,8 +105,8 @@ class Antivirus7LnA(ctk.CTk):
|
||||
self.init_audit_view()
|
||||
self.init_tools_view()
|
||||
self.init_quarantine_view()
|
||||
self.init_schedule_view() # NOUVEAU
|
||||
self.init_history_view() # NOUVEAU
|
||||
self.init_schedule_view()
|
||||
self.init_history_view()
|
||||
self.init_update_view()
|
||||
|
||||
self.select_view("dashboard")
|
||||
@@ -126,14 +119,12 @@ class Antivirus7LnA(ctk.CTk):
|
||||
return btn
|
||||
|
||||
def select_view(self, view_name):
|
||||
for view in self.views.values():
|
||||
view.grid_forget()
|
||||
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)
|
||||
if view_name == "quarantine": self.refresh_quarantine_list()
|
||||
if view_name == "history": self.refresh_history_list()
|
||||
|
||||
# --- UTILITAIRES DE CONSOLE ---
|
||||
def setup_console_tags(self, console):
|
||||
console.tag_config("danger", foreground="#EF4444")
|
||||
console.tag_config("success", foreground="#10B981")
|
||||
@@ -146,19 +137,14 @@ class Antivirus7LnA(ctk.CTk):
|
||||
def log_threat_to_history(self, filepath, threat_name):
|
||||
try:
|
||||
with open(HISTORY_FILE, "r") as f: data = json.load(f)
|
||||
data.append({
|
||||
"date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"file": filepath,
|
||||
"threat": threat_name
|
||||
})
|
||||
data.append({"date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "file": filepath, "threat": threat_name})
|
||||
with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4)
|
||||
except Exception as e: print("Erreur d'historique:", e)
|
||||
except: pass
|
||||
|
||||
# --- NOUVEAU : AUTO-DETECT USB ---
|
||||
# --- AUTO-DETECT USB ---
|
||||
def monitor_usb_drives(self):
|
||||
media_dir = f"/media/{os.getlogin()}"
|
||||
if not os.path.exists(media_dir): return
|
||||
|
||||
known_mounts = set(os.listdir(media_dir))
|
||||
while True:
|
||||
time.sleep(3)
|
||||
@@ -166,14 +152,13 @@ class Antivirus7LnA(ctk.CTk):
|
||||
current_mounts = set(os.listdir(media_dir))
|
||||
new_mounts = current_mounts - known_mounts
|
||||
for mount in new_mounts:
|
||||
full_path = os.path.join(media_dir, mount)
|
||||
self.after(0, self.prompt_usb_scan, full_path, mount)
|
||||
self.after(0, self.prompt_usb_scan, os.path.join(media_dir, mount), mount)
|
||||
known_mounts = current_mounts
|
||||
except Exception: pass
|
||||
except: pass
|
||||
|
||||
def prompt_usb_scan(self, path, name):
|
||||
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 lancer une analyse antivirus dessus ?"):
|
||||
if messagebox.askyesno("Protection USB", f"Nouveau périphérique USB 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()
|
||||
|
||||
@@ -202,17 +187,15 @@ class Antivirus7LnA(ctk.CTk):
|
||||
|
||||
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))
|
||||
|
||||
# Ligne 1 des boutons
|
||||
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.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.grid(row=1, column=1, padx=(5, 5), pady=5, sticky="ew")
|
||||
|
||||
self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures (freshclam)", command=self.update_virus_db, height=45, fg_color="#059669", hover_color="#047857")
|
||||
self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures", command=self.update_virus_db, height=45, fg_color="#059669", hover_color="#047857")
|
||||
self.btn_db_update.grid(row=1, column=2, padx=(5, 0), pady=5, sticky="ew")
|
||||
|
||||
# NOUVEAU : Scanner Rootkit
|
||||
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")
|
||||
|
||||
@@ -225,22 +208,57 @@ class Antivirus7LnA(ctk.CTk):
|
||||
self.setup_console_tags(self.scan_console)
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V8 prêt...\n", "info")
|
||||
|
||||
# ----- CORRECTION DU ROOTKIT (Mot de passe intégré) -----
|
||||
def run_rootkit_scan(self):
|
||||
threading.Thread(target=self._exec_rootkit, daemon=True).start()
|
||||
# Création d'une fenêtre pop-up pour demander le mot de passe sans dépendre du système
|
||||
dialog = ctk.CTkToplevel(self)
|
||||
dialog.title("Sécurité Administrateur")
|
||||
dialog.geometry("400x220")
|
||||
dialog.attributes("-topmost", True)
|
||||
dialog.grab_set() # Empêche de cliquer ailleurs
|
||||
|
||||
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'analyse anti-rootkit nécessite les droits sudo.\nEntrez votre mot de passe session :", justify="center").pack(pady=5)
|
||||
|
||||
pwd_entry = ctk.CTkEntry(dialog, show="*", width=250)
|
||||
pwd_entry.pack(pady=10)
|
||||
pwd_entry.focus()
|
||||
|
||||
def on_submit(event=None):
|
||||
pwd = pwd_entry.get()
|
||||
dialog.destroy()
|
||||
if pwd:
|
||||
threading.Thread(target=self._exec_rootkit, args=(pwd,), daemon=True).start()
|
||||
|
||||
ctk.CTkButton(dialog, text="Lancer l'analyse", command=on_submit, fg_color="#DC2626", hover_color="#991B1B").pack(pady=10)
|
||||
dialog.bind("<Return>", on_submit)
|
||||
|
||||
def _exec_rootkit(self):
|
||||
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de l'analyse Rootkit (Requiert l'authentification)...\n", "info")
|
||||
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_progress.start()
|
||||
try:
|
||||
# pkexec permet de demander le mot de passe proprement en interface graphique Linux
|
||||
process = subprocess.Popen(['pkexec', 'rkhunter', '-c', '--sk', '--report-warnings-only'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
for line in process.stdout:
|
||||
self.scan_console.insert("end", line)
|
||||
# -S permet de passer le mot de passe via l'entrée standard
|
||||
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)
|
||||
|
||||
# Envoi du mot de passe
|
||||
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()
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\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 FileNotFoundError:
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] rkhunter n'est pas installé. (sudo apt install rkhunter)\n", "warning")
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] rkhunter n'est pas installé. (Lancez le nouveau script install.sh)\n", "warning")
|
||||
except Exception as e:
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
|
||||
finally:
|
||||
@@ -259,10 +277,8 @@ class Antivirus7LnA(ctk.CTk):
|
||||
self.scan_console.see("end")
|
||||
process.wait()
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures à jour.\n", "success")
|
||||
except Exception as e:
|
||||
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
|
||||
finally:
|
||||
self.scan_progress.stop()
|
||||
except: pass
|
||||
finally: self.scan_progress.stop()
|
||||
|
||||
def start_manual_scan(self, is_dir):
|
||||
path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename()
|
||||
@@ -274,14 +290,11 @@ class Antivirus7LnA(ctk.CTk):
|
||||
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, font=ctk.CTkFont(weight="bold"))
|
||||
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)
|
||||
@@ -297,7 +310,7 @@ class Antivirus7LnA(ctk.CTk):
|
||||
else:
|
||||
self.shield_observer.stop()
|
||||
self.shield_active = False
|
||||
self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857")
|
||||
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):
|
||||
@@ -309,34 +322,25 @@ class Antivirus7LnA(ctk.CTk):
|
||||
if not self.clamav_installed: return
|
||||
console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n")
|
||||
if console == self.scan_console: self.scan_progress.start()
|
||||
|
||||
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:
|
||||
threat_details = clean_line.split(":")
|
||||
threat_name = threat_details[-1].replace("FOUND", "").strip() if len(threat_details) > 1 else "Malware Inconnu"
|
||||
|
||||
threat_name = clean_line.split(":")[-1].replace("FOUND", "").strip()
|
||||
console.insert("end", f"{self.get_time_prefix()}☠️ MENACE DÉTECTÉE : {clean_line}\n", "danger")
|
||||
infected += 1
|
||||
self.log_threat_to_history(path, threat_name) # Enregistrement dans l'historique
|
||||
elif clean_line:
|
||||
console.insert("end", f" -> {clean_line}\n")
|
||||
self.log_threat_to_history(path, threat_name)
|
||||
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")
|
||||
else:
|
||||
send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s) et isolée(s).", is_critical=True)
|
||||
except Exception as e:
|
||||
console.insert("end", f"{self.get_time_prefix()}❌ Erreur Moteur : {e}\n", "danger")
|
||||
if infected == 0: console.insert("end", f"{self.get_time_prefix()}[+] Fichier 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")
|
||||
@@ -350,81 +354,61 @@ class Antivirus7LnA(ctk.CTk):
|
||||
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))
|
||||
btn_frame = ctk.CTkFrame(frame, fg_color="transparent")
|
||||
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
|
||||
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 Statut UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left")
|
||||
ctk.CTkButton(btn_frame, text="Lancer l'Audit", 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")
|
||||
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
|
||||
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):
|
||||
self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Recherche de connexions fantômes (Ports ouverts)...\n", "info")
|
||||
try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
|
||||
except: pass
|
||||
self.audit_console.insert("end", f"\n{self.get_time_prefix()}[*] Top 10 Processus (Charge CPU)...\n", "info")
|
||||
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")
|
||||
self.audit_console.insert("end", "\n" + "\n".join(res.split('\n')[:11]) + "\n")
|
||||
except: pass
|
||||
|
||||
def check_firewall(self):
|
||||
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", "success")
|
||||
else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n", "danger")
|
||||
except: self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Impossible de déterminer l'état de UFW.\n", "warning")
|
||||
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")
|
||||
except: pass
|
||||
|
||||
# --- VUE : OUTILS AVANCÉS (Avec Nettoyeur) ---
|
||||
# --- VUE : OUTILS AVANCÉS ---
|
||||
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))
|
||||
|
||||
# NOUVEAU : Nettoyeur Système
|
||||
clean_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
|
||||
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="Vide la corbeille, les fichiers temporaires et le cache de l'utilisateur pour libérer de l'espace.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
|
||||
ctk.CTkButton(clean_card, text="Nettoyer l'ordinateur", fg_color="#059669", hover_color="#047857", command=self.run_cleaner).pack(anchor="w", padx=20)
|
||||
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)
|
||||
|
||||
# Destructeur de Fichiers
|
||||
shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
|
||||
shred_card.pack(fill="x", pady=10, ipady=15)
|
||||
ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers (File Shredder)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0))
|
||||
ctk.CTkLabel(shred_card, text="Écrase le fichier avec des données aléatoires (3 passes) pour empêcher sa récupération.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
|
||||
ctk.CTkButton(shred_card, text="Détruire un fichier à jamais", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20)
|
||||
|
||||
# Calculateur de Hash
|
||||
hash_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
|
||||
hash_card.pack(fill="x", pady=10, ipady=15)
|
||||
ctk.CTkLabel(hash_card, text="🧬 Extracteur d'Empreintes (Hash)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#3B82F6").pack(anchor="w", padx=20, pady=(10,0))
|
||||
self.hash_result_var = ctk.StringVar(value="Aucun fichier sélectionné.")
|
||||
ctk.CTkButton(hash_card, text="Sélectionner un fichier", fg_color="#2563EB", hover_color="#1D4ED8", command=self.calculate_hash).pack(anchor="w", padx=20, pady=(0, 10))
|
||||
ctk.CTkEntry(hash_card, textvariable=self.hash_result_var, state="readonly", width=500, fg_color="#111827").pack(anchor="w", padx=20)
|
||||
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)
|
||||
|
||||
def run_cleaner(self):
|
||||
freed_space = 0
|
||||
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:
|
||||
freed_space += os.path.getsize(item_path)
|
||||
if os.path.isfile(item_path): os.remove(item_path)
|
||||
elif os.path.isdir(item_path): shutil.rmtree(item_path)
|
||||
except: pass
|
||||
|
||||
freed_mb = freed_space / (1024 * 1024)
|
||||
messagebox.showinfo("Nettoyage Terminé", f"Le nettoyage est terminé.\nEspace libéré : {freed_mb:.2f} MB")
|
||||
messagebox.showinfo("Succès", "Nettoyage terminé.")
|
||||
|
||||
def run_shredder(self):
|
||||
filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ LE FICHIER À DÉTRUIRE")
|
||||
filepath = filedialog.askopenfilename()
|
||||
if not filepath: return
|
||||
if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
|
||||
try:
|
||||
@@ -435,40 +419,43 @@ class Antivirus7LnA(ctk.CTk):
|
||||
f.write(os.urandom(file_size))
|
||||
os.remove(filepath)
|
||||
messagebox.showinfo("Succès", "Fichier détruit.")
|
||||
except Exception as e: messagebox.showerror("Erreur", str(e))
|
||||
except: pass
|
||||
|
||||
def calculate_hash(self):
|
||||
filepath = filedialog.askopenfilename()
|
||||
if not filepath: return
|
||||
try:
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block)
|
||||
self.hash_result_var.set(f"SHA-256: {sha256_hash.hexdigest()}")
|
||||
except: self.hash_result_var.set("Erreur de lecture.")
|
||||
|
||||
# --- NOUVEAU : VUE PLANIFICATION (CRON) ---
|
||||
# ----- CORRECTION PLANIFICATION (Sélecteurs d'Heure) -----
|
||||
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 de votre système via les tâches planifiées Linux (Cron).", text_color="#9CA3AF").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(card, text="Programmer un scan automatique de ~/Téléchargements", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=10)
|
||||
ctk.CTkButton(card, text="Activer le Scan Quotidien (12h00)", fg_color="#2563EB", hover_color="#1D4ED8", command=self.setup_cronjob).pack(pady=10)
|
||||
ctk.CTkButton(card, text="Désactiver les scans programmés", fg_color="#4B5563", hover_color="#374151", command=self.remove_cronjob).pack(pady=10)
|
||||
ctk.CTkLabel(card, text="Programmer un scan automatique (Dossier Téléchargements)", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(10, 5))
|
||||
|
||||
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)
|
||||
self.cron_h_var = ctk.StringVar(value="12")
|
||||
self.cron_m_var = ctk.StringVar(value="00")
|
||||
|
||||
# Menus déroulants élégants pour le choix de l'heure
|
||||
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.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)
|
||||
|
||||
def setup_cronjob(self):
|
||||
cron_command = f"0 12 * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1"
|
||||
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"
|
||||
try:
|
||||
# Récupère l'ancien crontab, enlève nos anciennes tâches, ajoute la nouvelle
|
||||
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cron_command}") | crontab -')
|
||||
messagebox.showinfo("Succès", "Scan automatique configuré tous les jours à 12h00.")
|
||||
except Exception as e:
|
||||
messagebox.showerror("Erreur", "Impossible de configurer Cron.")
|
||||
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:
|
||||
@@ -476,17 +463,13 @@ class Antivirus7LnA(ctk.CTk):
|
||||
messagebox.showinfo("Succès", "Planification désactivée.")
|
||||
except: pass
|
||||
|
||||
# --- NOUVEAU : VUE HISTORIQUE ---
|
||||
# --- 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
|
||||
|
||||
header = ctk.CTkFrame(frame, fg_color="transparent")
|
||||
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
|
||||
ctk.CTkLabel(header, text="📜 Registre des Menaces", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
|
||||
|
||||
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)
|
||||
self.history_console.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
@@ -494,12 +477,11 @@ class Antivirus7LnA(ctk.CTk):
|
||||
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. Aucun virus n'a été détecté sur cette machine.\n")
|
||||
if not data: self.history_console.insert("end", "\n ✅ Historique vierge.\n")
|
||||
else:
|
||||
for entry in reversed(data): # Du plus récent au plus ancien
|
||||
for entry in reversed(data):
|
||||
self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['file']}\n\n")
|
||||
except: self.history_console.insert("end", "Impossible de lire l'historique.")
|
||||
except: pass
|
||||
|
||||
# --- VUE : QUARANTAINE ---
|
||||
def init_quarantine_view(self):
|
||||
@@ -519,21 +501,17 @@ class Antivirus7LnA(ctk.CTk):
|
||||
files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
|
||||
if not files: self.quarantine_listbox.insert("end", "\n ✅ Aucun fichier malveillant isolé.\n")
|
||||
else:
|
||||
self.quarantine_listbox.insert("end", f" ⚠️ {len(files)} menace(s) :\n\n")
|
||||
for f in files:
|
||||
size_kb = os.path.getsize(f) / 1024
|
||||
self.quarantine_listbox.insert("end", f" -> {os.path.basename(f)} ({size_kb:.1f} KB)\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 not files: return
|
||||
if messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"):
|
||||
if files and messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"):
|
||||
for f in files:
|
||||
try: os.remove(f)
|
||||
except: pass
|
||||
self.refresh_quarantine_list()
|
||||
|
||||
# --- VUE : MISE À JOUR (OTA) ---
|
||||
# --- VUE : MISE À JOUR ---
|
||||
def init_update_view(self):
|
||||
frame = ctk.CTkFrame(self, fg_color="transparent")
|
||||
self.views["update"] = frame
|
||||
@@ -542,19 +520,17 @@ class Antivirus7LnA(ctk.CTk):
|
||||
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)
|
||||
self.setup_console_tags(self.update_console)
|
||||
|
||||
def run_software_update(self):
|
||||
self.btn_update_soft.configure(state="disabled")
|
||||
self.update_console.insert("end", f"{self.get_time_prefix()}[*] Négociation avec serveur...\n", "info")
|
||||
self.update_console.insert("end", "[*] Négociation avec serveur...\n")
|
||||
try:
|
||||
current_file_path = os.path.abspath(__file__)
|
||||
urllib.request.urlretrieve(UPDATE_URL, current_file_path)
|
||||
self.update_console.insert("end", f"{self.get_time_prefix()}[+] Code source mis à jour !\n", "success")
|
||||
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 Exception as e:
|
||||
self.update_console.insert("end", f"{self.get_time_prefix()}[-] Échec : {e}\n", "danger")
|
||||
except:
|
||||
self.update_console.insert("end", "[-] Échec.\n")
|
||||
self.btn_update_soft.configure(state="normal")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user