Compare commits
11
Commits
86836c2c9c
...
source
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baa8f095d0 | ||
|
|
de522360b8 | ||
|
|
da4946cb59 | ||
|
|
ac122451b3 | ||
|
|
eee1842421 | ||
|
|
bb987894b2 | ||
|
|
19b74fc72a | ||
|
|
5a407f2568 | ||
|
|
faefa786c5 | ||
|
|
4893899d5d | ||
|
|
73edfdf533 |
+3
-1
@@ -11,8 +11,10 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|||||||
datas = [
|
datas = [
|
||||||
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
|
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
|
||||||
('dokogen/dictionaries', 'dokogen/dictionaries'),
|
('dokogen/dictionaries', 'dokogen/dictionaries'),
|
||||||
# Пустой опросник 152-ФЗ (для «Справка → Пустой опросник»)
|
# Пустой опросник MU (для «Справка → Пустой опросник»)
|
||||||
('dokogen/data', 'dokogen/data'),
|
('dokogen/data', 'dokogen/data'),
|
||||||
|
# Иконка приложения (для окна программы в exe)
|
||||||
|
('dokogen/icon.ico', 'dokogen'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# ---------- Словари pymorphy3 (обязательно!) ----------
|
# ---------- Словари pymorphy3 (обязательно!) ----------
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Пакет генератора документов 152-ФЗ"""
|
"""DokoGen — Пакет генератора документов MU"""
|
||||||
|
|
||||||
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
||||||
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
|
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import sys
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
DEFAULT_SETTINGS = {
|
DEFAULT_SETTINGS = {
|
||||||
'dadata_token': '',
|
'dadata_token': '431aa11706bf83f571cef0319259289aa750bcb1',
|
||||||
'ai_api_key': '',
|
'ai_api_key': 'sk-3c7e6673f245412697d0e14b6096435f',
|
||||||
'ai_model': 'deepseek-chat',
|
'ai_model': 'deepseek-chat',
|
||||||
'ai_base_url': 'https://api.deepseek.com',
|
'ai_base_url': 'https://api.deepseek.com',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
"внешний нарушитель",
|
||||||
|
"внутренний нарушитель",
|
||||||
|
"внешний и внутренний нарушитель"
|
||||||
|
]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
"Социальная значимость — количество граждан, на которые может повлиять нарушение",
|
||||||
|
"Политическая значимость — международный или внутриполитический резонанс",
|
||||||
|
"Экономическая значимость — возможный размер ущерба (в рублях)",
|
||||||
|
"Экологическая значимость — возможный ущерб окружающей среде",
|
||||||
|
"Значимость для обороны страны, безопасности государства и правопорядка"
|
||||||
|
]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
"отказ в обслуживании",
|
||||||
|
"несанкционированный доступ",
|
||||||
|
"утечка данных (нарушение конфиденциальности)",
|
||||||
|
"модификация (подмена) данных",
|
||||||
|
"нарушение функционирования технических средств",
|
||||||
|
"несанкционированное использование вычислительных ресурсов объекта"
|
||||||
|
]
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[
|
||||||
|
"Установление контролируемой зоны",
|
||||||
|
"Контроль физического доступа к объекту",
|
||||||
|
"Разработка документов (регламентов, инструкций, руководств) по обеспечению безопасности объекта",
|
||||||
|
"Назначение ответственных за обеспечение безопасности объекта",
|
||||||
|
"Обучение и инструктаж персонала",
|
||||||
|
"Регламентация действий при компьютерных инцидентах",
|
||||||
|
"Утверждение перечня лиц, имеющих доступ к объекту"
|
||||||
|
]
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
[
|
[
|
||||||
"Сбор персональных данных",
|
"сбор",
|
||||||
"Запись персональных данных",
|
"систематизация",
|
||||||
"Систематизация персональных данных",
|
"хранение",
|
||||||
"Накопление персональных данных",
|
"извлечение",
|
||||||
"Хранение персональных данных",
|
"передача (предоставление доступа)",
|
||||||
"Уточнение (обновление, изменение) персональных данных",
|
"блокирование",
|
||||||
"Извлечение персональных данных",
|
"уничтожение",
|
||||||
"Использование персональных данных",
|
"запись",
|
||||||
"Передача (распространение, предоставление, доступ) персональных данных",
|
"накопление",
|
||||||
"Обезличивание персональных данных",
|
"уточнение (обновление, изменение)",
|
||||||
"Блокирование персональных данных",
|
"использование",
|
||||||
"Удаление персональных данных",
|
"обезличивание",
|
||||||
"Уничтожение персональных данных",
|
"удаление",
|
||||||
"Трансграничная передача персональных данных",
|
"распространение",
|
||||||
"Автоматизированная обработка персональных данных",
|
"иные действия"
|
||||||
"Неавтоматизированная обработка персональных данных",
|
]
|
||||||
"Смешанная обработка персональных данных",
|
|
||||||
"Формирование и ведение баз данных",
|
|
||||||
"Передача данных третьим лицам",
|
|
||||||
"Внутренняя передача данных между подразделениями"
|
|
||||||
]
|
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
[
|
[
|
||||||
"работники оператора",
|
"Сотрудникам организации",
|
||||||
"клиенты",
|
"Соискателям",
|
||||||
"контрагенты",
|
"Родственникам сотрудников",
|
||||||
"родственники работников",
|
"Уволенным сотрудникам",
|
||||||
"соискатели",
|
"Контрагентам",
|
||||||
"иные лица"
|
"Представителям контрагентов",
|
||||||
]
|
"Клиентам",
|
||||||
|
"Посетителям сайта",
|
||||||
|
"Выгодоприобретателям по договорам",
|
||||||
|
"Субъект персональных данных, предоставивший согласие на трансграничную передачу персональных данных",
|
||||||
|
"Учащимя",
|
||||||
|
"Студентам",
|
||||||
|
"Законным представителям",
|
||||||
|
"Иным категориям субъектов персональных данных, персональные данные которых обрабатываются"
|
||||||
|
]
|
||||||
@@ -1,28 +1,44 @@
|
|||||||
[
|
[
|
||||||
"Фамилия, имя, отчество",
|
"Фамилия, Имя, Отчество",
|
||||||
|
"Год рождения",
|
||||||
|
"Месяц рождения",
|
||||||
"Дата рождения",
|
"Дата рождения",
|
||||||
"Место рождения",
|
"Место рождения",
|
||||||
"Паспортные данные",
|
|
||||||
"Адрес регистрации",
|
|
||||||
"Адрес проживания",
|
|
||||||
"ИНН",
|
|
||||||
"СНИЛС",
|
|
||||||
"Номер телефона",
|
|
||||||
"Адрес электронной почты",
|
|
||||||
"Образование",
|
|
||||||
"Профессия",
|
|
||||||
"Сведения о доходах",
|
|
||||||
"Семейное положение",
|
"Семейное положение",
|
||||||
"Состав семьи",
|
"Социальное положение",
|
||||||
"Состояние здоровья",
|
"Имущественное положение",
|
||||||
"Национальность",
|
"Сведения о доходах",
|
||||||
"Гражданство",
|
"Пол",
|
||||||
"Сведения о судимости",
|
"Адрес электронной почты",
|
||||||
"Фотография",
|
"Адрес места жительства",
|
||||||
"Биометрические данные",
|
"Адрес регистрации",
|
||||||
"Рабочий e-mail",
|
"Номер телефона",
|
||||||
|
"Страховой номер индивидуального лицевого счета (СНИЛС)",
|
||||||
|
"Идентификационный номер налогоплательщика (ИНН);",
|
||||||
|
"Сведения о гражданстве",
|
||||||
|
"Данные документа, удостоверяющего личность",
|
||||||
|
"Данные водительского удостоверения",
|
||||||
|
"Данные документа, удостоверяющего личность за пределами Российской Федерации",
|
||||||
|
"Данные документа, содержащиеся в свидетельстве о рождении",
|
||||||
|
"Реквизиты банковских карт",
|
||||||
|
"Номер расчетного счета",
|
||||||
|
"Номер лицевого счета",
|
||||||
|
"Профессия",
|
||||||
"Должность",
|
"Должность",
|
||||||
"Табельный номер",
|
"Сведения о трудовой деятельности (в том числе стаж работы, данные о трудовой занятости на текущее время с указанием наименования и расчетного счета организации)",
|
||||||
"Сведения о воинском учете",
|
"Сведения об отношении к воинской обязанности, о воинском учете",
|
||||||
"Сведения о командировках"
|
"Иные персональные данные",
|
||||||
]
|
"Сведения, собираемые посредством метрических программ (например, Яндекс.Метрика)",
|
||||||
|
"Сведения об образовании",
|
||||||
|
"Фото-видео изображение лица",
|
||||||
|
"Данные голоса человека",
|
||||||
|
"Сведения о состоянии здоровья",
|
||||||
|
"Национальная принадлежность",
|
||||||
|
"Политические взгляды",
|
||||||
|
"Религиозные или философские убеждения",
|
||||||
|
"Сведения о судимости",
|
||||||
|
"Данные изображения лица, полученные с помощью фото- видео устройств, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
|
||||||
|
"Данные голоса человека, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
|
||||||
|
"Изображение папиллярных узоров пальцев рук",
|
||||||
|
"Иные биометрические персональные данные"
|
||||||
|
]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
"Идентификация и аутентификация пользователей",
|
||||||
|
"Управление доступом",
|
||||||
|
"Ограничение программной среды",
|
||||||
|
"Антивирусная защита",
|
||||||
|
"Защита информации от несанкционированного доступа (СЗИ от НСД)",
|
||||||
|
"Регистрация и мониторинг событий безопасности",
|
||||||
|
"Межсетевое экранирование",
|
||||||
|
"Обнаружение вторжений (СОВ)",
|
||||||
|
"Криптографическая защита информации (СКЗИ)",
|
||||||
|
"Защита среды виртуализации",
|
||||||
|
"Резервное копирование и восстановление",
|
||||||
|
"Защита технических средств и линий связи"
|
||||||
|
]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
"Угроза несанкционированного доступа к информационным ресурсам объекта",
|
||||||
|
"Угроза нарушения функционирования объекта (отказ в обслуживании)",
|
||||||
|
"Угроза утечки (нарушения конфиденциальности) информации",
|
||||||
|
"Угроза модификации (подмены) данных",
|
||||||
|
"Угроза внедрения вредоносного кода",
|
||||||
|
"Угроза несанкционированного использования вычислительных ресурсов",
|
||||||
|
"Угроза перехвата (прослушивания) сетевого трафика",
|
||||||
|
"Угроза социальной инженерии",
|
||||||
|
"Угроза использования уязвимостей программного обеспечения",
|
||||||
|
"Угроза физического воздействия на технические средства"
|
||||||
|
]
|
||||||
@@ -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
|
||||||
@@ -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 []
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
рядом с программой (при сборке .exe — рядом с exe):
|
рядом с программой (при сборке .exe — рядом с exe):
|
||||||
- Инструкция_пользователя.docx
|
- Инструкция_пользователя.docx
|
||||||
- Инструкция_администратора.docx
|
- Инструкция_администратора.docx
|
||||||
- Опросный_лист_152_ФЗ.xlsx (пустой шаблон)
|
- Опросный_лист_MU.xlsx (пустой шаблон)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -45,7 +45,7 @@ def variables_reference_path():
|
|||||||
|
|
||||||
|
|
||||||
def survey_path():
|
def survey_path():
|
||||||
return os.path.join(docs_dir(), 'Опросный_лист_152_ФЗ.xlsx')
|
return os.path.join(docs_dir(), 'Опросный_лист_MU.xlsx')
|
||||||
|
|
||||||
|
|
||||||
def _h(doc, text, size=14):
|
def _h(doc, text, size=14):
|
||||||
@@ -83,7 +83,7 @@ def _generate_variables_reference(path):
|
|||||||
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
|
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
|
||||||
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
|
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
|
||||||
|
|
||||||
groups = {'A': 'Общие переменные', 'B': '152-ФЗ (персональные данные)'}
|
groups = {'A': 'Общие переменные', 'B': 'MU (персональные данные)'}
|
||||||
by_group = {'A': [], 'B': []}
|
by_group = {'A': [], 'B': []}
|
||||||
for var, group, desc, _ in VARIABLE_DEFS:
|
for var, group, desc, _ in VARIABLE_DEFS:
|
||||||
by_group.setdefault(group, []).append((var, desc))
|
by_group.setdefault(group, []).append((var, desc))
|
||||||
@@ -108,11 +108,11 @@ def _generate_variables_reference(path):
|
|||||||
def _generate_user_manual(path):
|
def _generate_user_manual(path):
|
||||||
"""Инструкция пользователя: программа, циклы, переменные, проверки."""
|
"""Инструкция пользователя: программа, циклы, переменные, проверки."""
|
||||||
doc = docx.Document()
|
doc = docx.Document()
|
||||||
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ')
|
_h(doc, 'ДоКоГеНеРаТоР — MU')
|
||||||
_h(doc, 'Инструкция пользователя', 13)
|
_h(doc, 'Инструкция пользователя', 13)
|
||||||
|
|
||||||
_h(doc, '1. Что это за программа', 12)
|
_h(doc, '1. Что это за программа', 12)
|
||||||
_p(doc, 'Программа автоматически заполняет шаблоны документов по 152-ФЗ '
|
_p(doc, 'Программа автоматически заполняет шаблоны документов по MU '
|
||||||
'(акты обследования, политики, инструкции, приказы и т.д.) данными '
|
'(акты обследования, политики, инструкции, приказы и т.д.) данными '
|
||||||
'об организации, информационных системах, комиссии и сотрудниках. '
|
'об организации, информационных системах, комиссии и сотрудниках. '
|
||||||
'Данные можно ввести вручную на вкладках или импортировать из '
|
'Данные можно ввести вручную на вкладках или импортировать из '
|
||||||
@@ -173,7 +173,7 @@ def _generate_user_manual(path):
|
|||||||
def _generate_admin_manual(path):
|
def _generate_admin_manual(path):
|
||||||
"""Инструкция администратора: словари, API-ключи, лимиты."""
|
"""Инструкция администратора: словари, API-ключи, лимиты."""
|
||||||
doc = docx.Document()
|
doc = docx.Document()
|
||||||
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ')
|
_h(doc, 'ДоКоГеНеРаТоР — MU')
|
||||||
_h(doc, 'Инструкция администратора', 13)
|
_h(doc, 'Инструкция администратора', 13)
|
||||||
|
|
||||||
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
||||||
@@ -240,8 +240,8 @@ def _ensure_survey():
|
|||||||
return target
|
return target
|
||||||
# Ищем шаблон опросника: рядом с программой или встроенный
|
# Ищем шаблон опросника: рядом с программой или встроенный
|
||||||
candidates = [
|
candidates = [
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_152_ФЗ.xlsx'),
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_MU.xlsx'),
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_152_ФЗ.xlsx'),
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_MU.xlsx'),
|
||||||
]
|
]
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
if os.path.exists(c):
|
if os.path.exists(c):
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
+9
-9
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Модуль импорта из Excel (опросный лист 152-ФЗ)"""
|
"""DokoGen — Модуль импорта из Excel (опросный лист MU)"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
@@ -122,8 +122,8 @@ def clean_value(val):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
def import_mufz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
||||||
"""Импорт данных из опросного листа Excel (152-ФЗ).
|
"""Импорт данных из опросного листа Excel (MU).
|
||||||
|
|
||||||
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
|
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
|
||||||
Если None — файл import_settings.json рядом с программой; если его нет —
|
Если None — файл import_settings.json рядом с программой; если его нет —
|
||||||
@@ -154,9 +154,9 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_import_152_common(wb, data, settings, log_fn)
|
_import_mu_common(wb, data, settings, log_fn)
|
||||||
_import_152_is_list(wb, data, settings, log_fn)
|
_import_mu_is_list(wb, data, settings, log_fn)
|
||||||
_import_152_employees(wb, data, settings, log_fn)
|
_import_mu_employees(wb, data, settings, log_fn)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_fn(f"❌ Ошибка при импорте: {e}")
|
log_fn(f"❌ Ошибка при импорте: {e}")
|
||||||
log_fn(traceback.format_exc())
|
log_fn(traceback.format_exc())
|
||||||
@@ -179,7 +179,7 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def _import_152_common(wb, data, settings, log_fn):
|
def _import_mu_common(wb, data, settings, log_fn):
|
||||||
"""Импорт общих сведений по словарю настроек.
|
"""Импорт общих сведений по словарю настроек.
|
||||||
|
|
||||||
Для каждого поля: сначала пробуем конкретную ячейку (cell),
|
Для каждого поля: сначала пробуем конкретную ячейку (cell),
|
||||||
@@ -247,7 +247,7 @@ def _import_152_common(wb, data, settings, log_fn):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def _import_152_is_list(wb, data, settings, log_fn):
|
def _import_mu_is_list(wb, data, settings, log_fn):
|
||||||
"""Импорт информационных систем из остальных листов."""
|
"""Импорт информационных систем из остальных листов."""
|
||||||
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
||||||
is_sheets = []
|
is_sheets = []
|
||||||
@@ -447,7 +447,7 @@ def _import_152_is_list(wb, data, settings, log_fn):
|
|||||||
log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}")
|
log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}")
|
||||||
|
|
||||||
|
|
||||||
def _import_152_employees(wb, data, settings, log_fn):
|
def _import_mu_employees(wb, data, settings, log_fn):
|
||||||
"""Импорт комиссии и сотрудников из листа «Сотрудники»."""
|
"""Импорт комиссии и сотрудников из листа «Сотрудники»."""
|
||||||
sheet_name = None
|
sheet_name = None
|
||||||
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
|
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Модели данных (только 152-ФЗ, только нужные поля)"""
|
"""DokoGen — Модели данных (только MU, только нужные поля)"""
|
||||||
|
|
||||||
from dataclasses import dataclass, field, asdict
|
from dataclasses import dataclass, field, asdict
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
@@ -49,7 +49,7 @@ class PaperDocument:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Company:
|
class Company:
|
||||||
"""Организация — основной объект (152-ФЗ)"""
|
"""Организация — основной объект (MU)"""
|
||||||
# Полное название
|
# Полное название
|
||||||
full_name: str = ""
|
full_name: str = ""
|
||||||
company_name_1: str = "" # именительный
|
company_name_1: str = "" # именительный
|
||||||
|
|||||||
@@ -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()
|
||||||
+29
-8
@@ -31,9 +31,10 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def __init__(self, root: tk.Tk):
|
def __init__(self, root: tk.Tk):
|
||||||
self.root = root
|
self.root = root
|
||||||
root.title("ДоКоГеНеРаТоР — 152-ФЗ (Персональные данные)")
|
root.title("ДоКоГеНеРаТоР — MU")
|
||||||
root.geometry("1200x800")
|
root.geometry("1200x800")
|
||||||
root.minsize(900, 600)
|
root.minsize(900, 600)
|
||||||
|
self._set_app_icon()
|
||||||
|
|
||||||
self.model = Company()
|
self.model = Company()
|
||||||
self.current_file: Optional[str] = None
|
self.current_file: Optional[str] = None
|
||||||
@@ -43,7 +44,7 @@ class DokoGenApp:
|
|||||||
self._last_template_dir: str = ""
|
self._last_template_dir: str = ""
|
||||||
self._autosave_path = os.path.join(
|
self._autosave_path = os.path.join(
|
||||||
os.path.dirname(os.path.abspath(__file__)),
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
'.autosave', 'autosave_152.json'
|
'.autosave', 'autosave_mufz.json'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Путь к файлу настроек импорта (словарь «что и откуда брать»)
|
# Путь к файлу настроек импорта (словарь «что и откуда брать»)
|
||||||
@@ -81,6 +82,26 @@ class DokoGenApp:
|
|||||||
self._load_autosave()
|
self._load_autosave()
|
||||||
self._start_autosave_timer()
|
self._start_autosave_timer()
|
||||||
|
|
||||||
|
def _set_app_icon(self):
|
||||||
|
"""Устанавливает иконку приложения на окно (заголовок, панель задач).
|
||||||
|
|
||||||
|
Работает и из исходников, и из exe (frozen):
|
||||||
|
- исходники: dokogen/icon.ico рядом с модулем
|
||||||
|
- exe: dokogen/icon.ico из _MEIPASS (ресурсы PyInstaller)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
base = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
|
||||||
|
ico = os.path.join(base, 'dokogen', 'icon.ico')
|
||||||
|
if not os.path.exists(ico):
|
||||||
|
ico = os.path.join(os.path.dirname(sys.executable), 'icon.ico')
|
||||||
|
else:
|
||||||
|
ico = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon.ico')
|
||||||
|
if os.path.exists(ico):
|
||||||
|
self.root.iconbitmap(ico)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# ==================== МЕНЮ ====================
|
# ==================== МЕНЮ ====================
|
||||||
def _build_menu(self):
|
def _build_menu(self):
|
||||||
menubar = tk.Menu(self.root)
|
menubar = tk.Menu(self.root)
|
||||||
@@ -1736,7 +1757,7 @@ class DokoGenApp:
|
|||||||
is_list=self.model.information_systems,
|
is_list=self.model.information_systems,
|
||||||
doc_number=self.doc_number_var.get(),
|
doc_number=self.doc_number_var.get(),
|
||||||
doc_date=self.doc_date_var.get(),
|
doc_date=self.doc_date_var.get(),
|
||||||
direction='152fz',
|
direction='mufz',
|
||||||
)
|
)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
@@ -1825,7 +1846,7 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def import_from_survey(self):
|
def import_from_survey(self):
|
||||||
path = filedialog.askopenfilename(
|
path = filedialog.askopenfilename(
|
||||||
title="Выберите опросный лист (152-ФЗ)",
|
title="Выберите опросный лист (MU)",
|
||||||
filetypes=[("Excel", "*.xlsx *.xls")]
|
filetypes=[("Excel", "*.xlsx *.xls")]
|
||||||
)
|
)
|
||||||
if not path:
|
if not path:
|
||||||
@@ -1835,8 +1856,8 @@ class DokoGenApp:
|
|||||||
# Импорт в отдельном потоке
|
# Импорт в отдельном потоке
|
||||||
def _run():
|
def _run():
|
||||||
try:
|
try:
|
||||||
from .importer import import_152fz
|
from .importer import import_mufz
|
||||||
data = import_152fz(path, self.log, settings_path=self.import_settings_path)
|
data = import_mufz(path, self.log, settings_path=self.import_settings_path)
|
||||||
if data:
|
if data:
|
||||||
self._apply_imported_data(data)
|
self._apply_imported_data(data)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -2182,7 +2203,7 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def show_about(self):
|
def show_about(self):
|
||||||
messagebox.showinfo("О программе",
|
messagebox.showinfo("О программе",
|
||||||
"ДоКоГеНеРаТоР — 152-ФЗ\n"
|
"ДоКоГеНеРаТоР — MU\n"
|
||||||
"Версия 1.0\n"
|
"Версия 1.0\n"
|
||||||
"Генератор документов из шаблонов")
|
"Генератор документов из шаблонов")
|
||||||
|
|
||||||
@@ -2230,7 +2251,7 @@ class DokoGenApp:
|
|||||||
def download_survey(self):
|
def download_survey(self):
|
||||||
from .help_docs import ensure_docs, survey_path
|
from .help_docs import ensure_docs, survey_path
|
||||||
ensure_docs()
|
ensure_docs()
|
||||||
self._save_resource(survey_path(), "Опросный_лист_152_ФЗ.xlsx", "Сохранить пустой опросник")
|
self._save_resource(survey_path(), "Опросный_лист_MU.xlsx", "Сохранить пустой опросник")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def _norm_name(s):
|
|||||||
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
||||||
# Группы: A — общие, B — 152-ФЗ
|
# Группы: A — общие, B — MU
|
||||||
|
|
||||||
VARIABLE_DEFS = [
|
VARIABLE_DEFS = [
|
||||||
# === Полное название организации (6 падежей) ===
|
# === Полное название организации (6 падежей) ===
|
||||||
@@ -68,7 +68,7 @@ VARIABLE_DEFS = [
|
|||||||
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
||||||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||||||
|
|
||||||
# === 152-ФЗ ===
|
# === MU ===
|
||||||
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
||||||
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
||||||
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
||||||
@@ -138,7 +138,7 @@ def build_replacements(
|
|||||||
is_list: List = None,
|
is_list: List = None,
|
||||||
doc_number: str = "",
|
doc_number: str = "",
|
||||||
doc_date: str = "",
|
doc_date: str = "",
|
||||||
direction: str = "152fz",
|
direction: str = "mufz",
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
"""Строит полный словарь замен {{переменная}} → значение."""
|
"""Строит полный словарь замен {{переменная}} → значение."""
|
||||||
if is_list is None:
|
if is_list is None:
|
||||||
@@ -159,7 +159,7 @@ def build_replacements(
|
|||||||
|
|
||||||
replacements = {}
|
replacements = {}
|
||||||
for var_name, group, desc, source in VARIABLE_DEFS:
|
for var_name, group, desc, source in VARIABLE_DEFS:
|
||||||
if direction == '152fz' and group not in ('A', 'B'):
|
if direction == 'mufz' and group not in ('A', 'B'):
|
||||||
continue
|
continue
|
||||||
value = _resolve(source, ctx)
|
value = _resolve(source, ctx)
|
||||||
replacements[var_name] = str(value) if value else '—'
|
replacements[var_name] = str(value) if value else '—'
|
||||||
|
|||||||
+40
-1
@@ -389,6 +389,45 @@ def compare_company_values(old_data, new_data):
|
|||||||
return diffs
|
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):
|
def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None):
|
||||||
"""ИИ-проверка реквизитов организации.
|
"""ИИ-проверка реквизитов организации.
|
||||||
|
|
||||||
@@ -399,7 +438,7 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None
|
|||||||
if not api_key:
|
if not api_key:
|
||||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||||
system = (
|
system = (
|
||||||
"Ты — юрист по защите персональных данных (152-ФЗ). Проверь реквизиты организации. "
|
"Ты — юрист по защите персональных данных (MU). Проверь реквизиты организации. "
|
||||||
"Верни ТОЛЬКО JSON: "
|
"Верни ТОЛЬКО JSON: "
|
||||||
'{"оценка": "ok|warning|error", '
|
'{"оценка": "ok|warning|error", '
|
||||||
'"замечания": ["..."], '
|
'"замечания": ["..."], '
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Генератор документов 152-ФЗ
|
"""DokoGen — Генератор документов MU
|
||||||
Точка входа: python main.py
|
Точка входа: python main.py
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
+2
-2
@@ -87,7 +87,7 @@ def test_zip_loop_expansion():
|
|||||||
is_list=company.information_systems,
|
is_list=company.information_systems,
|
||||||
doc_number='001',
|
doc_number='001',
|
||||||
doc_date='31.07.2026',
|
doc_date='31.07.2026',
|
||||||
direction='152fz'
|
direction='mufz'
|
||||||
)
|
)
|
||||||
|
|
||||||
loops = replacements.get('_loops', {})
|
loops = replacements.get('_loops', {})
|
||||||
@@ -135,7 +135,7 @@ def test_zip_loop_expansion():
|
|||||||
is_list=company.information_systems,
|
is_list=company.information_systems,
|
||||||
doc_number='001',
|
doc_number='001',
|
||||||
doc_date='31.07.2026',
|
doc_date='31.07.2026',
|
||||||
direction='152fz'
|
direction='mufz'
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user