From 2598bd2da242766d7eeaa44011e92cefa1f74256 Mon Sep 17 00:00:00 2001 From: Harsh Raj Singhania Date: Fri, 14 Nov 2025 15:35:17 +0530 Subject: [PATCH] Release 0.7.1 --- imports/globals.py | 4 +- setup.cfg | 22 +++++++ theZoo.py | 4 +- theZoo_gui.py | 156 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 setup.cfg create mode 100644 theZoo_gui.py diff --git a/imports/globals.py b/imports/globals.py index 52e071d..e47c6d5 100644 --- a/imports/globals.py +++ b/imports/globals.py @@ -23,7 +23,7 @@ class init: def init(self): # Global Variables - version = "0.6.0 Moat" + version = "0.7.1 Moat" appname = "theZoo" codename = "Moat" authors = "Yuval Nativ, Lahad Ludar, 5fingers" @@ -64,7 +64,7 @@ class Completer: class vars: - version = "0.6.0 'Moat'" + version = "0.7.1 'Moat'" appname = "Malware DB" authors = "Yuval Nativ, Lahad Ludar, 5fingers" maintainers = [ "Shahak Shalev", "Yuval Nativ" ] diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1e229b0 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,22 @@ +[metadata] +name = theZoo +version = 0.7.1 +author = theZoo maintainers +license = GPL-3.0-or-later +license_files = LICENSE.md +long_description = file: README.md +long_description_content_type = text/markdown + +[options] +packages = find: +include_package_data = True +python_requires = >=3.8 +install_requires = + urllib3 + pyminizip + pyzipper + +[options.entry_points] +console_scripts = + theZoo = theZoo:main + theZoo-gui = theZoo_gui:run diff --git a/theZoo.py b/theZoo.py index ce6fbe8..ac223b9 100644 --- a/theZoo.py +++ b/theZoo.py @@ -28,10 +28,10 @@ from imports.globals import vars from imports.terminal_handler import Controller from imports import db_handler -__version__ = "0.6.0 Moat" +__version__ = "0.7.1 Moat" __codename__ = "Moat" __appname__ = "theZoo" -__authors__ = ["Yuval Nativ", "Shahak Shalev", "Lahad Ludar", "5Fingers"] +__authors__ = ["Yuval Nativ", "Shahak Shalev", "Lahad Ludar", "5Fingers", "Harsh Raj Singhania"] __licensev__ = "GPL v3.0" __maintainer = "Yuval Nativ & Shahak Shalev" __status__ = "Beta" diff --git a/theZoo_gui.py b/theZoo_gui.py new file mode 100644 index 0000000..66cb96c --- /dev/null +++ b/theZoo_gui.py @@ -0,0 +1,156 @@ +"""Simple Tkinter-based GUI for theZoo malware repository.""" + +from __future__ import annotations + +import tkinter as tk +from tkinter import ttk, messagebox +from typing import List, Tuple + +from imports.db_handler import DBHandler + + +class TheZooGUI: + """Minimal graphical interface for browsing malware metadata.""" + + def __init__(self, root: tk.Tk) -> None: + self.root = root + self.root.title("theZoo GUI") + self.root.geometry("900x520") + + self.db = DBHandler() + self.malware_rows: List[Tuple] = [] + + self._build_layout() + self._load_data() + + def _build_layout(self) -> None: + main_frame = ttk.Frame(self.root, padding=16) + main_frame.pack(fill=tk.BOTH, expand=True) + + search_frame = ttk.Frame(main_frame) + search_frame.pack(fill=tk.X, pady=(0, 12)) + + ttk.Label(search_frame, text="Filter:").pack(side=tk.LEFT) + self.search_var = tk.StringVar() + search_entry = ttk.Entry(search_frame, textvariable=self.search_var) + search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(8, 0)) + search_entry.bind("", self._filter_rows) + + columns = ("ID", "TYPE", "LANGUAGE", "ARCH", "PLATFORM", "NAME") + self.tree = ttk.Treeview( + main_frame, + columns=columns, + show="headings", + height=15, + ) + + for col in columns: + self.tree.heading(col, text=col.title()) + self.tree.column(col, width=120 if col != "NAME" else 220, anchor=tk.W) + + self.tree.pack(fill=tk.BOTH, expand=True) + self.tree.bind("<>", self._on_select) + + button_frame = ttk.Frame(main_frame) + button_frame.pack(fill=tk.X, pady=(12, 0)) + + self.details_button = ttk.Button( + button_frame, + text="Show Details", + command=self._show_selected_details, + state=tk.DISABLED, + ) + self.details_button.pack(side=tk.LEFT) + + self.status_var = tk.StringVar(value="Loaded 0 entries") + ttk.Label(button_frame, textvariable=self.status_var).pack(side=tk.RIGHT) + + def _load_data(self) -> None: + try: + self.malware_rows = self.db.get_partial_details() + except Exception as exc: # pragma: no cover - defensive UI code + messagebox.showerror("Database Error", str(exc)) + self.malware_rows = [] + finally: + self._refresh_tree(self.malware_rows) + + def _refresh_tree(self, rows: List[Tuple]) -> None: + self.tree.delete(*self.tree.get_children()) + for row in rows: + self.tree.insert("", tk.END, iid=str(row[0]), values=row) + self.status_var.set(f"Loaded {len(rows)} entries") + self.details_button.configure(state=tk.DISABLED) + + def _filter_rows(self, _event: tk.Event) -> None: + text = self.search_var.get().strip().lower() + if not text: + self._refresh_tree(self.malware_rows) + return + + filtered = [ + row for row in self.malware_rows + if any(text in str(value).lower() for value in row) + ] + self._refresh_tree(filtered) + + def _on_select(self, _event: tk.Event) -> None: + has_selection = bool(self.tree.selection()) + new_state = tk.NORMAL if has_selection else tk.DISABLED + self.details_button.configure(state=new_state) + + def _show_selected_details(self) -> None: + selection = self.tree.selection() + if not selection: + return + + mal_id = int(selection[0]) + try: + result = self.db.get_mal_info(mal_id) + except Exception as exc: # pragma: no cover - defensive UI code + messagebox.showerror("Database Error", str(exc)) + return + + if not result: + messagebox.showinfo("Malware Info", "No additional metadata available.") + return + + ( + mal_type, + name, + version, + author, + language, + date, + architecture, + platform, + tags, + ) = result[0] + + info_lines = [ + f"Name: {name or 'N/A'}", + f"Type: {mal_type or 'N/A'}", + f"Version: {version or 'N/A'}", + f"Author: {author or 'N/A'}", + f"Language: {language or 'N/A'}", + f"Date: {date or 'N/A'}", + f"Architecture: {architecture or 'N/A'}", + f"Platform: {platform or 'N/A'}", + f"Tags: {tags or 'N/A'}", + ] + + messagebox.showinfo("Malware Info", "\n".join(info_lines)) + + def on_close(self) -> None: + self.db.close_connection() + self.root.destroy() + + +def run() -> None: + root = tk.Tk() + app = TheZooGUI(root) + root.protocol("WM_DELETE_WINDOW", app.on_close) + root.mainloop() + + +if __name__ == "__main__": + run()