feat: перенос вкладки «Безопасность» из DokoGen 187-ФЗ

- security_tab_187_reference.py — код вкладки (секция построения +
  методы-обработчики + ИИ-проверка) для переноса интерфейса
- словари: attacker_categories, incidents, criteria_indicators,
  org_measures, tech_measures, threats, threats_fstec (+дата актуализации)
- fstec_threats.py — скачивание перечня угроз с bdu.fstec.ru
- verify.py: check_security_ai — ИИ-проверка безопасности по 236 приказу
This commit is contained in:
LocoAgent
2026-08-04 17:36:02 +04:00
parent de522360b8
commit baa8f095d0
11 changed files with 953 additions and 0 deletions
@@ -0,0 +1,5 @@
[
"внешний нарушитель",
"внутренний нарушитель",
"внешний и внутренний нарушитель"
]
@@ -0,0 +1,7 @@
[
"Социальная значимость — количество граждан, на которые может повлиять нарушение",
"Политическая значимость — международный или внутриполитический резонанс",
"Экономическая значимость — возможный размер ущерба (в рублях)",
"Экологическая значимость — возможный ущерб окружающей среде",
"Значимость для обороны страны, безопасности государства и правопорядка"
]
+8
View File
@@ -0,0 +1,8 @@
[
"отказ в обслуживании",
"несанкционированный доступ",
"утечка данных (нарушение конфиденциальности)",
"модификация (подмена) данных",
"нарушение функционирования технических средств",
"несанкционированное использование вычислительных ресурсов объекта"
]
+9
View File
@@ -0,0 +1,9 @@
[
"Установление контролируемой зоны",
"Контроль физического доступа к объекту",
"Разработка документов (регламентов, инструкций, руководств) по обеспечению безопасности объекта",
"Назначение ответственных за обеспечение безопасности объекта",
"Обучение и инструктаж персонала",
"Регламентация действий при компьютерных инцидентах",
"Утверждение перечня лиц, имеющих доступ к объекту"
]
+14
View File
@@ -0,0 +1,14 @@
[
"Идентификация и аутентификация пользователей",
"Управление доступом",
"Ограничение программной среды",
"Антивирусная защита",
"Защита информации от несанкционированного доступа (СЗИ от НСД)",
"Регистрация и мониторинг событий безопасности",
"Межсетевое экранирование",
"Обнаружение вторжений (СОВ)",
"Криптографическая защита информации (СКЗИ)",
"Защита среды виртуализации",
"Резервное копирование и восстановление",
"Защита технических средств и линий связи"
]
+12
View File
@@ -0,0 +1,12 @@
[
"Угроза несанкционированного доступа к информационным ресурсам объекта",
"Угроза нарушения функционирования объекта (отказ в обслуживании)",
"Угроза утечки (нарушения конфиденциальности) информации",
"Угроза модификации (подмены) данных",
"Угроза внедрения вредоносного кода",
"Угроза несанкционированного использования вычислительных ресурсов",
"Угроза перехвата (прослушивания) сетевого трафика",
"Угроза социальной инженерии",
"Угроза использования уязвимостей программного обеспечения",
"Угроза физического воздействия на технические средства"
]
+229
View File
@@ -0,0 +1,229 @@
[
"Угроза автоматического распространения вредоносного кода в грид-системе",
"Угроза агрегирования данных, передаваемых в грид-системе",
"Угроза использования слабостей криптографических алгоритмов и уязвимостей в программном обеспечении их реализации",
"Угроза аппаратного сброса пароля BIOS",
"Угроза внедрения вредоносного кода в BIOS",
"Угроза внедрения кода или данных",
"Угроза воздействия на программы с высокими привилегиями",
"Угроза восстановления и/или повторного использования аутентификационной информации",
"Угроза восстановления предыдущей уязвимой версии BIOS",
"Угроза выхода процесса за пределы виртуальной машины",
"Угроза деавторизации санкционированного клиента беспроводной сети",
"Угроза деструктивного изменения конфигурации/среды окружения программ",
"Угроза деструктивного использования декларированного функционала BIOS",
"Угроза длительного удержания вычислительных ресурсов пользователями",
"Угроза доступа к защищаемым файлам с использованием обходного пути",
"Угроза доступа к локальным файлам сервера при помощи URL",
"Угроза доступа/перехвата/изменения HTTP cookies",
"Угроза загрузки нештатной операционной системы",
"Угроза заражения DNS-кеша",
"Угроза злоупотребления возможностями, предоставленными потребителям облачных услуг",
"Угроза злоупотребления доверием потребителей облачных услуг",
"Угроза избыточного выделения оперативной памяти",
"Угроза изменения компонентов информационной (автоматизированной) системы",
"Угроза изменения режимов работы аппаратных элементов компьютера",
"Угроза изменения системных и глобальных переменных",
"Угроза искажения XML-схемы",
"Угроза искажения вводимой и выводимой на периферийные устройства информации",
"Угроза использования альтернативных путей доступа к ресурсам",
"Угроза использования вычислительных ресурсов суперкомпьютера «паразитными» процессами",
"Угроза использования информации идентификации/аутентификации, заданной по умолчанию",
"Угроза использования механизмов авторизации для повышения привилегий",
"Угроза использования поддельных цифровых подписей BIOS",
"Угроза использования слабостей кодирования входных данных",
"Угроза использования слабостей протоколов сетевого/локального обмена данными",
"Угроза использования слабых криптографических алгоритмов BIOS",
"Угроза исследования механизмов работы программы",
"Угроза исследования приложения через отчёты об ошибках",
"Угроза исчерпания вычислительных ресурсов хранилища больших данных",
"Угроза исчерпания запаса ключей, необходимых для обновления BIOS",
"Угроза конфликта юрисдикций различных стран",
"Угроза межсайтового скриптинга",
"Угроза межсайтовой подделки запроса",
"Угроза нарушения доступности облачного сервера",
"Угроза нарушения изоляции пользовательских данных внутри виртуальной машины",
"Угроза нарушения изоляции среды исполнения BIOS",
"Угроза нарушения процедуры аутентификации субъектов виртуального информационного взаимодействия",
"Угроза нарушения работоспособности грид-системы при нетипичной сетевой нагрузке",
"Угроза нарушения технологии обработки информации путём несанкционированного внесения изменений в образы виртуальных машин",
"Угроза нарушения целостности данных кеша",
"Угроза неверного определения формата входных данных, поступающих в хранилище больших данных",
"Угроза невозможности восстановления сессии работы на ПЭВМ при выводе из промежуточных состояний питания",
"Угроза невозможности миграции образов виртуальных машин из-за несовместимости аппаратного и программного обеспечения",
"Угроза невозможности управления правами пользователей BIOS",
"Угроза недобросовестного исполнения обязательств поставщиками облачных услуг",
"Угроза незащищённого администрирования облачных услуг",
"Угроза некачественного переноса инфраструктуры в облако",
"Угроза неконтролируемого копирования данных внутри хранилища больших данных",
"Угроза неконтролируемого роста числа виртуальных машин",
"Угроза неконтролируемого роста числа зарезервированных вычислительных ресурсов",
"Угроза неконтролируемого уничтожения информации хранилищем больших данных",
"Угроза некорректного задания структуры данных транзакции",
"Угроза некорректного использования прозрачного прокси-сервера за счёт плагинов браузера",
"Угроза некорректного использования функционала программного и аппаратного обеспечения",
"Угроза некорректной реализации политики лицензирования в облаке",
"Угроза неопределённости в распределении ответственности между ролями в облаке",
"Угроза неопределённости ответственности за обеспечение безопасности облака",
"Угроза неправомерного ознакомления с защищаемой информацией",
"Угроза неправомерного/некорректного использования интерфейса взаимодействия с приложением",
"Угроза неправомерных действий в каналах связи",
"Угроза непрерывной модернизации облачной инфраструктуры",
"Угроза несанкционированного восстановления удалённой защищаемой информации",
"Угроза несанкционированного выключения или обхода механизма защиты от записи в BIOS",
"Угроза несанкционированного доступа к активному и (или) пассивному виртуальному и (или) физическому сетевому оборудованию из физической и (или) виртуальной сети",
"Угроза несанкционированного доступа к аутентификационной информации",
"Угроза несанкционированного доступа к виртуальным каналам передачи",
"Угроза несанкционированного доступа к гипервизору из виртуальной машины и (или) физической сети",
"Угроза несанкционированного доступа к данным за пределами зарезервированного адресного пространства, в том числе выделенного под виртуальное аппаратное обеспечение",
"Угроза несанкционированного доступа к защищаемым виртуальным машинам из виртуальной и (или) физической сети",
"Угроза несанкционированного доступа к защищаемым виртуальным машинам со стороны других виртуальных машин",
"Угроза несанкционированного доступа к защищаемым виртуальным устройствам из виртуальной и (или) физической сети",
"Угроза несанкционированного доступа к локальному компьютеру через клиента грид-системы",
"Угроза несанкционированного доступа к сегментам вычислительного поля",
"Угроза несанкционированного доступа к системе по беспроводным каналам",
"Угроза несанкционированного доступа к системе хранения данных из виртуальной и (или) физической сети",
"Угроза несанкционированного доступа к хранимой в виртуальном пространстве защищаемой информации",
"Угроза несанкционированного изменения аутентификационной информации",
"Угроза несанкционированного использования привилегированных функций BIOS",
"Угроза несанкционированного копирования защищаемой информации",
"Угроза несанкционированного редактирования реестра",
"Угроза несанкционированного создания учётной записи пользователя",
"Угроза несанкционированного удаления защищаемой информации",
"Угроза несанкционированного удалённого внеполосного доступа к аппаратным средствам",
"Угроза несанкционированного управления буфером",
"Угроза несанкционированного управления синхронизацией и состоянием",
"Угроза несанкционированного управления указателями",
"Угроза несогласованности политик безопасности элементов облачной инфраструктуры",
"Угроза несогласованности правил доступа к большим данным",
"Угроза обнаружения открытых портов и идентификации привязанных к ним сетевых служб",
"Угроза обнаружения хостов",
"Угроза обхода некорректно настроенных механизмов аутентификации",
"Угроза общедоступности облачной инфраструктуры",
"Угроза опосредованного управления группой программ через совместно используемые данные",
"Угроза определения типов объектов защиты",
"Угроза определения топологии вычислительной сети",
"Угроза отказа в загрузке входных данных неизвестного формата хранилищем больших данных",
"Угроза отказа в обслуживании системой хранения данных суперкомпьютера",
"Угроза отключения контрольных датчиков",
"Угроза ошибки обновления гипервизора",
"Угроза перебора всех настроек и параметров приложения",
"Угроза перегрузки грид-системы вычислительными заданиями",
"Угроза передачи данных по скрытым каналам",
"Угроза передачи запрещённых команд на оборудование с числовым программным управлением",
"Угроза перезагрузки аппаратных и программно-аппаратных средств вычислительной техники",
"Угроза переполнения целочисленных переменных",
"Угроза перехвата вводимой и выводимой на периферийные устройства информации",
"Угроза перехвата данных, передаваемых по вычислительной сети",
"Угроза перехвата привилегированного потока",
"Угроза перехвата привилегированного процесса",
"Угроза перехвата управления гипервизором",
"Угроза перехвата управления средой виртуализации",
"Угроза повреждения системного реестра",
"Угроза повышения привилегий",
"Угроза подбора пароля BIOS",
"Угроза подделки записей журнала регистрации событий",
"Угроза подключения к беспроводной сети в обход процедуры аутентификации",
"Угроза подмены беспроводного клиента или точки доступа",
"Угроза подмены действия пользователя путём обмана",
"Угроза подмены доверенного пользователя",
"Угроза подмены резервной копии программного обеспечения BIOS",
"Угроза подмены содержимого сетевых ресурсов",
"Угроза подмены субъекта сетевого доступа",
"Угроза получения предварительной информации об объекте защиты",
"Угроза получения сведений о владельце беспроводного устройства",
"Угроза потери доверия к поставщику облачных услуг",
"Угроза потери и утечки данных, обрабатываемых в облаке",
"Угроза потери информации вследствие несогласованности работы узлов хранилища больших данных",
"Угроза потери управления облачными ресурсами",
"Угроза потери управления собственной инфраструктурой при переносе её в облако",
"Угроза преодоления физической защиты",
"Угроза приведения системы в состояние «отказ в обслуживании»",
"Угроза привязки к поставщику облачных услуг",
"Угроза приостановки оказания облачных услуг вследствие технических сбоев",
"Угроза программного выведения из строя средств хранения, обработки и (или) ввода/вывода/передачи информации",
"Угроза программного сброса пароля BIOS",
"Угроза пропуска проверки целостности программного обеспечения",
"Угроза прямого обращения к памяти вычислительного поля суперкомпьютера",
"Угроза распространения несанкционированно повышенных прав на всю грид-систему",
"Угроза сбоя автоматического управления системой разграничения доступа хранилища больших данных",
"Угроза сбоя обработки специальным образом изменённых файлов",
"Угроза сбоя процесса обновления BIOS",
"Угроза сканирования веб-сервисов, разработанных на основе языка описания WSDL",
"Угроза удаления аутентификационной информации",
"Угроза усиления воздействия на вычислительные ресурсы пользователей при помощи сторонних серверов",
"Угроза установки уязвимых версий обновления программного обеспечения BIOS",
"Угроза утраты вычислительных ресурсов",
"Угроза утраты носителей информации",
"Угроза физического выведения из строя средств хранения, обработки и (или) ввода/вывода/передачи информации",
"Угроза форматирования носителей информации",
"Угроза «форсированного веб-браузинга»",
"Угроза хищения средств хранения, обработки и (или) ввода/вывода/передачи информации",
"Угроза чрезмерного использования вычислительных ресурсов суперкомпьютера в ходе интенсивного обмена межпроцессорными сообщениями",
"Угроза эксплуатации цифровой подписи программного кода",
"Угроза перехвата исключения/сигнала из привилегированного блока функций",
"Угроза распространения состояния «отказ в обслуживании» в облачной инфраструктуре",
"Угроза включения в проект не достоверно испытанных компонентов",
"Угроза внедрения системной избыточности",
"Угроза заражения компьютера при посещении неблагонадёжных сайтов",
"Угроза «кражи» учётной записи доступа к сетевым сервисам",
"Угроза наличия механизмов разработчика",
"Угроза неправомерного шифрования информации",
"Угроза скрытного включения вычислительного устройства в состав бот-сети",
"Угроза распространения «почтовых червей»",
"Угроза «спама» веб-сервера",
"Угроза «фарминга»",
"Угроза «фишинга»",
"Угроза нарушения технологического/производственного процесса из-за временны́х задержек, вносимых средством защиты",
"Угроза неподтверждённого ввода данных оператором в систему, связанную с безопасностью",
"Угроза несанкционированного использования системных и сетевых утилит",
"Угроза несанкционированной модификации защищаемой информации",
"Угроза отказа подсистемы обеспечения температурного режима",
"Угроза перехвата одноразовых паролей в режиме реального времени",
"Угроза физического устаревания аппаратных компонентов",
"Угроза перехвата управления автоматизированной системой управления технологическими процессами",
"Угроза агрегирования данных, обрабатываемых с помощью мобильного устройства",
"Угроза несанкционированного изменения параметров настройки средств защиты информации",
"Угроза внедрения вредоносного кода через рекламу, сервисы и контент",
"Угроза несанкционированного воздействия на средство защиты информации",
"Угроза подмены программного обеспечения",
"Угроза маскирования действий вредоносного кода",
"Угроза внедрения вредоносного кода за счет посещения зараженных сайтов в сети Интернет",
"Угроза внедрения вредоносного кода в дистрибутив программного обеспечения",
"Угроза использования уязвимых версий программного обеспечения",
"Угроза утечки информации за счет применения вредоносным программным обеспечением алгоритмов шифрования трафика",
"Угроза несанкционированного использования привилегированных функций мобильного устройства",
"Угроза удаленного запуска вредоносного кода в обход механизмов защиты операционной системы",
"Угроза контроля вредоносной программой списка приложений, запущенных на мобильном устройстве",
"Угроза хищения аутентификационной информации из временных файлов cookie",
"Угроза скрытной регистрации вредоносной программой учетных записей администраторов",
"Угроза перехвата управления мобильного устройства при использовании виртуальных голосовых ассистентов",
"Угроза хищения информации с мобильного устройства при использовании виртуальных голосовых ассистентов",
"Угроза утечки пользовательских данных при использовании функций автоматического заполнения аутентификационной информации в браузере",
"Угроза несанкционированной установки приложений на мобильные устройства",
"Угроза утечки информации с неподключенных к сети Интернет компьютеров",
"Угроза несанкционированного изменения вредоносной программой значений параметров программируемых логических контроллеров",
"Угроза нарушения работы компьютера и блокирования доступа к его данным из-за некорректной работы установленных на нем средств защиты",
"Угроза отказа в работе оборудования из-за изменения геолокационной информации о нем",
"Угроза несанкционированного доступа к параметрам настройки оборудования за счет использования «мастер-кодов» (инженерных паролей)",
"Угроза нецелевого использования вычислительных ресурсов средства вычислительной техники",
"Угроза несанкционированного доступа к защищаемой памяти \r\nядра процессора",
"Угроза нарушения работы информационной системы, вызванного обновлением используемого в ней программного обеспечения",
"Угроза использования непроверенных пользовательских данных при формировании конфигурационного файла, используемого программным обеспечением администрирования информационных систем",
"Угроза перехвата управления информационной системой",
"Угроза обхода многофакторной аутентификации",
"Угроза несвоевременного выявления и реагирования компонентами информационной (автоматизированной) системы (в том числе средствами защиты информации) на события безопасности информации",
"Угроза несанкционированного доступа к системе при помощи сторонних сервисов",
"Угроза получения несанкционированного доступа к приложениям, установленным на Smart-картах",
"Угроза использования скомпрометированного доверенного источника обновлений программного обеспечения",
"Угроза раскрытия информации о модели машинного обучения",
"Угроза хищения обучающих данных",
"Угроза нарушения функционирования («обхода») средств, реализующих технологии искусственного интеллекта",
"Угроза модификации модели машинного обучения путем искажения («отравления») обучающих данных",
"Угроза подмены модели машинного обучения",
"Угроза несанкционированного доступа к контейнерам, предоставляющего пользователям расширенные привилегии",
"Угроза нарушения целостности (подмены) контейнеров",
"Угроза нарушения изоляции контейнеров",
"Угроза внедрения вредоносного программного обеспечения в контейнеры",
"Угроза модификации (подмены) образов контейнеров"
]
@@ -0,0 +1 @@
2026-08-04
+149
View File
@@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
"""DokoGen — загрузка перечня угроз безопасности информации с сайта ФСТЭК (БДУ).
Скачивает https://bdu.fstec.ru/threat (таблица угроз, с пагинацией),
парсит названия угроз и сохраняет в dictionaries/threats_fstec.json.
Рядом хранится дата актуализации (threats_fstec_date.txt).
"""
import os
import re
import json
import ssl
import urllib.request
import urllib.parse
from datetime import datetime
BDU_URL = 'https://bdu.fstec.ru/threat'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
def _ssl_ctx():
"""SSL-контекст: сначала обычная проверка, при проблемах — без проверки."""
try:
ctx = ssl.create_default_context()
return ctx
except Exception:
pass
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
return ctx
def _fetch(url, timeout=30):
"""GET с User-Agent, возвращает текст или None."""
req = urllib.request.Request(url, headers={'User-Agent': USER_AGENT})
try:
with urllib.request.urlopen(req, timeout=timeout, context=_ssl_ctx()) as resp:
return resp.read().decode('utf-8', errors='ignore')
except Exception:
# повтор с отключённой проверкой сертификата (некоторые сети/прокси)
try:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
return resp.read().decode('utf-8', errors='ignore')
except Exception:
return None
def _parse_threats(html):
"""Извлекает названия угроз из HTML-таблицы БДУ.
Строки вида: <tr>...<td>УБИ. 001</td><td>Угроза ...</td>...</tr>
Возвращает список названий (без кода).
"""
names = []
if not html:
return names
for row in re.findall(r'<tr[^>]*>(.*?)</tr>', html, re.S):
cells = re.findall(r'<t[dh][^>]*>(.*?)</t[dh]>', row, re.S)
cleaned = [re.sub(r'<[^>]+>', '', c).strip() for c in cells]
# формат: [код, название, ...] — берём ячейку, которая не похожа на код УБИ
for cell in cleaned:
if not cell:
continue
if re.fullmatch(r'УБИ\.?\s*[\d.]+', cell):
continue
if cell in ('', 'Идентификатор', 'Наименование', 'Дата', 'Уровень опасности'):
continue
names.append(cell)
break
return names
def download_fstec_threats(dict_dir=None, max_pages=8, page_size=100):
"""Скачивает перечень угроз с bdu.fstec.ru.
Возвращает (ok, сообщение, список_угроз). При успехе сохраняет:
- dictionaries/threats_fstec.json — список названий угроз
- dictionaries/threats_fstec_date.txt — дата актуализации (ГГГГ-ММ-ДД)
"""
if dict_dir is None:
dict_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries')
threats = []
for page in range(1, max_pages + 1):
url = f"{BDU_URL}?size={page_size}&page={page}"
html = _fetch(url)
if not html:
break
names = _parse_threats(html)
if not names:
break
for n in names:
if n not in threats:
threats.append(n)
# если на странице меньше page_size — это последняя
if len(names) < page_size:
break
if not threats:
return False, "Не удалось загрузить угрозы с сайта ФСТЭК (bdu.fstec.ru)", []
try:
os.makedirs(dict_dir, exist_ok=True)
with open(os.path.join(dict_dir, 'threats_fstec.json'), 'w', encoding='utf-8') as f:
json.dump(threats, f, ensure_ascii=False, indent=2)
today = datetime.now().strftime('%Y-%m-%d')
with open(os.path.join(dict_dir, 'threats_fstec_date.txt'), 'w', encoding='utf-8') as f:
f.write(today)
except Exception as e:
return False, f"Ошибка сохранения: {e}", threats
return True, f"Загружено угроз: {len(threats)}", threats
def get_threats_date(dict_dir=None):
"""Дата последней актуализации списка угроз (или None)."""
if dict_dir is None:
dict_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries')
path = os.path.join(dict_dir, 'threats_fstec_date.txt')
if os.path.exists(path):
try:
with open(path, encoding='utf-8') as f:
return f.read().strip()
except Exception:
pass
return None
def load_threats(dict_dir=None):
"""Загружает список угроз: сначала скачанный с ФСТЭК, иначе базовый threats.json."""
if dict_dir is None:
dict_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries')
fstec = os.path.join(dict_dir, 'threats_fstec.json')
if os.path.exists(fstec):
try:
with open(fstec, encoding='utf-8') as f:
return json.load(f)
except Exception:
pass
base = os.path.join(dict_dir, 'threats.json')
try:
with open(base, encoding='utf-8') as f:
return json.load(f)
except Exception:
return []
+480
View File
@@ -0,0 +1,480 @@
# -*- coding: utf-8 -*-
"""
ВКЛАДКА «БЕЗОПАСНОСТЬ» из DokoGen 187-ФЗ (dokogen187, ветка source)
=====================================================================
Скопировано для переноса в DokoGen MU, чтобы интерфейс был одинаковым.
ЧТО ВХОДИТ:
1. Секция построения вкладки (вставить в метод _build_is_tab перед вкладкой «Средства защиты»)
2. Методы-обработчики (вставить в класс приложения)
3. Словари — скопированы в dokogen/dictionaries/:
attacker_categories.json, incidents.json, criteria_indicators.json,
org_measures.json, tech_measures.json, threats.json,
threats_fstec.json, threats_fstec_date.txt
4. Модуль fstec_threats.py (скачивание перечня угроз с bdu.fstec.ru)
5. check_security_ai() — ИИ-проверка (см. конец файла, добавить в verify.py)
ЗАВИСИМОСТИ:
- tkinter/ttk (стандарт)
- dokogen/fstec_threats.py (загрузка угроз ФСТЭК)
- dokogen/verify.py: check_security_ai (ИИ-проверка)
- dokogen/api_settings.py: load_api_settings
- Поля модели InformationSystem (добавить в models.py, если нет):
attacker_category, attacker_characteristics, threats, incidents,
criteria_values, criteria_justification, org_measures, tech_measures
- Маппинги _collect_is/_on_is_select (см. комментарии в конце)
ПРИМЕЧАНИЕ: в DokoGen MU (152-ФЗ) модель InformationSystem другая —
при переносе адаптировать имена полей или добавить новые.
"""
# ============================================================
# 1. СЕКЦИЯ ПОСТРОЕНИЯ ВКЛАДКИ (вставить в _build_is_tab)
# ============================================================
# ===== Вкладка «Безопасность» =====
sec_tab = ttk.Frame(nb2)
nb2.add(sec_tab, text="Безопасность")
sec_frame = ttk.Frame(sec_tab, padding=8)
sec_frame.pack(fill=tk.BOTH, expand=True)
# --- Категория нарушителя (словарик) ---
ttk.Label(sec_frame, text="Категория нарушителя:").grid(row=0, column=0, sticky=tk.W, pady=2)
self.is_attacker_category_combo = ttk.Combobox(sec_frame, width=68, state='readonly')
self.is_attacker_category_combo.grid(row=0, column=1, padx=8, sticky=tk.EW, pady=1)
try:
import json as _json
_ac = os.path.join(os.path.dirname(__file__), 'dictionaries', 'attacker_categories.json')
with open(_ac, encoding='utf-8') as _f:
self.is_attacker_category_combo['values'] = [''] + _json.load(_f)
except Exception:
pass
# --- Характеристика нарушителя ---
ttk.Label(sec_frame, text="Характеристика нарушителя:").grid(row=1, column=0, sticky=tk.NW, pady=2)
self.is_attacker_characteristics_listbox = tk.Listbox(sec_frame, height=3, exportselection=False)
self.is_attacker_characteristics_listbox.grid(row=1, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_char_btn = ttk.Frame(sec_frame)
sec_char_btn.grid(row=1, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_char_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_attacker_characteristics_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_char_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_attacker_characteristics_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_char_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_attacker_characteristics_listbox)).pack(fill=tk.X, pady=1)
# --- Основные угрозы БИ (словарик + загрузка с ФСТЭК) ---
ttk.Label(sec_frame, text="Основные угрозы БИ:").grid(row=2, column=0, sticky=tk.W, pady=2)
self.is_threats_listbox = tk.Listbox(sec_frame, height=5, exportselection=False)
self.is_threats_listbox.grid(row=2, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_thr_btn = ttk.Frame(sec_frame)
sec_thr_btn.grid(row=2, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_thr_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_threats_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_thr_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_threats_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_thr_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_threats_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_thr_btn, text="📚 Из словаря",
command=lambda: self._select_sw_from_dict(self.is_threats_listbox, 'threats_fstec.json')).pack(fill=tk.X, pady=1)
ttk.Button(sec_thr_btn, text="⬇️ Загрузить угрозы ФСТЭК",
command=self._download_fstec_threats).pack(fill=tk.X, pady=1)
self._threats_date_label = ttk.Label(sec_frame, text="", foreground='gray')
self._threats_date_label.grid(row=3, column=1, sticky=tk.W, padx=8, pady=(0, 2))
# --- Типы компьютерных инцидентов (словарик) ---
ttk.Label(sec_frame, text="Типы компьютерных инцидентов:").grid(row=4, column=0, sticky=tk.W, pady=2)
self.is_incidents_listbox = tk.Listbox(sec_frame, height=4, exportselection=False)
self.is_incidents_listbox.grid(row=4, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_inc_btn = ttk.Frame(sec_frame)
sec_inc_btn.grid(row=4, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_inc_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_incidents_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_inc_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_incidents_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_inc_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_incidents_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_inc_btn, text="📚 Из словаря",
command=lambda: self._select_sw_from_dict(self.is_incidents_listbox, 'incidents.json')).pack(fill=tk.X, pady=1)
# --- Показатели критериев значимости (словарик) ---
ttk.Label(sec_frame, text="Показатели критериев значимости:").grid(row=5, column=0, sticky=tk.W, pady=2)
self.is_criteria_values_listbox = tk.Listbox(sec_frame, height=4, exportselection=False)
self.is_criteria_values_listbox.grid(row=5, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_crit_btn = ttk.Frame(sec_frame)
sec_crit_btn.grid(row=5, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_crit_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_criteria_values_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_crit_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_criteria_values_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_crit_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_criteria_values_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_crit_btn, text="📚 Из словаря",
command=lambda: self._select_sw_from_dict(self.is_criteria_values_listbox, 'criteria_indicators.json')).pack(fill=tk.X, pady=1)
# --- Обоснование значений показателей ---
ttk.Label(sec_frame, text="Обоснование значений показателей:").grid(row=6, column=0, sticky=tk.NW, pady=2)
self.is_criteria_justification_listbox = tk.Listbox(sec_frame, height=3, exportselection=False)
self.is_criteria_justification_listbox.grid(row=6, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_just_btn = ttk.Frame(sec_frame)
sec_just_btn.grid(row=6, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_just_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_criteria_justification_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_just_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_criteria_justification_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_just_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_criteria_justification_listbox)).pack(fill=tk.X, pady=1)
# --- Организационные меры (словарик) ---
ttk.Label(sec_frame, text="Организационные меры:").grid(row=7, column=0, sticky=tk.W, pady=2)
self.is_org_measures_listbox = tk.Listbox(sec_frame, height=3, exportselection=False)
self.is_org_measures_listbox.grid(row=7, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_org_btn = ttk.Frame(sec_frame)
sec_org_btn.grid(row=7, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_org_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_org_measures_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_org_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_org_measures_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_org_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_org_measures_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_org_btn, text="📚 Из словаря",
command=lambda: self._select_sw_from_dict(self.is_org_measures_listbox, 'org_measures.json')).pack(fill=tk.X, pady=1)
# --- Технические меры (словарик) ---
ttk.Label(sec_frame, text="Технические меры:").grid(row=8, column=0, sticky=tk.W, pady=2)
self.is_tech_measures_listbox = tk.Listbox(sec_frame, height=3, exportselection=False)
self.is_tech_measures_listbox.grid(row=8, column=1, sticky=tk.EW, padx=(8, 4), pady=1)
sec_tech_btn = ttk.Frame(sec_frame)
sec_tech_btn.grid(row=8, column=2, sticky=tk.N, padx=(4, 0))
ttk.Button(sec_tech_btn, text="+ Добавить",
command=lambda: self._add_sw_item(self.is_tech_measures_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_tech_btn, text="- Удалить",
command=lambda: self._del_sw_item(self.is_tech_measures_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_tech_btn, text="✏️ Редактировать",
command=lambda: self._edit_sw_item(self.is_tech_measures_listbox)).pack(fill=tk.X, pady=1)
ttk.Button(sec_tech_btn, text="📚 Из словаря",
command=lambda: self._select_sw_from_dict(self.is_tech_measures_listbox, 'tech_measures.json')).pack(fill=tk.X, pady=1)
# --- Кнопка ИИ-проверки ---
ttk.Button(sec_frame, text="🤖 Проверить безопасность через ИИ",
command=self._check_security_ai).grid(
row=9, column=0, columnspan=3, sticky=tk.W, pady=(10, 0))
sec_frame.columnconfigure(1, weight=1)
# ============================================================
# 2. МЕТОДЫ-ОБРАБОТЧИКИ (вставить в класс приложения)
# ============================================================
def _download_fstec_threats(self):
"""Скачать перечень угроз с сайта ФСТЭК (БДУ) и обновить список."""
try:
from .fstec_threats import download_fstec_threats, get_threats_date
except ImportError as e:
messagebox.showerror("Ошибка", f"Модуль fstec_threats не найден: {e}")
return
self.log("⬇️ Загрузка перечня угроз с bdu.fstec.ru…")
ok, msg, threats = download_fstec_threats()
if not ok:
messagebox.showerror("Загрузка угроз",
f"Не удалось загрузить:\n{msg}\n\n"
"Проверьте доступ к интернету или попробуйте позже.")
self.log(f"❌ Загрузка угроз: {msg}")
return
# Обновляем label даты актуализации
self._update_threats_date_label()
# Если список угроз уже открыт — можно показать диалог выбора
messagebox.showinfo("Загрузка угроз",
f"{msg}\n\nСписок сохранён. Теперь нажмите «📚 Из словаря» "
"в разделе «Основные угрозы БИ», чтобы выбрать нужные угрозы.")
self.log(f"{msg}")
def _update_threats_date_label(self):
"""Обновить подпись даты актуализации списка угроз."""
try:
from .fstec_threats import get_threats_date
d = get_threats_date()
except Exception:
d = None
if hasattr(self, '_threats_date_label'):
if d:
self._threats_date_label.config(
text=f"Актуально на: {d}", foreground='#2e7d32')
else:
self._threats_date_label.config(
text="⚠ Список угроз ещё не скачан — нажмите «⬇️ Загрузить угрозы ФСТЭК»",
foreground='#c62828')
def _check_security_ai(self):
"""Проверка вкладки «Безопасность» через ИИ (DeepSeek/OpenAI-совместимый API)."""
if self._current_is_idx is None:
messagebox.showinfo("Безопасность", "Сначала выберите объект КИИ в списке слева")
return
settings = load_api_settings()
if not settings.get('ai_api_key'):
messagebox.showwarning("Настройки", "Не задан API-ключ ИИ.\nМеню «Файл → Настройки»")
return
# Собираем текущие данные безопасности объекта
self._collect_is()
isys = self.model.information_systems[self._current_is_idx]
security_data = {
'name': isys.name or '',
'description': isys.description or '',
'category': isys.category or '',
'sphere': isys.sphere or '',
'object_type': isys.object_type or '',
'architecture': isys.architecture or '',
'attacker_category': isys.attacker_category or '',
'attacker_characteristics': isys.attacker_characteristics or '',
'threats': isys.threats or '',
'incidents': isys.incidents or '',
'criteria_values': isys.criteria_values or '',
'criteria_justification': isys.criteria_justification or '',
'org_measures': isys.org_measures or '',
'tech_measures': isys.tech_measures or '',
}
self.log(f"🤖 Запрос к ИИ ({settings.get('ai_model') or 'deepseek-chat'}): проверка безопасности")
self._verify_log("Проверка безопасности через ИИ…")
def _run():
try:
from .verify import check_security_ai
result, err = check_security_ai(
security_data,
settings['ai_api_key'],
settings.get('ai_model'),
settings.get('ai_base_url'),
)
self.root.after(0, lambda: self._security_ai_done(result, err))
except Exception as e:
self.root.after(0, lambda: self._security_ai_done(None, str(e)))
threading.Thread(target=_run, daemon=True).start()
def _security_ai_done(self, result, err):
"""Обработка ответа ИИ по безопасности."""
if err:
self._verify_log(f"{err}")
self.log(f"❌ Проверка безопасности: {err}")
return
d = result['data']
lines = [f"Источник: {result['source']}"]
lines.append(f"Оценка: {d.get('rating', 'warning')}")
notes = d.get('notes') or []
if notes:
lines.append("\nЗамечания:")
for n in notes:
lines.append(f"{n}")
props = d.get('proposals') or {}
if any(props.values()):
lines.append("\nПредложения ИИ:")
for key, label in [('attacker_category', 'Нарушитель'),
('threats', 'Угрозы'),
('incidents', 'Инциденты'),
('criteria_values', 'Показатели'),
('criteria_justification', 'Обоснование'),
('org_measures', 'Орг. меры'),
('tech_measures', 'Тех. меры')]:
v = props.get(key)
if v:
lines.append(f" {label}: {v}")
self._verify_log('\n'.join(lines))
self.log(f"🤖 ИИ: оценка {d.get('rating', 'warning')}, замечаний: {len(notes)}")
def _refine_dict_item_dialog(self, base_item):
"""Окно уточнения модели и количества для позиции из словаря.
Возвращает полную строку вида «Windows 10 Pro 5 шт.» или
«Сервер Supermicro SSG-2027R -E1R24N 2 шт.» или None.
"""
import re
# Убираем из базы маркер «(уточнить …)»
base = re.sub(r'\s*\([^)]*уточн[^)]*\)', '', base_item, flags=re.I).strip() or base_item
dialog = tk.Toplevel(self.root)
dialog.title("Уточнить модель и количество")
dialog.geometry("500x180")
dialog.transient(self.root)
dialog.grab_set()
frame = ttk.Frame(dialog, padding=10)
frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(frame, text=f"Позиция: {base}", wraplength=470,
font=('Arial', 9, 'bold')).pack(anchor=tk.W, pady=(0, 8))
ttk.Label(frame, text="Модель / версия / конфигурация (необязательно):").pack(anchor=tk.W)
model_entry = ttk.Entry(frame, width=60)
model_entry.pack(fill=tk.X, pady=(2, 6))
model_entry.focus_set()
ttk.Label(frame, text="Количество:").pack(anchor=tk.W)
count_entry = ttk.Entry(frame, width=20)
count_entry.pack(anchor=tk.W, pady=(2, 6))
result = {}
def _ok():
model = model_entry.get().strip()
count = count_entry.get().strip()
text = base
if model:
text = f"{text} {model}"
if count:
text = f"{text} {count} шт."
result['val'] = text
dialog.destroy()
def _cancel():
result['val'] = None
dialog.destroy()
btn_frame = ttk.Frame(frame)
btn_frame.pack(fill=tk.X, pady=(6, 0))
ttk.Button(btn_frame, text="✅ ОК", command=_ok).pack(side=tk.RIGHT, padx=2)
ttk.Button(btn_frame, text="Отмена", command=_cancel).pack(side=tk.RIGHT, padx=2)
dialog.bind('<Return>', lambda e: _ok())
dialog.bind('<Escape>', lambda e: _cancel())
dialog.wait_window()
return result.get('val')
def _select_sw_from_dict(self, listbox, dict_name):
"""Выбор позиций из словаря (JSON) — можно несколько."""
import json
dict_path = os.path.join(os.path.dirname(__file__), 'dictionaries', dict_name)
try:
with open(dict_path, 'r', encoding='utf-8') as f:
items = json.load(f)
title = "Выбор из словаря"
self._select_from_dialog(title, items, listbox)
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь: {e}")
def _sw_dialog(self, title, initial=""):
"""Большое окно для ввода/редактирования позиции ПО."""
dialog = tk.Toplevel(self.root)
dialog.title(title)
dialog.geometry("560x220")
dialog.transient(self.root)
dialog.grab_set()
dialog.resizable(True, True)
frame = ttk.Frame(dialog, padding=10)
frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(frame, text="Текст позиции:").pack(anchor=tk.W)
txt = tk.Text(frame, width=70, height=6, wrap=tk.WORD)
txt.pack(fill=tk.BOTH, expand=True, pady=5)
txt.insert('1.0', initial)
txt.focus_set()
result = {}
def _ok():
result['val'] = txt.get('1.0', 'end-1c').strip()
dialog.destroy()
def _cancel():
result['val'] = None
dialog.destroy()
btn_frame = ttk.Frame(frame)
btn_frame.pack(fill=tk.X)
ttk.Button(btn_frame, text="✅ ОК", command=_ok).pack(side=tk.RIGHT, padx=2)
ttk.Button(btn_frame, text="Отмена", command=_cancel).pack(side=tk.RIGHT, padx=2)
dialog.bind('<Return>', lambda e: _ok())
dialog.bind('<Escape>', lambda e: _cancel())
dialog.wait_window()
return result.get('val')
def _add_sw_item(self, listbox):
"""Добавить строку в список ПО (большое окно)."""
item = self._sw_dialog("Добавить позицию")
if item:
listbox.insert(tk.END, item)
def _del_sw_item(self, listbox):
"""Удалить выбранную строку из списка ПО."""
sel = listbox.curselection()
if sel:
listbox.delete(sel[0])
def _edit_sw_item(self, listbox):
"""Редактировать выбранную строку списка ПО (большое окно)."""
sel = listbox.curselection()
if not sel:
messagebox.showinfo("Редактировать", "Сначала выберите позицию в списке")
return
current = listbox.get(sel[0])
item = self._sw_dialog("Редактировать позицию", initial=current)
if item is not None:
listbox.delete(sel[0])
listbox.insert(sel[0], item)
# ============================================================
# 3. ИИ-ПРОВЕРКА (добавить в verify.py)
# ============================================================
def check_security_ai(security_data, api_key, model='deepseek-chat', base_url=None):
"""ИИ-проверка заполненной вкладки «Безопасность» объекта КИИ (236 приказ).
Возвращает (результат, ошибка): результат — dict с 'оценка', 'замечания', 'предложения'.
"""
if not api_key:
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
system = (
"Ты — эксперт по безопасности критической информационной инфраструктуры (187-ФЗ, "
"приказ ФСТЭК России от 25.12.2017 № 239 «Об утверждении Требований по обеспечению "
"безопасности значимых объектов КИИ» и приказ № 236). Проверь заполненные сведения "
"об объекте КИИ и дай рекомендации. Верни ТОЛЬКО JSON: "
'{"оценка": "ok|warning|error", '
'"замечания": ["..."], '
'"предложения": {"attacker_category": "...", "threats": "...", "incidents": "...", '
'"criteria_values": "...", "criteria_justification": "...", '
'"org_measures": "...", "tech_measures": "..."}} '
"В «предложениях» укажи, что добавить/исправить по каждому разделу, если нужно, иначе пустые строки."
)
user = json.dumps(security_data, ensure_ascii=False)
js, err = _ai_json(api_key, model, base_url, system, user)
if err:
return None, err
notes = js.get('замечания') or []
if isinstance(notes, str):
notes = [notes]
proposals = js.get('предложения') or {}
if not isinstance(proposals, dict):
proposals = {}
return {
'source': 'ИИ',
'source_url': 'openai-совместимый API',
'disclaimer': AI_DISCLAIMER,
'kind': 'security',
'data': {
'rating': js.get('оценка', 'warning'),
'notes': notes,
'proposals': proposals,
},
}, None
# ============================================================
# 4. МАППИНГИ ПОЛЕЙ
# ============================================================
# В _collect_is() (список виджетов → модель):
# ('is_attacker_category_combo', 'attacker_category'),
# ('is_attacker_characteristics_listbox', 'attacker_characteristics'),
# ('is_threats_listbox', 'threats'),
# ('is_incidents_listbox', 'incidents'),
# ('is_criteria_values_listbox', 'criteria_values'),
# ('is_criteria_justification_listbox', 'criteria_justification'),
# ('is_org_measures_listbox', 'org_measures'),
# ('is_tech_measures_listbox', 'tech_measures'),
# В _on_is_select() — обратный порядок (combo → .set(), listbox → split('\n'))
# После построения вкладок вызвать: self._update_threats_date_label()
+39
View File
@@ -389,6 +389,45 @@ def compare_company_values(old_data, new_data):
return diffs
def check_security_ai(security_data, api_key, model='deepseek-chat', base_url=None):
"""ИИ-проверка заполненной вкладки «Безопасность» объекта КИИ (236 приказ)."""
if not api_key:
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
system = (
"Ты — эксперт по безопасности критической информационной инфраструктуры (187-ФЗ, "
"приказ ФСТЭК России от 25.12.2017 № 239 «Об утверждении Требований по обеспечению "
"безопасности значимых объектов КИИ» и приказ № 236). Проверь заполненные сведения "
"об объекте КИИ и дай рекомендации. Верни ТОЛЬКО JSON: "
'{"оценка": "ok|warning|error", '
'"замечания": ["..."], '
'"предложения": {"attacker_category": "...", "threats": "...", "incidents": "...", '
'"criteria_values": "...", "criteria_justification": "...", '
'"org_measures": "...", "tech_measures": "..."}} '
"В «предложениях» укажи, что добавить/исправить по каждому разделу, если нужно, иначе пустые строки."
)
user = json.dumps(security_data, ensure_ascii=False)
js, err = _ai_json(api_key, model, base_url, system, user)
if err:
return None, err
notes = js.get('замечания') or []
if isinstance(notes, str):
notes = [notes]
proposals = js.get('предложения') or {}
if not isinstance(proposals, dict):
proposals = {}
return {
'source': 'ИИ',
'source_url': 'openai-совместимый API',
'disclaimer': AI_DISCLAIMER,
'kind': 'security',
'data': {
'rating': js.get('оценка', 'warning'),
'notes': notes,
'proposals': proposals,
},
}, None
def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None):
"""ИИ-проверка реквизитов организации.