diff --git a/attack_module.7z b/attack_module.7z new file mode 100644 index 0000000..adf57b2 Binary files /dev/null and b/attack_module.7z differ diff --git a/dpg.ini b/dpg.ini index ebeffd3..ab1064d 100644 --- a/dpg.ini +++ b/dpg.ini @@ -39,7 +39,7 @@ Size=210,800 Collapsed=0 [Window][###108] -Pos=340,250 +Pos=492,806 Size=600,300 Collapsed=0 @@ -83,3 +83,68 @@ Pos=340,250 Size=600,300 Collapsed=0 +[Window][###126] +Pos=532,210 +Size=400,200 +Collapsed=0 + +[Window][###298] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###204] +Pos=247,200 +Size=700,400 +Collapsed=0 + +[Window][###226] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###288] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###310] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###332] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###354] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###376] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###398] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###305] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###327] +Pos=300,200 +Size=700,400 +Collapsed=0 + +[Window][###426] +Pos=300,200 +Size=700,400 +Collapsed=0 + diff --git a/pyproject.toml b/pyproject.toml index 06c004e..c68da42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,9 @@ dependencies = [ "dbus-python (>=1.4.0,<2.0.0)", "dearpygui (>=2.0.0,<3.0.0)", "pyqt5 (>=5.15.11,<6.0.0)", - "python-gvm (>=26.2.0,<27.0.0)" + "python-gvm (>=26.2.0,<27.0.0)", + "reportlab (>=4.4.1,<5.0.0)", + "pyserial (>=3.5,<4.0)" ] diff --git a/scripts/modbus_test.py b/scripts/modbus_test.py new file mode 100644 index 0000000..4e7d039 --- /dev/null +++ b/scripts/modbus_test.py @@ -0,0 +1,18 @@ +from pymodbus.client import ModbusSerialClient + +client = ModbusSerialClient(port="/dev/ttyUSB0", baudrate=9600) + +result = client.read_input_registers(address=1, count=2, slave=1) + +print(f"Temp {result.registers[0] / 10}, Humidity {result.registers[1] / 10}") + +slave_id = client.read_holding_registers(address=257, count=4, slave=1) + +print(f"""Slave id {slave_id.registers[0]} Baudrate {slave_id.registers[1]} + Temp correction {slave_id.registers[2]} Humidity correction {slave_id.registers[3]}""") +print(slave_id) + +# change_slave_id = client.write_register(address=257, value=1, slave=101) +# change_baudrate = client.write_register(address=258, value=1, slave=101) +# print(change_slave_id) +# print(change_baudrate) diff --git a/src/core/api/openvas.py b/src/core/api/openvas.py index 2db3e89..101b571 100644 --- a/src/core/api/openvas.py +++ b/src/core/api/openvas.py @@ -1,4 +1,4 @@ -from gvm.connections import UnixSocketConnection +from gvm.connections import TLSConnection from gvm.protocols.gmp import Gmp from gvm.errors import GvmError from src.utils.logger import get_logger @@ -6,13 +6,15 @@ from src.utils.logger import get_logger logger = get_logger("openvas_scanner") class OpenvasScanner: - def __init__(self, socket_path='/var/lib/docker/volumes/greenbone-community-edition_gvmd_socket_vol/_data/gvmd.sock'): + def __init__(self, host='127.0.0.1'): """ Инициализация сканера OpenVAS. :param socket_path: Путь к сокету GVM (по умолчанию '/var/run/gvmd.sock') """ - self.socket_path = socket_path + self.host = host + self.port = 9392 + self.password = None self.connection = None self.gmp = None @@ -24,7 +26,7 @@ class OpenvasScanner: :param password: Пароль для аутентификации """ try: - self.connection = UnixSocketConnection(path=self.socket_path) + self.connection = TLSConnection(hostname=self.host, port=self.port) with Gmp(connection=self.connection) as gmp: gmp.authenticate(username, password) logger.info(f"Успешно подключено к GVM через {self.socket_path}") diff --git a/src/core/database/dao/session_dao.py b/src/core/database/dao/session_dao.py index 7149f7d..23e9dda 100644 --- a/src/core/database/dao/session_dao.py +++ b/src/core/database/dao/session_dao.py @@ -40,7 +40,7 @@ class SessionDAO: def get_all(self) -> List[Session]: """Получение всех сессий.""" with self.db.get_cursor() as cursor: - cursor.execute("SELECT * FROM sessions ORDER BY created_at DESC") + cursor.execute("SELECT * FROM sessions ORDER BY created_at ASC") rows = cursor.fetchall() return [ Session( diff --git a/src/core/database/database.py b/src/core/database/database.py index a846e9b..6666303 100644 --- a/src/core/database/database.py +++ b/src/core/database/database.py @@ -1,18 +1,21 @@ # src/core/database/db.py import sqlite3 +from contextlib import contextmanager from pathlib import Path from typing import Optional -from contextlib import contextmanager from src.utils.logger import get_logger logger = get_logger("database") + class Database: - def __init__(self, db_path: str = "/home/lodqa/attack_module_data/security_scanner.db"): + def __init__( + self, db_path: str = "/home/lodqa/attack_module_data/security_scanner.db" + ): self.db_path = Path(db_path) self.db_path.parent.mkdir(parents=True, exist_ok=True) - self.conn: Optional[sqlite3.Connection] = None + self.conn: Optional[sqlite3.Connection] self.init_db() def connect(self): @@ -133,4 +136,5 @@ class Database: """Закрытие соединения с базой данных.""" if self.conn: self.conn.close() - logger.info("Database connection closed") \ No newline at end of file + logger.info("Database connection closed") + diff --git a/src/core/services/reports.py b/src/core/services/reports.py new file mode 100644 index 0000000..60c22e2 --- /dev/null +++ b/src/core/services/reports.py @@ -0,0 +1,425 @@ +import json +import os +from pathlib import Path +from reportlab.lib.pagesizes import letter +from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak +from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle +from reportlab.lib import colors +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont +from src.core.database.managers.scanner_manager import ScannerManager +from src.utils.logger import get_logger + +logger = get_logger("reports") + +# Определяем базовый путь проекта +BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent + +# Регистрируем шрифт Exo 2 +def register_exo2_font(): + """Регистрация шрифта Exo 2 для поддержки кириллицы""" + try: + font_path = BASE_DIR / "fonts" / "Exo2.ttf" + + # Регистрируем обычное начертание + pdfmetrics.registerFont(TTFont('Exo2', str(font_path))) + + # Регистрируем жирное начертание (используем тот же файл, но с другим именем) + pdfmetrics.registerFont(TTFont('Exo2-Bold', str(font_path))) + + # Создаем псевдо-курсивное начертание (если нужно) + pdfmetrics.registerFont(TTFont('Exo2-Italic', str(font_path))) + + # Регистрируем семейство шрифтов + pdfmetrics.registerFontFamily('Exo2', normal='Exo2', bold='Exo2-Bold', italic='Exo2-Italic') + return True + except Exception as e: + logger.error(f"Ошибка регистрации шрифта Exo 2: {e}") + # Возвращаем False, если шрифт не удалось зарегистрировать + return False + +# Функция generate_json_report остается без изменений + +def generate_pdf_report(scanner_service: ScannerManager, session_id: int, output_path: str): + """Генерация PDF отчета для указанной сессии с использованием шрифта Exo 2""" + try: + # Регистрируем шрифт + font_registered = register_exo2_font() + base_font = 'Exo2' if font_registered else 'Helvetica' + + session = scanner_service.get_session_by_id(session_id) + if not session: + logger.warning(f"Сессия с ID {session_id} не найдена") + return + + scans = scanner_service.get_scans_by_session(session_id) + attacks = scanner_service.get_attacks_by_session(session_id) + + # Создаем PDF документ + doc = SimpleDocTemplate(output_path, pagesize=letter) + + # Создаем кастомные стили с использованием Exo 2 + styles = getSampleStyleSheet() + + # Переопределяем стили для поддержки Exo 2 + title_style = ParagraphStyle( + 'Title', + parent=styles['Title'], + fontName=f'{base_font}-Bold', + fontSize=18, + alignment=1, + spaceAfter=12 + ) + + heading1_style = ParagraphStyle( + 'Heading1', + parent=styles['Heading1'], + fontName=f'{base_font}-Bold', + fontSize=14, + spaceAfter=6 + ) + + heading2_style = ParagraphStyle( + 'Heading2', + parent=styles['Heading2'], + fontName=f'{base_font}-Bold', + fontSize=12, + spaceAfter=6 + ) + + heading3_style = ParagraphStyle( + 'Heading3', + parent=styles['Heading3'], + fontName=f'{base_font}-Bold', + fontSize=11, + spaceAfter=6 + ) + + heading4_style = ParagraphStyle( + 'Heading4', + parent=styles['Heading4'], + fontName=base_font, + fontSize=10, + spaceAfter=4 + ) + + normal_style = ParagraphStyle( + 'Normal', + parent=styles['Normal'], + fontName=base_font, + fontSize=10, + spaceAfter=6 + ) + + table_header_style = ParagraphStyle( + 'TableHeader', + parent=styles['Normal'], + fontName=f'{base_font}-Bold', + fontSize=10, + alignment=1, + textColor=colors.black + ) + + table_cell_style = ParagraphStyle( + 'TableCell', + parent=styles['Normal'], + fontName=base_font, + fontSize=9, + alignment=1 + ) + + elements = [] + + # Заголовок отчета + elements.append(Paragraph(f"Отчет по тестированию безопасности", title_style)) + elements.append(Paragraph(f"Сессия: {session.name}", heading2_style)) + elements.append(Spacer(1, 12)) + + # Информация о сессии + elements.append(Paragraph("Информация о сессии", heading2_style)) + session_data = [ + [Paragraph("ID", table_header_style), Paragraph(str(session.id), table_cell_style)], + [Paragraph("Название", table_header_style), Paragraph(session.name, table_cell_style)], + [Paragraph("Дата создания", table_header_style), Paragraph(str(session.created_at), table_cell_style)] + ] + session_table = Table(session_data, colWidths=[100, 400]) + session_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ])) + elements.append(session_table) + elements.append(Spacer(1, 24)) + + # Сканирования + if scans: + elements.append(PageBreak()) + elements.append(Paragraph("Результаты сканирования", heading1_style)) + + for scan in scans: + elements.append(Paragraph(f"Сканирование: {scan.tool}", heading2_style)) + scan_data = [ + [Paragraph("Параметр", table_header_style), Paragraph("Значение", table_header_style)], + [Paragraph("ID", table_header_style), Paragraph(str(scan.id), table_cell_style)], + [Paragraph("Аргументы", table_header_style), Paragraph(scan.args or "Нет", table_cell_style)], + [Paragraph("Сводка", table_header_style), Paragraph(scan.summary or "Нет", table_cell_style)], + [Paragraph("Длительность", table_header_style), Paragraph(f"{scan.duration} секунд", table_cell_style)], + [Paragraph("Дата создания", table_header_style), Paragraph(str(scan.created_at), table_cell_style)] + ] + scan_table = Table(scan_data, colWidths=[150, 350]) + scan_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ])) + elements.append(scan_table) + elements.append(Spacer(1, 12)) + + # Хосты + hosts = scanner_service.get_hosts_by_scan(scan.id) + if hosts: + elements.append(Paragraph("Обнаруженные хосты", heading3_style)) + host_data = [[ + Paragraph("IP адрес", table_header_style), + Paragraph("MAC адрес", table_header_style), + Paragraph("Операционная система", table_header_style) + ]] + + for host in hosts: + host_data.append([ + Paragraph(host.ip, table_cell_style), + Paragraph(host.mac or "Нет", table_cell_style), + Paragraph(host.os or "Нет", table_cell_style) + ]) + + host_table = Table(host_data, colWidths=[150, 150, 200]) + host_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ])) + elements.append(host_table) + elements.append(Spacer(1, 12)) + + # Порты и сервисы + for host in hosts: + ports = scanner_service.get_ports_by_host(host.id) + if ports: + elements.append(Paragraph(f"Порты и сервисы для хоста: {host.ip}", heading4_style)) + + for port in ports: + port_info = [ + [Paragraph("Порт", table_header_style), Paragraph("Протокол", table_header_style), Paragraph("Состояние", table_header_style)], + [ + Paragraph(str(port.port_num), table_cell_style), + Paragraph(port.protocol, table_cell_style), + Paragraph(port.state, table_cell_style) + ] + ] + port_table = Table(port_info, colWidths=[80, 80, 80]) + port_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#5D7CA6")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#F0F5FC")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ])) + elements.append(port_table) + + services = scanner_service.get_services_by_port(port.id) + if services: + elements.append(Paragraph("Сервисы:", heading4_style)) + service_data = [[ + Paragraph("Название", table_header_style), + Paragraph("Продукт", table_header_style), + Paragraph("Версия", table_header_style), + Paragraph("Доп. информация", table_header_style) + ]] + + for service in services: + service_data.append([ + Paragraph(service.name or "Нет", table_cell_style), + Paragraph(service.product or "Нет", table_cell_style), + Paragraph(service.version or "Нет", table_cell_style), + Paragraph(service.extrainfo or "Нет", table_cell_style) + ]) + + service_table = Table(service_data, colWidths=[100, 100, 80, 120]) + service_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#6D8CB8")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#F5F8FD")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ])) + elements.append(service_table) + elements.append(Spacer(1, 8)) + + # Результаты Modbus + modbus_result = scanner_service.get_modbus_result(scan.id) + if modbus_result: + elements.append(Paragraph("Результаты Modbus сканирования", heading3_style)) + modbus_data = [ + [Paragraph("Параметр", table_header_style), Paragraph("Значение", table_header_style)], + [Paragraph("Активные Coils", table_header_style), Paragraph(modbus_result.active_coils or "Нет", table_cell_style)], + [Paragraph("Активные Discrete Inputs", table_header_style), Paragraph(modbus_result.active_discrete_inputs or "Нет", table_cell_style)], + [Paragraph("Активные Holding Registers", table_header_style), Paragraph(modbus_result.active_holding_registers or "Нет", table_cell_style)], + [Paragraph("Активные Input Registers", table_header_style), Paragraph(modbus_result.active_input_registers or "Нет", table_cell_style)] + ] + modbus_table = Table(modbus_data, colWidths=[200, 300]) + modbus_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ])) + elements.append(modbus_table) + + elements.append(Spacer(1, 24)) + + # Атаки + if attacks: + elements.append(PageBreak()) + elements.append(Paragraph("Результаты атак", heading1_style)) + + for attack in attacks: + elements.append(Paragraph(f"Атака: {attack.tool}", heading2_style)) + attack_data = [ + [Paragraph("Параметр", table_header_style), Paragraph("Значение", table_header_style)], + [Paragraph("ID", table_header_style), Paragraph(str(attack.id), table_cell_style)], + [Paragraph("Аргументы", table_header_style), Paragraph(attack.args or "Нет", table_cell_style)], + [Paragraph("Сводка", table_header_style), Paragraph(attack.summary or "Нет", table_cell_style)], + [Paragraph("Длительность", table_header_style), Paragraph(f"{attack.duration} секунд", table_cell_style)], + [Paragraph("Дата создания", table_header_style), Paragraph(str(attack.created_at), table_cell_style)] + ] + attack_table = Table(attack_data, colWidths=[150, 350]) + attack_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#4A6F9E")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#E8EDF4")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), + ])) + elements.append(attack_table) + + # Результаты атак + results = scanner_service.get_attack_results_by_attack(attack.id) + if results: + elements.append(Paragraph("Результаты:", heading3_style)) + result_data = [[ + Paragraph("ID", table_header_style), + Paragraph("Сводка", table_header_style) + ]] + + for result in results: + result_data.append([ + Paragraph(str(result.id), table_cell_style), + Paragraph(result.summary or "Нет", table_cell_style) + ]) + + result_table = Table(result_data, colWidths=[50, 450]) + result_table.setStyle(TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#5D7CA6")), + ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor("#F0F5FC")), + ('GRID', (0, 0), (-1, -1), 1, colors.black), + ])) + elements.append(result_table) + + elements.append(Spacer(1, 24)) + + # Создание PDF + doc.build(elements) + logger.info(f"PDF отчет успешно создан: {output_path}") + except Exception as e: + logger.error(f"Ошибка при создании PDF отчета: {e}") + raise + +def generate_json_report(scanner_service: ScannerManager, session_id: int) -> str: + """Генерация JSON отчета для указанной сессии""" + try: + session = scanner_service.get_session_by_id(session_id) + if not session: + logger.warning(f"Сессия с ID {session_id} не найдена") + return json.dumps({"error": "Сессия не найдена"}) + + scans = scanner_service.get_scans_by_session(session_id) + scans_data = [] + for scan in scans: + hosts = scanner_service.get_hosts_by_scan(scan.id) + hosts_data = [] + for host in hosts: + ports = scanner_service.get_ports_by_host(host.id) + ports_data = [] + for port in ports: + services = scanner_service.get_services_by_port(port.id) + services_data = [] + for service in services: + cpes = scanner_service.get_cpes_by_service(service.id) + cpes_data = [{"id": cpe.id, "name": cpe.name} for cpe in cpes] + services_data.append({ + "id": service.id, + "name": service.name, + "product": service.product, + "extrainfo": service.extrainfo, + "ostype": service.ostype, + "cpes": cpes_data + }) + ports_data.append({ + "id": port.id, + "protocol": port.protocol, + "port_num": port.port_num, + "state": port.state, + "services": services_data + }) + hosts_data.append({ + "id": host.id, + "ip": host.ip, + "mac": host.mac, + "os": host.os, + "ports": ports_data + }) + modbus_result = scanner_service.get_modbus_result(scan.id) + modbus_data = { + "active_coils": modbus_result.active_coils if modbus_result else "", + "active_discrete_inputs": modbus_result.active_discrete_inputs if modbus_result else "", + "active_holding_registers": modbus_result.active_holding_registers if modbus_result else "", + "active_input_registers": modbus_result.active_input_registers if modbus_result else "" + } if modbus_result else None + scans_data.append({ + "id": scan.id, + "tool": scan.tool, + "args": scan.args, + "summary": scan.summary, + "duration": scan.duration, + "created_at": scan.created_at, + "hosts": hosts_data, + "modbus_result": modbus_data + }) + + attacks = scanner_service.get_attacks_by_session(session_id) + attacks_data = [] + for attack in attacks: + results = scanner_service.get_attack_results_by_attack(attack.id) + results_data = [{"id": result.id, "summary": result.summary} for result in results] + attacks_data.append({ + "id": attack.id, + "tool": attack.tool, + "args": attack.args, + "summary": attack.summary, + "duration": attack.duration, + "created_at": attack.created_at, + "results": results_data + }) + + report_data = { + "session": { + "id": session.id, + "name": session.name, + "created_at": session.created_at + }, + "scans": scans_data, + "attacks": attacks_data + } + + logger.info(f"JSON отчет успешно сгенерирован для сессии {session_id}") + return json.dumps(report_data, indent=4, ensure_ascii=False) + except Exception as e: + logger.error(f"Ошибка при генерации JSON отчета: {e}") + return json.dumps({"error": str(e)}) \ No newline at end of file diff --git a/src/ui/configuration_window.py b/src/ui/configuration_window.py index 5bec901..22ca909 100644 --- a/src/ui/configuration_window.py +++ b/src/ui/configuration_window.py @@ -49,7 +49,7 @@ def create_configuration_window(db, session: Session): dpg.add_button( label="Генерация отчета", - callback=lambda: logger.info("Reporting selected"), + callback=lambda: show_report_window(db, session), width=-1, height=50 ) @@ -110,4 +110,15 @@ def show_attack_window(db, session): # Создать новую контентную область # with dpg.child_window(parent="config_window", tag="content_area", width=-1, height=-1): from src.ui.attack_window import create_attack_window - create_attack_window(db, session, parent="content_area") \ No newline at end of file + create_attack_window(db, session, parent="content_area") + +def show_report_window(db, session): + """Показать окно эксплуатации""" + # Очистить контентную область + if dpg.does_item_exist("content_area"): + dpg.delete_item("content_area", children_only=True) + + # Создать новую контентную область + # with dpg.child_window(parent="config_window", tag="content_area", width=-1, height=-1): + from src.ui.report_window import create_report_window + create_report_window(db, session, parent="content_area") \ No newline at end of file diff --git a/src/ui/report_window.py b/src/ui/report_window.py new file mode 100644 index 0000000..40ba4ca --- /dev/null +++ b/src/ui/report_window.py @@ -0,0 +1,103 @@ +import dearpygui.dearpygui as dpg +from src.core.models.models import Session +from src.core.database.managers.scanner_manager import ScannerManager +from src.core.services.reports import generate_json_report, generate_pdf_report +from src.utils.logger import get_logger + +logger = get_logger("report_window") + +def create_report_window(db, session: Session, parent=None): + """Окно генерации отчета (с поддержкой родительского контейнера)""" + if parent: + with dpg.child_window( + parent=parent, + tag="report_window", + width=-1, + height=-1 + ): + build_report_content(db, session) + else: + with dpg.child_window( + tag="report_window", + width=-1, + height=-1 + ): + build_report_content(db, session) + +def build_report_content(db, session): + """Окно генерации отчетов для выбранной сессии""" + scanner_service = ScannerManager(db) + + dpg.add_text(f"Сессия: {session.name}") + dpg.add_spacer(height=10) + dpg.add_button( + label="Генерировать JSON отчет", + callback=lambda: generate_json_report_callback(scanner_service, session.id), + width=400, + height=80 + ) + dpg.add_spacer(height=10) + dpg.add_button( + label="Генерировать PDF отчет", + callback=lambda: generate_pdf_report_callback(scanner_service, session.id), + width=400, + height=80 + ) + dpg.add_spacer(height=10) + dpg.add_text("", tag="report_status") + +def generate_json_report_callback(scanner_service, session_id): + """Обработчик для генерации JSON отчета""" + # Создаем диалог выбора файла + dpg.add_file_dialog( + directory_selector=False, + default_path="/home/lodqa/", + width = 800, + height = 400, + modal=True, + callback=lambda s, a: save_json_report(scanner_service, session_id, a['file_path_name']), + cancel_callback=lambda: dpg.set_value("report_status", "Выбор пути отменен"), + tag="json_file_dialog", + show=True + ) + +def save_json_report(scanner_service, session_id, file_path): + """Сохранение JSON отчета""" + try: + report = generate_json_report(scanner_service, session_id) + with open(file_path, 'w', encoding='utf-8') as f: + f.write(report) + logger.info(f"JSON отчет сохранен в {file_path}") + dpg.set_value("report_status", f"JSON отчет сохранен в {file_path}") + dpg.delete_item("json_file_dialog") # Удаляем диалог после использования + except Exception as e: + logger.error(f"Ошибка при сохранении JSON отчета: {e}") + dpg.set_value("report_status", f"Ошибка: {str(e)}") + dpg.delete_item("json_file_dialog") # Удаляем диалог при ошибке + +def generate_pdf_report_callback(scanner_service, session_id): + """Обработчик для генерации PDF отчета""" + # Создаем диалог выбора файла + dpg.add_file_dialog( + directory_selector=False, + default_path="/home/lodqa/", + width = 800, + height = 400, + modal=True, + callback=lambda s, a: save_pdf_report(scanner_service, session_id, a['file_path_name']), + cancel_callback=lambda: dpg.set_value("report_status", "Выбор пути отменен"), + tag="pdf_file_dialog", + show=True + ) + +def save_pdf_report(scanner_service, session_id, file_path): + """Сохранение PDF отчета""" + try: + generate_pdf_report(scanner_service, session_id, file_path) + logger.info(f"PDF отчет сохранен в {file_path}") + dpg.set_value("report_status", f"PDF отчет сохранен в {file_path}") + dpg.delete_item("pdf_file_dialog") # Удаляем диалог после использования + except Exception as e: + logger.error(f"Ошибка при сохранении PDF отчета: {e}") + dpg.set_value("report_status", f"Ошибка: {str(e)}") + dpg.delete_item("pdf_file_dialog") # Удаляем диалог при ошибке \ No newline at end of file diff --git a/src/ui/scanner_window.py b/src/ui/scanner_window.py index 24a29b6..12a0140 100644 --- a/src/ui/scanner_window.py +++ b/src/ui/scanner_window.py @@ -244,8 +244,8 @@ def create_openvas_tab(scanner_service, session): # Группа для настроек подключения with dpg.group(tag="openvas_connection_group"): dpg.add_text("Настройки подключения к OpenVAS") - dpg.add_text("Сокет (например, /var/run/gvmd.sock):") - socket_input = dpg.add_input_text(tag="openvas_socket", default_value="/var/run/gvmd.sock", width=300) + dpg.add_text("IP-адрес Greenbone:") + host_input = dpg.add_input_text(tag="openvas_socket", default_value="127.0.0.1", width=300) dpg.add_text("Имя пользователя:") username_input = dpg.add_input_text(tag="openvas_username", width=300) dpg.add_text("Пароль:") @@ -253,7 +253,7 @@ def create_openvas_tab(scanner_service, session): dpg.add_button( label="Подключиться", callback=lambda: connect_to_openvas( - dpg.get_value(socket_input), + dpg.get_value(host_input), dpg.get_value(username_input), dpg.get_value(password_input) ), @@ -295,11 +295,11 @@ def create_openvas_tab(scanner_service, session): readonly=True ) -def connect_to_openvas(socket_path, username, password): +def connect_to_openvas(host, username, password): """Подключение к OpenVAS""" global openvas_scanner try: - openvas_scanner = OpenvasScanner(socket_path=socket_path) + openvas_scanner = OpenvasScanner(host=host) openvas_scanner.connect(username, password) configs = openvas_scanner.get_configurations() dpg.configure_item("openvas_config", items=configs) diff --git a/src/utils/window_utils.py b/src/utils/window_utils.py deleted file mode 100644 index b436aba..0000000 --- a/src/utils/window_utils.py +++ /dev/null @@ -1,42 +0,0 @@ -# src/utils/window_utils.py -from PyQt5.QtWidgets import QDialog, QMessageBox, QInputDialog -from PyQt5.QtCore import Qt - -def show_floating_dialog(parent, title, message, icon=QMessageBox.Information): - """Показывает диалоговое окно как плавающее поверх всех окон""" - dialog = QMessageBox(parent) - dialog.setWindowTitle(title) - dialog.setText(message) - dialog.setIcon(icon) - - # Устанавливаем флаги для плавающего окна - dialog.setWindowFlags( - Qt.Window | - Qt.CustomizeWindowHint | - Qt.WindowTitleHint | - Qt.WindowStaysOnTopHint - ) - - # Отключаем тайлинг для этого окна - dialog.setProperty("hyprland_floating", True) - - return dialog.exec_() - -def create_floating_input(parent, title, label): - """Создает плавающее окно ввода""" - dialog = QInputDialog(parent) - dialog.setWindowTitle(title) - dialog.setLabelText(label) - - # Устанавливаем флаги для плавающего окна - dialog.setWindowFlags( - Qt.Window | - Qt.CustomizeWindowHint | - Qt.WindowTitleHint | - Qt.WindowStaysOnTopHint - ) - - # Отключаем тайлинг для этого окна - dialog.setProperty("hyprland_floating", True) - - return dialog \ No newline at end of file