Compare commits
109
Commits
c14094c71e
...
source
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
691b167725 | ||
|
|
a7878804e4 | ||
|
|
8e97e10714 | ||
|
|
dbf8c613dd | ||
|
|
b2b68258fd | ||
|
|
1840f1e2d4 | ||
|
|
e15610ba29 | ||
|
|
36d2d5a87b | ||
|
|
c90660a0f8 | ||
|
|
5bb6a2f5e9 | ||
|
|
829a1a98e3 | ||
|
|
ad54d3e0e2 | ||
|
|
9f8bf3999d | ||
|
|
07bf25ebc3 | ||
|
|
0b3ef66125 | ||
|
|
1793182735 | ||
|
|
52b4ee6f1a | ||
|
|
c9dffdd4ca | ||
|
|
136c58c948 | ||
|
|
28fa7edaa8 | ||
|
|
614044a6ca | ||
|
|
9a1058559a | ||
|
|
a8fa3f908c | ||
|
|
033c21fccc | ||
|
|
bf75d67f5e | ||
|
|
5af23ae525 | ||
|
|
868e0065f3 | ||
|
|
5591dc2c5b | ||
|
|
094f6c26e2 | ||
|
|
88ab71dc14 | ||
|
|
638e2446d5 | ||
|
|
b3ae4c22cf | ||
|
|
d376a28184 | ||
|
|
fafeea4fcf | ||
|
|
5544941469 | ||
|
|
220b923e76 | ||
|
|
e4e7bb444a | ||
|
|
acb7aeaa64 | ||
|
|
c1ad496319 | ||
|
|
7742cb1e10 | ||
|
|
4bffd0e08d | ||
|
|
29ba4b0532 | ||
|
|
5aed863b83 | ||
|
|
0f9b3a1f11 | ||
|
|
0098f483d2 | ||
|
|
52b3bb8477 | ||
|
|
4313b0a65d | ||
|
|
dadec02665 | ||
|
|
b9968449ef | ||
|
|
7f70bb0792 | ||
|
|
972ff11b49 | ||
|
|
4fc6fba92a | ||
|
|
6852bc634b | ||
|
|
3922b8264a | ||
|
|
8c1d94d881 | ||
|
|
68caaf9b63 | ||
|
|
1ef8485b13 | ||
|
|
72f73d4c4f | ||
|
|
07f0800b73 | ||
|
|
5bbd9313f5 | ||
|
|
8438125bd1 | ||
|
|
0efd3b4d8b | ||
|
|
49d24db234 | ||
|
|
c4a8de57d1 | ||
|
|
730551aeba | ||
|
|
8e647fc79a | ||
|
|
879ada5859 | ||
|
|
b044b05eaf | ||
|
|
58b4402d31 | ||
|
|
6c2e4a0c38 | ||
|
|
8569b478d4 | ||
|
|
6206c867c7 | ||
|
|
5b318eef88 | ||
|
|
38eae4b16f | ||
|
|
7158a90b76 | ||
|
|
b6f595fd32 | ||
|
|
42a243f7b6 | ||
|
|
0b3aff4ae9 | ||
|
|
9d260b2655 | ||
|
|
3ef1ff41e0 | ||
|
|
e4d4324005 | ||
|
|
f04a5bd524 | ||
|
|
aae66dcca1 | ||
|
|
ba2a02d1cd | ||
|
|
28cd764795 | ||
|
|
300dfed5b3 | ||
|
|
f5548f2fb8 | ||
|
|
463745123c | ||
|
|
4ca596102f | ||
|
|
19b481a540 | ||
|
|
cf2a80c470 | ||
|
|
9489e6a37d | ||
|
|
4e7c3463f4 | ||
|
|
ffbe96d8c0 | ||
|
|
ed71343d92 | ||
|
|
2037925986 | ||
|
|
ce1b56066a | ||
|
|
b115f9e2b4 | ||
|
|
da4946cb59 | ||
|
|
ac122451b3 | ||
|
|
eee1842421 | ||
|
|
bb987894b2 | ||
|
|
19b74fc72a | ||
|
|
5a407f2568 | ||
|
|
faefa786c5 | ||
|
|
4893899d5d | ||
|
|
73edfdf533 | ||
|
|
86836c2c9c | ||
|
|
aa6056e686 |
@@ -3,3 +3,8 @@ __pycache__/
|
||||
.autosave/
|
||||
Сгенерированные_документы/
|
||||
*.result.docx
|
||||
|
||||
# Секреты и локальные настройки — НЕ коммитить
|
||||
api_settings.json
|
||||
.env
|
||||
.env.local
|
||||
|
||||
+3
-1
@@ -11,8 +11,10 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
||||
datas = [
|
||||
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
|
||||
('dokogen/dictionaries', 'dokogen/dictionaries'),
|
||||
# Пустой опросник 152-ФЗ (для «Справка → Пустой опросник»)
|
||||
# Пустой опросник 187-ФЗ (для «Справка → Пустой опросник»)
|
||||
('dokogen/data', 'dokogen/data'),
|
||||
# Иконка приложения (для окна программы в exe)
|
||||
('dokogen/icon.ico', 'dokogen'),
|
||||
]
|
||||
|
||||
# ---------- Словари pymorphy3 (обязательно!) ----------
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Пакет генератора документов 152-ФЗ"""
|
||||
"""DokoGen — Пакет генератора документов 187-ФЗ"""
|
||||
|
||||
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
||||
from .models import Company, CommissionMember, InformationSystem
|
||||
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
|
||||
from .variables import build_replacements, VARIABLE_DEFS
|
||||
from .generator import process_template
|
||||
|
||||
+10
-4
@@ -13,11 +13,17 @@ import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
# ВНИМАНИЕ: реальные ключи НЕ хранятся в коде (утечка в git-историю — см. аудит).
|
||||
# Источники ключей (по приоритету):
|
||||
# 1) переменные окружения DADATA_TOKEN / DEEPSEEK_API_KEY / DEEPSEEK_MODEL / DEEPSEEK_BASE_URL
|
||||
# 2) файл api_settings.json рядом с программой
|
||||
# 3) DEFAULT_SETTINGS ниже (пустые значения)
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
'dadata_token': '',
|
||||
'ai_api_key': '',
|
||||
'ai_model': 'deepseek-chat',
|
||||
'ai_base_url': 'https://api.deepseek.com',
|
||||
'dadata_token': os.environ.get('DADATA_TOKEN', ''),
|
||||
'ai_api_key': os.environ.get('DEEPSEEK_API_KEY', ''),
|
||||
'ai_model': os.environ.get('DEEPSEEK_MODEL', 'deepseek-chat'),
|
||||
'ai_base_url': os.environ.get('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'),
|
||||
}
|
||||
|
||||
# Старые ключи (для совместимости со старым файлом api_settings.json)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
[
|
||||
"Microsoft Office (уточнить версию)",
|
||||
"LibreOffice",
|
||||
"OpenOffice",
|
||||
"1С:Предприятие 8.3 (уточнить конфигурацию)",
|
||||
"Контур.Экстерн",
|
||||
"СБИС",
|
||||
"Kaspersky Endpoint Security",
|
||||
"Kaspersky Security Center",
|
||||
"Почтовый клиент (уточнить)",
|
||||
"Браузер (уточнить)",
|
||||
"СЭД (уточнить систему)",
|
||||
"АРМ оператора (уточнить)",
|
||||
"СКУД ПО (уточнить)",
|
||||
"Видеонаблюдение ПО (уточнить)"
|
||||
]
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
"одноранговая сеть",
|
||||
"клиент-серверная система",
|
||||
"технология \"тонкий клиент\"",
|
||||
"сеть передачи данных",
|
||||
"система диспетчерского управления и контроля",
|
||||
"распределенная система управления",
|
||||
"иная архитектура"
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "Внешний нарушитель",
|
||||
"description": "Нарушитель, не имеющий прав доступа в контролируемую (охраняемую) зону (территорию) и (или) полномочий по доступу к информационным ресурсам и компонентам систем и сетей, требующим авторизации"
|
||||
},
|
||||
{
|
||||
"name": "Внутренний нарушитель",
|
||||
"description": "Нарушитель, имеющий права доступа в контролируемую (охраняемую) зону (территорию) и (или) полномочия по автоматизированному доступу к информационным ресурсам и компонентам систем и сетей. К внутренним нарушителям относятся пользователи, имеющие как непривилегированные (пользовательские), так и привилегированные (административные) права доступа"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,43 @@
|
||||
[
|
||||
{
|
||||
"level": "Н1",
|
||||
"name": "Базовые возможности",
|
||||
"description": "Нарушитель, обладающий базовыми возможностями. Имеет возможность использовать только известные уязвимости, скрипты и инструменты, свободно распространяемые в сети «Интернет» и разработанные другими лицами. Обладает базовыми компьютерными знаниями и навыками на уровне пользователя. Имеет возможность реализации угроз за счет физических воздействий на технические средства обработки и хранения информации, линии связи и обеспечивающие системы при наличии физического доступа к ним. Может реализовывать только известные угрозы, направленные на известные (документированные) уязвимости, с использованием общедоступных инструментов.",
|
||||
"types": [
|
||||
"Отдельные физические лица (хакеры)",
|
||||
"Лица, обеспечивающие поставку программных, программно-аппаратных средств, обеспечивающих систем",
|
||||
"Лица, обеспечивающие функционирование систем и сетей или обеспечивающих систем (администрация, охрана, уборщики и т.д.)",
|
||||
"Авторизованные пользователи систем и сетей",
|
||||
"Бывшие работники (пользователи)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": "Н2",
|
||||
"name": "Базовые повышенные возможности",
|
||||
"description": "Обладает всеми возможностями нарушителей с базовыми возможностями. Хорошо владеет средствами и инструментами, понимает, как они работают, и может вносить изменения в их функционирование для повышения эффективности реализации угроз. Оснащен фреймворками и наборами средств для реализации угроз и использования уязвимостей. Имеет навыки самостоятельного планирования и реализации сценариев угроз. Обладает практическими знаниями о функционировании систем и сетей, операционных систем, а также знаниями защитных механизмов. Может реализовывать угрозы, в том числе направленные на неизвестные (недокументированные) уязвимости, с использованием специально созданных инструментов. Не имеет возможностей реализации угроз на физически изолированные сегменты систем и сетей.",
|
||||
"types": [
|
||||
"Преступные группы (два лица и более, действующие по единому плану)",
|
||||
"Конкурирующие организации",
|
||||
"Поставщики вычислительных услуг, услуг связи",
|
||||
"Лица, привлекаемые для установки, настройки, испытаний, пусконаладочных и иных видов работ",
|
||||
"Системные администраторы и администраторы безопасности"
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": "Н3",
|
||||
"name": "Средние возможности",
|
||||
"description": "Обладает всеми возможностями нарушителей с базовыми повышенными возможностями. Имеет возможность приобретать информацию об уязвимостях, размещаемую на специализированных платных ресурсах (биржах уязвимостей), приобретать дорогостоящие средства и инструменты для реализации угроз, самостоятельно разрабатывать средства (инструменты), необходимые для реализации угроз (атак). Имеет возможность получения доступа к встраиваемому программному обеспечению аппаратных платформ, системному и прикладному программному обеспечению, телекоммуникационному оборудованию для проведения их анализа. Обладает знаниями и практическими навыками проведения анализа программного кода для получения информации об уязвимостях. Имеет возможность реализовывать угрозы в составе группы лиц. Не имеет возможностей реализации угроз на физически изолированные сегменты систем и сетей.",
|
||||
"types": [
|
||||
"Террористические, экстремистские группировки",
|
||||
"Разработчики программных, программно-аппаратных средств"
|
||||
]
|
||||
},
|
||||
{
|
||||
"level": "Н4",
|
||||
"name": "Высокие возможности",
|
||||
"description": "Обладает всеми возможностями нарушителей со средними возможностями. Имеет возможность получения доступа к исходному коду встраиваемого программного обеспечения аппаратных платформ, системного и прикладного программного обеспечения, телекоммуникационного оборудования для получения сведений об уязвимостях «нулевого дня». Имеет возможность внедрения программных (программно-аппаратных) закладок или уязвимостей на различных этапах поставки программного обеспечения или программно-аппаратных средств. Имеет возможность создания методов и средств реализации угроз с привлечением специализированных научных организаций, создания и применения специальных технических средств для добывания информации. Имеет возможность долговременно и незаметно для операторов систем и сетей реализовывать угрозы. Обладает исключительными знаниями и практическими навыками о функционировании систем и сетей. Имеет практически неограниченные возможности реализовывать угрозы, в том числе с использованием недекларированных возможностей, программных, программно-аппаратных закладок, встроенных в компоненты систем и сетей.",
|
||||
"types": [
|
||||
"Специальные службы иностранных государств"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
[
|
||||
"Специальные службы иностранных государств",
|
||||
"Террористические, экстремистские группировки",
|
||||
"Преступные группы (криминальные структуры)",
|
||||
"Отдельные физические лица (хакеры)",
|
||||
"Конкурирующие организации",
|
||||
"Разработчики программных, программно-аппаратных средств",
|
||||
"Лица, обеспечивающие поставку программных, программно-аппаратных средств, обеспечивающих систем",
|
||||
"Поставщики услуг связи, вычислительных услуг",
|
||||
"Лица, привлекаемые для установки, настройки, испытаний, пусконаладочных и иных видов работ",
|
||||
"Лица, обеспечивающие функционирование систем и сетей или обеспечивающих систем оператора (администрация, охрана, уборщики и др.)",
|
||||
"Авторизованные пользователи систем и сетей",
|
||||
"Системные администраторы и администраторы безопасности",
|
||||
"Бывшие (уволенные) работники (пользователи)"
|
||||
]
|
||||
@@ -0,0 +1,297 @@
|
||||
[
|
||||
{
|
||||
"criteria": "I. Социальная значимость",
|
||||
"indicators": [
|
||||
{
|
||||
"num": "1",
|
||||
"name": "Причинение ущерба жизни и здоровью людей (человек)",
|
||||
"values": {
|
||||
"III": "более или равно 1, но менее или равно 50",
|
||||
"II": "более 50, но менее или равно 500",
|
||||
"I": "более 500"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "2а",
|
||||
"name": "Прекращение или нарушение функционирования объектов обеспечения жизнедеятельности населения, оцениваемые на территории, на которой возможно нарушение обеспечения жизнедеятельности населения",
|
||||
"values": {
|
||||
"III": "в пределах территории одного муниципального образования (численностью от 2 тыс. чел.) или одной внутригородской территории города федерального значения",
|
||||
"II": "выход за пределы территории одного муниципального образования (численностью от 2 тыс. чел.) или одной внутригородской территории города федерального значения, но не за пределы территории одного субъекта Российской Федерации или территории города федерального значения",
|
||||
"I": "выход за пределы территории одного субъекта Российской Федерации или территории города федерального значения"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "2б",
|
||||
"name": "Прекращение или нарушение функционирования объектов обеспечения жизнедеятельности населения, оцениваемые по количеству людей, условия жизнедеятельности которых могут быть нарушены (тыс. человек)",
|
||||
"values": {
|
||||
"III": "более или равно 2, но менее 1 000",
|
||||
"II": "более или равно 1 000, но менее 5 000",
|
||||
"I": "более или равно 5 000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "3а",
|
||||
"name": "Прекращение или нарушение функционирования объектов транспортной инфраструктуры, организаций, осуществляющих деятельность в области грузовых и пассажирских перевозок, транспортных средств, в том числе высокоавтоматизированных транспортных средств, оцениваемые на территории, на которой возможно нарушение транспортного сообщения или предоставления транспортных услуг (пассажирских и грузовых перевозок)",
|
||||
"values": {
|
||||
"III": "в пределах территории одного муниципального образования (численностью от 2 тыс. человек) или одной внутригородской территории города федерального значения",
|
||||
"II": "выход за пределы территории одного муниципального образования (численностью от 2 тыс. человек) или одной внутригородской территории города федерального значения, но не за пределы территории одного субъекта Российской Федерации или территории города федерального значения",
|
||||
"I": "выход за пределы территории одного субъекта Российской Федерации или территории города федерального значения"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "3б",
|
||||
"name": "Прекращение или нарушение функционирования объектов транспортной инфраструктуры, оцениваемые по количеству людей, для которых могут быть недоступны транспортные услуги (тыс. человек)",
|
||||
"values": {
|
||||
"III": "более или равно 2, но менее 1000",
|
||||
"II": "более или равно 1000, но менее 5000",
|
||||
"I": "более или равно 5000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "3в",
|
||||
"name": "Прекращение или нарушение функционирования объектов транспортной инфраструктуры, оцениваемые по типам нарушенных перевозок и грузов",
|
||||
"values": {
|
||||
"III": "мультимодальные и интермодальные перевозки",
|
||||
"II": "перевозка, погрузка, разгрузка, хранение опасных грузов, на перевозку которых требуется специальное разрешение, и (или) грузов повышенной опасности в пределах территории одного субъекта Российской Федерации",
|
||||
"I": "перевозка, погрузка, разгрузка, хранение опасных грузов, на перевозку которых требуется специальное разрешение, и (или) грузов повышенной опасности в пределах территорий 2 и более субъектов Российской Федерации"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "3г",
|
||||
"name": "Прекращение или нарушение функционирования объектов транспортной инфраструктуры, оцениваемые по категорированию объектов транспортной инфраструктуры",
|
||||
"values": {
|
||||
"III": "объект транспортной инфраструктуры 3-й или 4-й категории",
|
||||
"II": "объект транспортной инфраструктуры 2-й категории",
|
||||
"I": "объект транспортной инфраструктуры 1-й категории"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "4а",
|
||||
"name": "Прекращение или нарушение функционирования сети связи, оцениваемые по количеству абонентов, для которых могут быть недоступны услуги связи (тыс. человек)",
|
||||
"values": {
|
||||
"III": "более или равно 3, но менее 1 000",
|
||||
"II": "более или равно 1 000, но менее 5 000",
|
||||
"I": "более или равно 5 000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "4б",
|
||||
"name": "Прекращение или нарушение функционирования сети связи, оцениваемые по уровню органа государственной власти (количеству органов) или организации, для которых могут быть нарушены или недоступны услуги связи",
|
||||
"values": {
|
||||
"III": "орган государственной власти субъекта Российской Федерации",
|
||||
"II": "федеральный орган государственной власти, государственная корпорация, Центральный банк Российской Федерации, 2 и более органа государственной власти субъекта Российской Федерации",
|
||||
"I": "Администрация Президента Российской Федерации, Правительство Российской Федерации, Федеральное Собрание Российской Федерации, Совет Безопасности Российской Федерации, Верховный Суд Российской Федерации, Конституционный Суд Российской Федерации, 2 и более федеральных органа государственной власти"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "5а",
|
||||
"name": "Отсутствие доступа к государственной услуге, оцениваемое в максимальном допустимом времени, в течение которого государственная услуга может быть недоступна для получателей такой услуги (часов)",
|
||||
"values": {
|
||||
"III": "менее или равно 24, но более 12",
|
||||
"II": "менее или равно 12, но более 6",
|
||||
"I": "менее или равно 6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "5б",
|
||||
"name": "Отсутствие доступа к государственной услуге, оцениваемое во времени с момента приема запроса о предоставлении государственной услуги органом, предоставляющим государственную услугу, или подведомственной государственному органу организацией, участвующей в предоставлении государственной услуги, в течение которого государственная услуга не может быть оказана (в процентах от времени предоставления услуги, предусмотренного административным регламентом)",
|
||||
"values": {
|
||||
"III": "менее или равно 30",
|
||||
"II": "более 30, но менее или равно 70",
|
||||
"I": "более 70"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"criteria": "II. Политическая значимость",
|
||||
"indicators": [
|
||||
{
|
||||
"num": "6",
|
||||
"name": "Прекращение или нарушение функционирования государственного органа или организации, созданной на основании федерального закона, в части невыполнения возложенной на них функции (полномочия)",
|
||||
"values": {
|
||||
"III": "прекращение или нарушение функционирования органа государственной власти субъекта Российской Федерации",
|
||||
"II": "прекращение или нарушение функционирования федерального органа государственной власти, государственной корпорации, Центрального банка Российской Федерации, 2 и более органов государственной власти субъекта Российской Федерации",
|
||||
"I": "прекращение или нарушение функционирования Администрации Президента Российской Федерации, Правительства Российской Федерации, Федерального Собрания Российской Федерации, Совета Безопасности Российской Федерации, Верховного Суда Российской Федерации, Конституционного Суда Российской Федерации, 2 и более федеральных органов государственной власти"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "7",
|
||||
"name": "Нарушение условий международного договора Российской Федерации, срыв переговоров или подписания планируемого к заключению международного договора Российской Федерации, оцениваемые по уровню международного договора Российской Федерации",
|
||||
"values": {
|
||||
"III": "нарушение условий договора межведомственного характера (срыв переговоров или подписания)",
|
||||
"II": "нарушение условий межправительственного договора (срыв переговоров или подписания)",
|
||||
"I": "нарушение условий межгосударственного договора (срыв переговоров или подписания)"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"criteria": "III. Экономическая значимость",
|
||||
"indicators": [
|
||||
{
|
||||
"num": "8",
|
||||
"name": "Возникновение ущерба субъекту критической информационной инфраструктуры, который является государственной корпорацией, государственным унитарным предприятием, государственной компанией, организацией оборонно-промышленного комплекса, стратегическим акционерным обществом, стратегическим предприятием, оцениваемого в снижении уровня дохода (с учетом налога на добавленную стоимость, акцизов и иных обязательных платежей) по всем видам деятельности (процентов от годового объема доходов, усредненного за прошедший пятилетний период)",
|
||||
"values": {
|
||||
"III": "более или равно 1, но менее или равно 10",
|
||||
"II": "более 10, но менее или равно 20",
|
||||
"I": "более 20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "9",
|
||||
"name": "Возникновение ущерба бюджету Российской Федерации, оцениваемого в снижении выплат (отчислений) в бюджет, осуществляемых субъектом критической информационной инфраструктуры (процентов прогнозируемого годового дохода федерального бюджета, усредненного за планируемый 3-летний период)",
|
||||
"values": {
|
||||
"III": "более 0,0003, но менее или равно 0,0006",
|
||||
"II": "более 0,0006, но менее или равно 0,001",
|
||||
"I": "более 0,001"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10",
|
||||
"name": "Прекращение или нарушение проведения клиентами операций по осуществлению перевода денежных средств, осуществляемых субъектом критической информационной инфраструктуры, являющимся в соответствии с законодательством Российской Федерации оператором платформы цифрового рубля, системно значимой кредитной организацией, кредитной организацией, выполняющей функции оператора услуг платежной инфраструктуры системно и социально значимых платежных систем, кредитной организацией, значимой на рынке платежных услуг, оператором услуг платежной инфраструктуры, оказывающим услуги платежной инфраструктуры в рамках системно значимых платежных систем, оцениваемые среднедневным (по отношению к числу календарных дней в году) количеством осуществляемых операций (млн. единиц) (расчет осуществляется по итогам года, а для создаваемых объектов — на основе прогнозных значений)",
|
||||
"values": {
|
||||
"III": "менее или равно 70",
|
||||
"II": "более 70, но менее или равно 120",
|
||||
"I": "более 120"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.1",
|
||||
"name": "Прекращение или нарушение проведения операций по исполнению обязательств, осуществляемых субъектом критической информационной инфраструктуры, являющимся центральным контрагентом, среднедневной размер обязательств которого по передаче денежных средств в валюте Российской Федерации по итогам клиринга за последние 12 месяцев (трлн. рублей)",
|
||||
"values": {
|
||||
"III": "менее 1",
|
||||
"II": "более или равно 1, но менее 10",
|
||||
"I": "более или равно 10"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.2",
|
||||
"name": "Прекращение или нарушение проведения учетно-расчетных операций, осуществляемых субъектом критической информационной инфраструктуры, являющимся центральным депозитарием и регистратором финансовых транзакций, среднедневное количество ценных бумаг (ISIN) российских эмитентов, которые учитывались на счетах в центральном депозитарии (оцениваемые за последние 12 месяцев в тыс. штук)",
|
||||
"values": {
|
||||
"III": "менее 10",
|
||||
"II": "более или равно 10, но менее 25",
|
||||
"I": "более или равно 25"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.3",
|
||||
"name": "Прекращение или нарушение проведения операций по выплатам, передаче и размещению денежных средств, осуществляемых субъектом критической информационной инфраструктуры, являющимся негосударственным пенсионным фондом, которые оцениваются объемом активов (суммой пенсионных накоплений и пенсионных резервов) негосударственного пенсионного фонда (млрд. рублей)",
|
||||
"values": {
|
||||
"III": "более или равно 50, но менее 1000",
|
||||
"II": "более или равно 1000, но менее 2000",
|
||||
"I": "более или равно 2000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.4",
|
||||
"name": "Прекращение или нарушение проведения операций по выплатам, перестрахованию, инвестициям, осуществляемых субъектом критической информационной инфраструктуры, являющимся страховой организацией, которые оцениваются объемом активов страховой организации (млрд. рублей)",
|
||||
"values": {
|
||||
"III": "более или равно 100, но менее 1500",
|
||||
"II": "более или равно 1500, но менее 5000",
|
||||
"I": "более или равно 5000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.5",
|
||||
"name": "Прекращение или нарушение выполнения функций по переводу денежных средств, осуществляемых субъектом критической информационной инфраструктуры, являющимся оператором услуг информационного обмена (некредитной организацией), который оценивается количеством заключенных договоров с кредитными организациями",
|
||||
"values": {
|
||||
"III": "более или равно 25, но менее 100",
|
||||
"II": "более или равно 100, но менее 150",
|
||||
"I": "более или равно 150"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.6",
|
||||
"name": "Прекращение или нарушение выполнения деятельности по предоставлению потребительских займов субъектом критической информационной инфраструктуры, являющимся микрофинансовой компанией, которые оцениваются суммой микрозаймов, выданных за годовой отчетный период (млрд. рублей)",
|
||||
"values": {
|
||||
"III": "более или равно 100, но менее 1000",
|
||||
"II": "более или равно 1000, но менее 5000",
|
||||
"I": "более или равно 5000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "10.7",
|
||||
"name": "Прекращение или нарушение предоставления пользователям кредитных историй и субъектам кредитных историй услуг, предусмотренных Федеральным законом «О кредитных историях», субъектом критической информационной инфраструктуры, являющимся бюро кредитных историй, которые оцениваются количеством кредитных историй, хранящихся в бюро кредитных историй (млн. единиц)",
|
||||
"values": {
|
||||
"III": "более или равно 30, но менее 200",
|
||||
"II": "более или равно 200, но менее 500",
|
||||
"I": "более или равно 500"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"criteria": "IV. Экологическая значимость",
|
||||
"indicators": [
|
||||
{
|
||||
"num": "11а",
|
||||
"name": "Вредные воздействия на окружающую среду, оцениваемые на территории, на которой окружающая среда может подвергнуться вредным воздействиям",
|
||||
"values": {
|
||||
"III": "в пределах территории одного муниципального образования (численностью от 2 тыс. чел.) или одной внутригородской территории города федерального значения, с выходом вредных воздействий за пределы территории субъекта критической информационной инфраструктуры",
|
||||
"II": "выход за пределы территории одного муниципального образования (численностью от 2 тыс. чел.) или одной внутригородской территории города федерального значения, но не за пределы территории одного субъекта Российской Федерации или территории города федерального значения, с выходом вредных воздействий за пределы территории субъекта критической информационной инфраструктуры",
|
||||
"I": "выход за пределы территории одного субъекта Российской Федерации или территории города федерального значения, с выходом вредных воздействий за пределы территории субъекта критической информационной инфраструктуры"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "11б",
|
||||
"name": "Вредные воздействия на окружающую среду, оцениваемые по количеству людей, которые могут быть подвержены вредным воздействиям (тыс. человек)",
|
||||
"values": {
|
||||
"III": "более или равно 2, но менее 1 000",
|
||||
"II": "более или равно 1 000, но менее 5 000",
|
||||
"I": "более или равно 5 000"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"criteria": "V. Значимость для обеспечения обороны страны, безопасности государства и правопорядка",
|
||||
"indicators": [
|
||||
{
|
||||
"num": "12",
|
||||
"name": "Прекращение или нарушение функционирования (невыполнение установленных показателей) пункта управления (ситуационного центра), оцениваемое в уровне (значимости) пункта управления или ситуационного центра",
|
||||
"values": {
|
||||
"III": "прекращение или нарушение функционирования пункта управления или ситуационного центра органа государственной власти субъекта Российской Федерации или города федерального значения",
|
||||
"II": "прекращение или нарушение функционирования пункта управления или ситуационного центра федерального органа государственной власти или государственной корпорации",
|
||||
"I": "прекращение или нарушение функционирования пункта управления государством или ситуационного центра Администрации Президента Российской Федерации, Правительства Российской Федерации, Федерального Собрания Российской Федерации, Совета Безопасности Российской Федерации, Верховного Суда Российской Федерации, Конституционного Суда Российской Федерации"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "13а",
|
||||
"name": "Снижение показателей государственного оборонного заказа, выполняемого (обеспечиваемого) субъектом критической информационной инфраструктуры, оцениваемое в снижении объемов продукции (работ, услуг) в заданный период времени (процентов заданного объема продукции)",
|
||||
"values": {
|
||||
"III": "более 0, но менее или равно 10",
|
||||
"II": "более 10, но менее или равно 15",
|
||||
"I": "более 15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "13б",
|
||||
"name": "Снижение показателей государственного оборонного заказа, выполняемого (обеспечиваемого) субъектом критической информационной инфраструктуры, оцениваемое в увеличении времени изготовления единицы продукции с заданным объемом (процентов установленного времени на изготовление единицы продукции)",
|
||||
"values": {
|
||||
"III": "более 0, но менее или равно 10",
|
||||
"II": "более 10, но менее или равно 40",
|
||||
"I": "более 40"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "13.1",
|
||||
"name": "Снижение показателей государственного оборонного заказа, выполняемого (обеспечиваемого) субъектом критической информационной инфраструктуры, оцениваемое по его статусу в кооперации головного исполнителя поставок продукции по государственному оборонному заказу",
|
||||
"values": {
|
||||
"III": "соисполнитель, субподрядчик или поставщик части продукции, работ и услуг в рамках контракта по государственному оборонному заказу совместно с исполнителем, заключившим контракт с головным исполнителем государственного оборонного заказа",
|
||||
"II": "исполнитель контракта по государственному оборонному заказу, заключивший контракт с головным исполнителем государственного оборонного заказа",
|
||||
"I": "головной исполнитель поставок продукции, работ и услуг по государственному оборонному заказу"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num": "14",
|
||||
"name": "Прекращение или нарушение функционирования (невыполнение установленных показателей) информационной системы в области обеспечения обороны страны, безопасности государства и правопорядка, оцениваемое в максимально допустимом времени, в течение которого информационная система может быть недоступна пользователю (часов)",
|
||||
"values": {
|
||||
"III": "менее или равно 4, но более 2",
|
||||
"II": "менее или равно 2, но более 1",
|
||||
"I": "менее или равно 1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,180 @@
|
||||
{
|
||||
"Здравоохранение": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Наука": [
|
||||
"1",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"11",
|
||||
"13"
|
||||
],
|
||||
"Транспорт": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"8",
|
||||
"9",
|
||||
"11",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Связь": [
|
||||
"4",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"Энергетика": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Государственная регистрация прав": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Банковская сфера и финансовый рынок": [
|
||||
"8",
|
||||
"9",
|
||||
"10"
|
||||
],
|
||||
"Топливно-энергетический комплекс": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Атомная энергетика": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"11",
|
||||
"12",
|
||||
"13"
|
||||
],
|
||||
"Оборонная промышленность": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"11",
|
||||
"12",
|
||||
"13"
|
||||
],
|
||||
"Ракетно-космическая промышленность": [
|
||||
"10"
|
||||
],
|
||||
"Горнодобывающая промышленность": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Металлургическая промышленность": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Химическая промышленность": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
],
|
||||
"Иная сфера деятельности": [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
"Социальная значимость — количество граждан, на которые может повлиять нарушение",
|
||||
"Политическая значимость — международный или внутриполитический резонанс",
|
||||
"Экономическая значимость — возможный размер ущерба (в рублях)",
|
||||
"Экологическая значимость — возможный ущерб окружающей среде",
|
||||
"Значимость для обороны страны, безопасности государства и правопорядка"
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Здравоохранение": {
|
||||
"1": "Рассматриваемый объект КИИ не участвует в обработке информации, необходимой для оказания медицинской помощи, проведения медицинских экспертиз, медицинских осмотров и медицинских освидетельствований, санитарно-противоэпидемических (профилактических) мероприятий и не осуществляет управление, контроль или мониторинг данных бизнес-процессов. Объект является вспомогательной информационной системой, целью которой является автоматизация процессов ведения бухгалтерского учета. Объект не осуществляет мониторинг, контроль или управление другими объектами КИИ, обеспечивающими жизненно важные функции организма. Утрата или иное повреждение объекта КИИ не может повлиять на жизненно важные функции организма.",
|
||||
"2": "Объект КИИ не относится к объектам обеспечения жизнедеятельности населения, в том числе объектам водоснабжения и канализации, очистки сточных вод, тепло- и электроснабжения, гидротехнических сооружений.",
|
||||
"3": "Объекты КИИ организации сферы здравоохранения не относятся к объектам транспортной инфраструктуры, и субъект КИИ не имеет в своем составе подразделения транспортной инфраструктуры.",
|
||||
"4": "Субъект КИИ не является оператором связи, объекты КИИ организации сферы здравоохранения не относятся к объектам транспортной инфраструктуры.",
|
||||
"5": "Организация сферы здравоохранения является юридическим лицом, обеспечивающим эксплуатацию информационной системы, задействованной в процессе оказания государственной услуги, однако нарушение функционирования информационной системы (рассматриваемого объекта КИИ), не участвующей в обработке информации, необходимой для оказания государственной услуги, не влечет за собой нарушение и (или) прекращение доступа к государственной услуге. Рассматриваемый объект КИИ не участвует в обработке информации, необходимой для оказания государственной услуги.",
|
||||
"6": "Организация сферы здравоохранения не является органом государственной власти, в связи с этим критерий неприменим.",
|
||||
"7": "Организация сферы здравоохранения не является субъектом политической сферы деятельности, в связи с этим критерий неприменим.",
|
||||
"8": "Организация сферы здравоохранения не является государственной корпорацией, государственным унитарным предприятием, государственной компанией, стратегическим акционерным обществом, стратегическим предприятием, в связи с этим критерий неприменим.",
|
||||
"9": "Объект КИИ задействован в предоставлении медицинских услуг. Показатель 9 применим ко всем субъектам КИИ; при расчёте учитывается, что медицинские услуги освобождены от налогообложения (значение показателя может быть минимальным).",
|
||||
"10": "Организация сферы здравоохранения не является субъектом банковской сферы деятельности.",
|
||||
"10.1": "Организация сферы здравоохранения не является центральным контрагентом.",
|
||||
"10.2": "Организация сферы здравоохранения не является центральным депозитарием.",
|
||||
"10.3": "Организация сферы здравоохранения не является негосударственным пенсионным фондом.",
|
||||
"10.4": "Организация сферы здравоохранения не является страховой организацией.",
|
||||
"10.5": "Организация сферы здравоохранения не является оператором услуг информационного обмена.",
|
||||
"11": "Объекты КИИ организации сферы здравоохранения не относятся к опасным производственным объектам в соответствии с законодательством Российской Федерации.",
|
||||
"12": "Организация сферы здравоохранения не выполняет функции пункта управления (ситуационного центра) государственного органа власти или государственной корпорации.",
|
||||
"13": "Организация сферы здравоохранения не является субъектом обеспечения обороны страны, безопасности государства и правопорядка.",
|
||||
"14": "Организация сферы здравоохранения не имеет информационных систем, функционирующих в области обеспечения обороны страны, безопасности государства и правопорядка."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
[
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Центральному федеральному округу",
|
||||
"address": "117342, г. Москва, Севастопольский проспект, д. 56/40, стр. 1",
|
||||
"cities": [
|
||||
"москва",
|
||||
"белгород",
|
||||
"брянск",
|
||||
"владимир",
|
||||
"воронеж",
|
||||
"иваново",
|
||||
"калуга",
|
||||
"кострома",
|
||||
"курск",
|
||||
"липецк",
|
||||
"орёл",
|
||||
"орел",
|
||||
"рязань",
|
||||
"смоленск",
|
||||
"тамбов",
|
||||
"тверь",
|
||||
"тула",
|
||||
"ярославль",
|
||||
"подольск",
|
||||
"балашиха",
|
||||
"химки",
|
||||
"мытищи",
|
||||
"королёв",
|
||||
"люберцы",
|
||||
"одинцово",
|
||||
"домодедово",
|
||||
"серпухов",
|
||||
"электросталь",
|
||||
"сергиев посад",
|
||||
"коломна"
|
||||
],
|
||||
"regions": [
|
||||
"московская область",
|
||||
"белгородская область",
|
||||
"брянская область",
|
||||
"владимирская область",
|
||||
"воронежская область",
|
||||
"ивановская область",
|
||||
"калужская область",
|
||||
"костромская область",
|
||||
"курская область",
|
||||
"липецкая область",
|
||||
"орловская область",
|
||||
"рязанская область",
|
||||
"смоленская область",
|
||||
"тамбовская область",
|
||||
"тверская область",
|
||||
"тульская область",
|
||||
"ярославская область"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Северо-Западному федеральному округу",
|
||||
"address": "190121, г. Санкт-Петербург, Исаакиевская площадь, д. 11, лит. А",
|
||||
"cities": [
|
||||
"санкт-петербург",
|
||||
"петербург",
|
||||
"архангельск",
|
||||
"вологда",
|
||||
"калининград",
|
||||
"мурманск",
|
||||
"великий новгород",
|
||||
"новгород",
|
||||
"псков",
|
||||
"петрозаводск",
|
||||
"сыктывкар",
|
||||
"нарьян-мар",
|
||||
"северодвинск",
|
||||
"череповец",
|
||||
"гатчина"
|
||||
],
|
||||
"regions": [
|
||||
"ленинградская область",
|
||||
"архангельская область",
|
||||
"вологодская область",
|
||||
"калининградская область",
|
||||
"мурманская область",
|
||||
"новгородская область",
|
||||
"псковская область",
|
||||
"республика карелия",
|
||||
"республика коми",
|
||||
"ненецкий"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Южному и Северо-Кавказскому федеральным округам",
|
||||
"address": "344079, г. Ростов-на-Дону, ул. Ярослава Галана, д. 1Е/25",
|
||||
"cities": [
|
||||
"ростов-на-дону",
|
||||
"ростов",
|
||||
"астрахань",
|
||||
"волгоград",
|
||||
"краснодар",
|
||||
"майкоп",
|
||||
"элиста",
|
||||
"симферополь",
|
||||
"севастополь",
|
||||
"сочи",
|
||||
"новороссийск",
|
||||
"таганрог",
|
||||
"шахты",
|
||||
"волжский",
|
||||
"камышин",
|
||||
"махачкала",
|
||||
"грозный",
|
||||
"владикавказ",
|
||||
"нальчик",
|
||||
"черкесск",
|
||||
"магас",
|
||||
"ставрополь",
|
||||
"пятигорск",
|
||||
"кисловодск",
|
||||
"ессентуки",
|
||||
"минеральные воды",
|
||||
"назрань"
|
||||
],
|
||||
"regions": [
|
||||
"ростовская область",
|
||||
"астраханская область",
|
||||
"волгоградская область",
|
||||
"краснодарский край",
|
||||
"республика адыгея",
|
||||
"республика калмыкия",
|
||||
"республика крым",
|
||||
"севастополь",
|
||||
"республика дагестан",
|
||||
"чеченская республика",
|
||||
"республика северная осетия",
|
||||
"кабардино-балкарская республика",
|
||||
"карачаево-черкесская республика",
|
||||
"республика ингушетия",
|
||||
"ставропольский край"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Приволжскому федеральному округу",
|
||||
"address": "603104, г. Нижний Новгород, проспект Гагарина, д. 60, корп. 11",
|
||||
"cities": [
|
||||
"нижний новгород",
|
||||
"казань",
|
||||
"самара",
|
||||
"саратов",
|
||||
"пенза",
|
||||
"пермь",
|
||||
"уфа",
|
||||
"ижевск",
|
||||
"оренбург",
|
||||
"ульяновск",
|
||||
"киров",
|
||||
"саранск",
|
||||
"чебоксары",
|
||||
"йошкар-ола",
|
||||
"тольятти",
|
||||
"набережные челны",
|
||||
"арзамас",
|
||||
"дзержинск",
|
||||
"балаково",
|
||||
"новокуйбышевск",
|
||||
"сызрань"
|
||||
],
|
||||
"regions": [
|
||||
"нижегородская область",
|
||||
"республика татарстан",
|
||||
"самарская область",
|
||||
"саратовская область",
|
||||
"пензенская область",
|
||||
"пермский край",
|
||||
"республика башкортостан",
|
||||
"удмуртская республика",
|
||||
"оренбургская область",
|
||||
"ульяновская область",
|
||||
"кировская область",
|
||||
"республика мордовия",
|
||||
"чувашская республика",
|
||||
"республика марий эл"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Уральскому федеральному округу",
|
||||
"address": "620078, г. Екатеринбург, ул. Гагарина, д. 28Б",
|
||||
"cities": [
|
||||
"екатеринбург",
|
||||
"челябинск",
|
||||
"тюмень",
|
||||
"курган",
|
||||
"ханты-мансийск",
|
||||
"салехард",
|
||||
"сургут",
|
||||
"нижневартовск",
|
||||
"магнитогорск",
|
||||
"нижний тагил",
|
||||
"каменск-уральский",
|
||||
"первоуральск",
|
||||
"миасс",
|
||||
"зубовой"
|
||||
],
|
||||
"regions": [
|
||||
"свердловская область",
|
||||
"челябинская область",
|
||||
"тюменская область",
|
||||
"курганская область",
|
||||
"ханты-мансийский",
|
||||
"ямало-ненецкий"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Сибирскому федеральному округу",
|
||||
"address": "630091, г. Новосибирск, Красный проспект, д. 41",
|
||||
"cities": [
|
||||
"новосибирск",
|
||||
"омск",
|
||||
"томск",
|
||||
"кемерово",
|
||||
"барнаул",
|
||||
"красноярск",
|
||||
"иркутск",
|
||||
"новокузнецк",
|
||||
"абакан",
|
||||
"горно-алтайск",
|
||||
"кызыл",
|
||||
"улан-удэ",
|
||||
"чита",
|
||||
"ангарск",
|
||||
"братск",
|
||||
"прокопьевск",
|
||||
"бийск",
|
||||
"рубцовск",
|
||||
"бердск",
|
||||
"северск"
|
||||
],
|
||||
"regions": [
|
||||
"новосибирская область",
|
||||
"омская область",
|
||||
"томская область",
|
||||
"кемеровская область",
|
||||
"алтайский край",
|
||||
"красноярский край",
|
||||
"иркутская область",
|
||||
"республика алтай",
|
||||
"республика тыва",
|
||||
"республика хакасия",
|
||||
"республика бурятия",
|
||||
"забайкальский край"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Управление ФСТЭК России по Дальневосточному федеральному округу",
|
||||
"address": "680030, г. Хабаровск, ул. Ленина, д. 37",
|
||||
"cities": [
|
||||
"хабаровск",
|
||||
"владивосток",
|
||||
"благовещенск",
|
||||
"якутск",
|
||||
"петропавловск-камчатский",
|
||||
"южно-сахалинск",
|
||||
"магадан",
|
||||
"биробиджан",
|
||||
"анадырь",
|
||||
"комсомольск-на-амуре",
|
||||
"уссурийск",
|
||||
"находка",
|
||||
"артём",
|
||||
"белогорск",
|
||||
"свободный"
|
||||
],
|
||||
"regions": [
|
||||
"хабаровский край",
|
||||
"приморский край",
|
||||
"амурская область",
|
||||
"республика саха",
|
||||
"камчатский край",
|
||||
"сахалинская область",
|
||||
"магаданская область",
|
||||
"еврейская автономная область",
|
||||
"чукотский"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
[
|
||||
"Сервер (уточнить модель)",
|
||||
"АРМ пользователя (уточнить конфигурацию)",
|
||||
"Ноутбук (уточнить модель)",
|
||||
"Коммутатор (уточнить модель)",
|
||||
"Маршрутизатор (уточнить модель)",
|
||||
"Межсетевой экран (уточнить модель)",
|
||||
"Видеорегистратор (уточнить модель)",
|
||||
"Видеокамера (уточнить модель)",
|
||||
"Контроллер доступа СКУД (уточнить модель)",
|
||||
"Шлагбаум (уточнить модель)",
|
||||
"Турникет (уточнить модель)",
|
||||
"Сетевое хранилище (NAS) (уточнить модель)",
|
||||
"Беспроводная точка доступа (уточнить модель)",
|
||||
"ИБП (уточнить модель)"
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
"отказ в обслуживании",
|
||||
"несанкционированный доступ",
|
||||
"утечка данных (нарушение конфиденциальности)",
|
||||
"модификация (подмена) данных",
|
||||
"нарушение функционирования технических средств",
|
||||
"несанкционированное использование вычислительных ресурсов объекта"
|
||||
]
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
"общего пользования",
|
||||
"выделенная",
|
||||
"технологическая",
|
||||
"присоединенная к сети связи общего пользования",
|
||||
"специального назначения",
|
||||
"другая сеть связи для передачи информации при помощи электромагнитных систем",
|
||||
"взаимодействие с сетями электросвязи отсутствует"
|
||||
]
|
||||
@@ -0,0 +1,6 @@
|
||||
[
|
||||
"проводной",
|
||||
"беспроводной",
|
||||
"Проводной",
|
||||
"Без проводной"
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
"передача (прием) информации",
|
||||
"оказание услуг",
|
||||
"управление",
|
||||
"контроль за технологическим производственным оборудованием (исполнительными устройствами)",
|
||||
"иная цель"
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
"Информационная система",
|
||||
"Автоматизированная система управления",
|
||||
"Информационно-телекоммуникационная сеть",
|
||||
"Автоматизированные системы управления",
|
||||
"Информационно-телекоммуникационные сети"
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
[
|
||||
"05.20",
|
||||
"06.1",
|
||||
"06.2",
|
||||
"09.90",
|
||||
"19.20",
|
||||
"20.13",
|
||||
"22.21",
|
||||
"24.46",
|
||||
"25.30",
|
||||
"25.40",
|
||||
"25.99",
|
||||
"26.20",
|
||||
"26.30",
|
||||
"26.51",
|
||||
"27.90",
|
||||
"28.13",
|
||||
"28.14",
|
||||
"28.99",
|
||||
"30.11",
|
||||
"33.11",
|
||||
"33.12",
|
||||
"33.20",
|
||||
"35.11",
|
||||
"35.12",
|
||||
"35.13",
|
||||
"35.14",
|
||||
"35.3",
|
||||
"35.30",
|
||||
"38.12",
|
||||
"38.22",
|
||||
"41.20",
|
||||
"45.20",
|
||||
"46.46",
|
||||
"47.73",
|
||||
"49",
|
||||
"49.10",
|
||||
"49.20",
|
||||
"49.32",
|
||||
"49.39",
|
||||
"49.4",
|
||||
"49.41",
|
||||
"49.42",
|
||||
"49.50",
|
||||
"50",
|
||||
"50.20",
|
||||
"50.30",
|
||||
"50.40",
|
||||
"51",
|
||||
"51.10",
|
||||
"51.21",
|
||||
"52",
|
||||
"52.10",
|
||||
"52.2",
|
||||
"52.22",
|
||||
"52.23",
|
||||
"52.24",
|
||||
"61",
|
||||
"61.1",
|
||||
"61.20",
|
||||
"61.3",
|
||||
"62.01",
|
||||
"62.09",
|
||||
"63",
|
||||
"63.11",
|
||||
"64",
|
||||
"64.11",
|
||||
"71.12",
|
||||
"71.20",
|
||||
"72",
|
||||
"72.19",
|
||||
"74.90",
|
||||
"84",
|
||||
"84.11",
|
||||
"84.13",
|
||||
"84.30",
|
||||
"86.1",
|
||||
"86.2",
|
||||
"86.90"
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
"Установлены границы контролируемой зоны.",
|
||||
"Физическая охрана периметра обеспечена бетонным забором с колючей проволокой, исключающим прямой доступ к зданиям, и турникетом на проходной; въезд и вход осуществляются по пропускам с применением СКУД, ведется учет посетителей.",
|
||||
"Имеется система видеонаблюдения.",
|
||||
"Здания не имеют окон, выходящих во вне, входная группа оснащена металлической дверью.",
|
||||
"Установлены охранно-пожарная сигнализация и тревожная кнопка.",
|
||||
"Исключена возможность неконтролируемого пребывания посторонних лиц в помещениях, где ведется работа с объектами критической информационной инфраструктуры.",
|
||||
"Назначены: ответственный за организацию обеспечения безопасности объектов критической информационной инфраструктуры, администратор и администратор безопасности объектов критической информационной инфраструктуры, утверждены инструкции, регламентирующие работу с данными и объектами критической информационной инфраструктуры.",
|
||||
"Осуществляется внутренний контроль соответствия обеспечения безопасности объектов критической информационной инфраструктуры установленным требованиям.",
|
||||
"Работники, непосредственно осуществляющие работу с объектами критической информационной инфраструктуры, ознакомлены с положениями законодательства Российской Федерации об объектах критической информационной инфраструктуры, документами, определяющими политику организации в отношении работы с объектами критической информационной инфраструктуры.",
|
||||
"Обеспечивается учет машинных носителей объектов критической информационной инфраструктуры.",
|
||||
"Обеспечивается резервирование и восстановление данных, модифицированных или уничтоженных вследствие несанкционированного доступа к ним.",
|
||||
"Разработаны правила доступа к данным, обрабатываемым в объектах критической информационной инфраструктуры.",
|
||||
"Определён перечень сотрудников, осуществляющих работу с объектами критической информационной инфраструктуры.",
|
||||
"Ведётся учёт всех защищаемых носителей информации с помощью их маркировки и занесения учетных данных в журнал учета с отметкой об их выдаче (приеме).",
|
||||
"Обеспечено раздельное хранение материальных носителей, обработка которых осуществляется в различных целях."
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
[
|
||||
"Windows 10 Pro 64bit",
|
||||
"Windows 11 Pro",
|
||||
"Windows Server 2019",
|
||||
"Windows Server 2022",
|
||||
"Astra Linux Special Edition",
|
||||
"РЕД ОС",
|
||||
"Альт Рабочая станция",
|
||||
"Альт Сервер",
|
||||
"Ubuntu Server",
|
||||
"CentOS",
|
||||
"Debian",
|
||||
"MS-DOS (для АРМ специального назначения)",
|
||||
"Android (для планшетов/КПК)"
|
||||
]
|
||||
@@ -1,22 +0,0 @@
|
||||
[
|
||||
"Сбор персональных данных",
|
||||
"Запись персональных данных",
|
||||
"Систематизация персональных данных",
|
||||
"Накопление персональных данных",
|
||||
"Хранение персональных данных",
|
||||
"Уточнение (обновление, изменение) персональных данных",
|
||||
"Извлечение персональных данных",
|
||||
"Использование персональных данных",
|
||||
"Передача (распространение, предоставление, доступ) персональных данных",
|
||||
"Обезличивание персональных данных",
|
||||
"Блокирование персональных данных",
|
||||
"Удаление персональных данных",
|
||||
"Уничтожение персональных данных",
|
||||
"Трансграничная передача персональных данных",
|
||||
"Автоматизированная обработка персональных данных",
|
||||
"Неавтоматизированная обработка персональных данных",
|
||||
"Смешанная обработка персональных данных",
|
||||
"Формирование и ведение баз данных",
|
||||
"Передача данных третьим лицам",
|
||||
"Внутренняя передача данных между подразделениями"
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
[
|
||||
"работники оператора",
|
||||
"клиенты",
|
||||
"контрагенты",
|
||||
"родственники работников",
|
||||
"соискатели",
|
||||
"иные лица"
|
||||
]
|
||||
@@ -1,28 +0,0 @@
|
||||
[
|
||||
"Фамилия, имя, отчество",
|
||||
"Дата рождения",
|
||||
"Место рождения",
|
||||
"Паспортные данные",
|
||||
"Адрес регистрации",
|
||||
"Адрес проживания",
|
||||
"ИНН",
|
||||
"СНИЛС",
|
||||
"Номер телефона",
|
||||
"Адрес электронной почты",
|
||||
"Образование",
|
||||
"Профессия",
|
||||
"Сведения о доходах",
|
||||
"Семейное положение",
|
||||
"Состав семьи",
|
||||
"Состояние здоровья",
|
||||
"Национальность",
|
||||
"Гражданство",
|
||||
"Сведения о судимости",
|
||||
"Фотография",
|
||||
"Биометрические данные",
|
||||
"Рабочий e-mail",
|
||||
"Должность",
|
||||
"Табельный номер",
|
||||
"Сведения о воинском учете",
|
||||
"Сведения о командировках"
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"system": "Ты — юрист по защите персональных данных и критической информационной инфраструктуры (187-ФЗ). Проверь реквизиты организации.",
|
||||
"rules": [
|
||||
"Не выдумывай реквизиты — исправляй/дополняй только если уверен.",
|
||||
"В «полях» для каждого поля укажи статус: ok (заполнено корректно), пусто (не заполнено), ошибка (заполнено с ошибкой)."
|
||||
],
|
||||
"response_format": "Верни ТОЛЬКО JSON: {\"оценка\": \"ok|warning|error\", \"замечания\": [\"...\"], \"предложения\": {\"full_name\": \"...\", \"short_name\": \"...\", \"address\": \"...\", \"inn\": \"...\", \"kpp\": \"...\", \"ogrn\": \"...\", \"ogrn_date\": \"...\", \"chief_position\": \"...\", \"chief_fio\": \"...\"}, \"поля\": {\"full_name\": {\"статус\": \"ok|пусто|ошибка\", \"комментарий\": \"...\"}, ...}}"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"system": "Ты — эксперт по категорированию объектов критической информационной инфраструктуры (187-ФЗ, Постановление Правительства РФ № 127). Проверь корректность присвоенных категорий значимости по показателям критериев. Категория значимости объекта определяется по максимальной из категорий показателей: I — если хотя бы один показатель I; II — если есть II и нет I; III — если есть III и нет I/II; «—» — если показатели не присвоены. Учитывай сферу деятельности субъекта и применяемость показателей (показатель 8 — госкорпорации/ГУП/госкомпании/стратегические АО; показатель 9 — все субъекты; показатель 10 — финансовый рынок/банки; показатель 13 — оборонно-промышленный комплекс).",
|
||||
"response_format": "Верни ТОЛЬКО JSON: {\"оценка\": \"ok|warning|error\", \"замечания\": [{\"показатель\": \"номер\", \"категория\": \"текущая\", \"комментарий\": \"что не так и как должно быть\"}], \"предложения\": {\"criteria_values\": \"исправленный список показателей с категориями\", \"criteria_justification\": \"исправленное обоснование\"}, \"итоговая_категория\": \"I|II|III|—\"}. В «предложениях» — только если нужно что-то исправить, иначе пустые строки."
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"system": "Ты — эксперт по безопасности критической информационной инфраструктуры (187-ФЗ, приказ ФСТЭК России № 239 и № 236). Проверь заполненные сведения об объекте КИИ и дай рекомендации.",
|
||||
"response_format": "Верни ТОЛЬКО JSON: {\"оценка\": \"ok|warning|error\", \"замечания\": [\"...\"], \"предложения\": {\"attacker_category\": \"...\", \"threats\": \"...\", \"incidents\": \"...\", \"criteria_values\": \"...\", \"criteria_justification\": \"...\", \"org_measures\": \"...\", \"tech_measures\": \"...\"}}. В «предложениях» укажи, что добавить/исправить по каждому разделу, если нужно, иначе пустые строки."
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"system": "Ты — лингвист, специалист по склонению русских наименований организаций (187-ФЗ). Просклоняй название организации по падежам.",
|
||||
"rules": [
|
||||
"Склоняй только само название (включая организационно-правовую форму, если она склоняется по правилам русского языка).",
|
||||
"Сохраняй кавычки, регистр и пунктуацию оригинала.",
|
||||
"Не выдумывай падежи — используй стандартную парадигму: именительный, родительный, дательный, винительный, творительный, предложный."
|
||||
],
|
||||
"response_format": "Верни ТОЛЬКО JSON: {\"nominative\": \"...\", \"genitive\": \"...\", \"dative\": \"...\", \"accusative\": \"...\", \"instrumental\": \"...\", \"prepositional\": \"...\"}"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"system": "Ты — эксперт по безопасности критической информационной инфраструктуры (187-ФЗ, приказ ФСТЭК России № 239 и № 236). По категории значимости объекта КИИ, характеристикам объекта, актуальным угрозам и уже выбранным средствам защиты информации (СЗИ) определи, каких средств не хватает, и дай рекомендации. Обязательно учитывай следующее:\n1) КАТЕГОРИЯ ЗНАЧИМОСТИ: если категория «без категории», «—», пусто или не присвоена — объект НЕ является значимым объектом КИИ, полный набор СЗИ по приказу № 239 для него НЕ требуется. В этом случае рекомендую только минимальный базовый набор (антивирусная защита, резервное копирование, управление доступом) и главный совет — провести категорирование объекта. НЕ перечисляй средства для I категории (СКЗИ, SIEM, анти-APT, доверенная загрузка и т.п.), если категория не присвоена. Для I категории — максимальный набор, для II — расширенный, для III — базовый набор по приказу № 239.\n2) ХАРАКТЕРИСТИКИ ОБЪЕКТА: внимательно читай переданные характеристики (наличие интернета, архитектура, ПО, тип объекта). НЕ рекомендуй СЗИ для технологий, которых нет у объекта: нет интернета/внешних сетей → не рекомендуй DDoS-защиту, VPN, веб-защиту, защиту от удалённых атак из интернета; нет виртуализации/контейнеров → не рекомендуй защиту виртуальной инфраструктуры и контейнеров; нет мобильных устройств → не рекомендуй защиту мобильных; объект не АСУ ТП → не рекомендуй защиту АСУ ТП; нет гостайны → не рекомендуй средства для гостайны. Отмечай только реально применимые классы СЗИ.\n3) ОБЪЁМ: максимум 5–7 рекомендаций, только самые важные для данной категории и объекта. Если текущий набор СЗИ достаточен — верни пустой список рекомендаций и «достаточно»: true.\nВыбирай СЗИ ТОЛЬКО из реально существующих классов средств защиты (антивирусная защита, СКЗИ, межсетевой экран, SIEM, DLP, IDS/IPS, средства доверенной загрузки, контроля целостности, управления доступом и т.п.).",
|
||||
"response_format": "Верни ТОЛЬКО JSON: {\"рекомендации\": [{\"сзи\": \"название класса/средства\", \"зачем\": \"для чего нужно\"}], \"общие_советы\": [\"...\"], \"достаточно\": true|false}. Поле «достаточно» — true, если текущий набор СЗИ покрывает требования категории. Не придумывай конкретные торговые марки, если их нет в списке — указывай класс средства."
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"system": "Ты — эксперт ФСТЭК по категорированию объектов критической информационной инфраструктуры (187-ФЗ, ПП РФ № 127, Распоряжение ПРФ № 360-р, приказ ФСТЭК № 236). Твоя задача — по описанию объекта КИИ ПОДОБРАТЬ его характеристики ТОЛЬКО из предоставленных списков, ничего не выдумывая.",
|
||||
"rules": [
|
||||
"Выбирай значения ТОЛЬКО из предоставленных списков. Если подходящего значения в списке нет — верни пустую строку, не придумывай своё.",
|
||||
"Поле «типовой_объект»: выбери полную строку ТОЛЬКО из предоставленного перечня типовых отраслевых объектов (Распоряжение ПРФ № 360-р), наиболее подходящую. Не перефразируй, не сокращай — копируй строку дословно. Если ничего не подходит — пустая строка.",
|
||||
"Поле «сфера»: выбери ТОЛЬКО одну из предоставленных сфер. Не придумывай свои.",
|
||||
"Поле «тип»: выбери ТОЛЬКО один из предоставленных типов объектов.",
|
||||
"Поле «архитектура»: выбери ТОЛЬКО одну из предоставленных архитектур.",
|
||||
"Поле «назначение»: заполни строго по форме: «Объект предназначен для <что делает> с целью <зачем>.». Не отклоняйся от этой формы.",
|
||||
"Поле «обоснование»: 1-2 предложения, почему выбраны такие характеристики."
|
||||
],
|
||||
"response_format": "Верни ТОЛЬКО JSON без пояснений: {\"назначение\": \"...\", \"сфера\": \"...\", \"тип\": \"...\", \"архитектура\": \"...\", \"типовой_объект\": \"...\", \"обоснование\": \"...\"}"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"system": "Ты — эксперт ФСТЭК по безопасности критической информационной инфраструктуры (187-ФЗ, приказ ФСТЭК № 236). На основе характеристик объекта КИИ выбери из предоставленного перечня АКТУАЛЬНЫЕ угрозы безопасности информации.",
|
||||
"rules": [
|
||||
"Выбирай угрозы ТОЛЬКО из предоставленного перечня — не выдумывай и не перефразируй, используй полные строки (код + название) как они есть.",
|
||||
"Верни от 10 до 40 угроз.",
|
||||
"Учитывай характеристики объекта: тип (ИС/АСУ/ИТКС), наличие интернета, сферу, категорию нарушителя."
|
||||
],
|
||||
"response_format": "Верни ТОЛЬКО JSON: {\"threats\": [\"УБИ.xxx Название угрозы\", ...]} — без пояснений и комментариев."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
"Здравоохранение",
|
||||
"Наука",
|
||||
"Транспорт",
|
||||
"Связь",
|
||||
"Энергетика",
|
||||
"Государственная регистрация прав",
|
||||
"Банковская сфера и финансовый рынок",
|
||||
"Топливно-энергетический комплекс",
|
||||
"Атомная энергетика",
|
||||
"Оборонная промышленность",
|
||||
"Ракетно-космическая промышленность",
|
||||
"Горнодобывающая промышленность",
|
||||
"Металлургическая промышленность",
|
||||
"Химическая промышленность",
|
||||
"Иная сфера деятельности"
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
"Идентификация и аутентификация пользователей",
|
||||
"Управление доступом",
|
||||
"Ограничение программной среды",
|
||||
"Антивирусная защита",
|
||||
"Защита информации от несанкционированного доступа (СЗИ от НСД)",
|
||||
"Регистрация и мониторинг событий безопасности",
|
||||
"Межсетевое экранирование",
|
||||
"Обнаружение вторжений (СОВ)",
|
||||
"Криптографическая защита информации (СКЗИ)",
|
||||
"Защита среды виртуализации",
|
||||
"Резервное копирование и восстановление",
|
||||
"Защита технических средств и линий связи"
|
||||
]
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
"Угроза несанкционированного доступа к информационным ресурсам объекта",
|
||||
"Угроза нарушения функционирования объекта (отказ в обслуживании)",
|
||||
"Угроза утечки (нарушения конфиденциальности) информации",
|
||||
"Угроза модификации (подмены) данных",
|
||||
"Угроза внедрения вредоносного кода",
|
||||
"Угроза несанкционированного использования вычислительных ресурсов",
|
||||
"Угроза перехвата (прослушивания) сетевого трафика",
|
||||
"Угроза социальной инженерии",
|
||||
"Угроза использования уязвимостей программного обеспечения",
|
||||
"Угроза физического воздействия на технические средства"
|
||||
]
|
||||
@@ -0,0 +1,229 @@
|
||||
[
|
||||
"УБИ.001 Угроза автоматического распространения вредоносного кода в грид-системе",
|
||||
"УБИ.002 Угроза агрегирования данных, передаваемых в грид-системе",
|
||||
"УБИ.003 Угроза использования слабостей криптографических алгоритмов и уязвимостей в программном обеспечении их реализации",
|
||||
"УБИ.004 Угроза аппаратного сброса пароля BIOS",
|
||||
"УБИ.005 Угроза внедрения вредоносного кода в BIOS",
|
||||
"УБИ.006 Угроза внедрения кода или данных",
|
||||
"УБИ.007 Угроза воздействия на программы с высокими привилегиями",
|
||||
"УБИ.008 Угроза восстановления и/или повторного использования аутентификационной информации",
|
||||
"УБИ.009 Угроза восстановления предыдущей уязвимой версии BIOS",
|
||||
"УБИ.010 Угроза выхода процесса за пределы виртуальной машины",
|
||||
"УБИ.011 Угроза деавторизации санкционированного клиента беспроводной сети",
|
||||
"УБИ.012 Угроза деструктивного изменения конфигурации/среды окружения программ",
|
||||
"УБИ.013 Угроза деструктивного использования декларированного функционала BIOS",
|
||||
"УБИ.014 Угроза длительного удержания вычислительных ресурсов пользователями",
|
||||
"УБИ.015 Угроза доступа к защищаемым файлам с использованием обходного пути",
|
||||
"УБИ.016 Угроза доступа к локальным файлам сервера при помощи URL",
|
||||
"УБИ.017 Угроза доступа/перехвата/изменения HTTP cookies",
|
||||
"УБИ.018 Угроза загрузки нештатной операционной системы",
|
||||
"УБИ.019 Угроза заражения DNS-кеша",
|
||||
"УБИ.020 Угроза злоупотребления возможностями, предоставленными потребителям облачных услуг",
|
||||
"УБИ.021 Угроза злоупотребления доверием потребителей облачных услуг",
|
||||
"УБИ.022 Угроза избыточного выделения оперативной памяти",
|
||||
"УБИ.023 Угроза изменения компонентов информационной (автоматизированной) системы",
|
||||
"УБИ.024 Угроза изменения режимов работы аппаратных элементов компьютера",
|
||||
"УБИ.025 Угроза изменения системных и глобальных переменных",
|
||||
"УБИ.026 Угроза искажения XML-схемы",
|
||||
"УБИ.027 Угроза искажения вводимой и выводимой на периферийные устройства информации",
|
||||
"УБИ.028 Угроза использования альтернативных путей доступа к ресурсам",
|
||||
"УБИ.029 Угроза использования вычислительных ресурсов суперкомпьютера «паразитными» процессами",
|
||||
"УБИ.030 Угроза использования информации идентификации/аутентификации, заданной по умолчанию",
|
||||
"УБИ.031 Угроза использования механизмов авторизации для повышения привилегий",
|
||||
"УБИ.032 Угроза использования поддельных цифровых подписей BIOS",
|
||||
"УБИ.033 Угроза использования слабостей кодирования входных данных",
|
||||
"УБИ.034 Угроза использования слабостей протоколов сетевого/локального обмена данными",
|
||||
"УБИ.035 Угроза использования слабых криптографических алгоритмов BIOS",
|
||||
"УБИ.036 Угроза исследования механизмов работы программы",
|
||||
"УБИ.037 Угроза исследования приложения через отчёты об ошибках",
|
||||
"УБИ.038 Угроза исчерпания вычислительных ресурсов хранилища больших данных",
|
||||
"УБИ.039 Угроза исчерпания запаса ключей, необходимых для обновления BIOS",
|
||||
"УБИ.040 Угроза конфликта юрисдикций различных стран",
|
||||
"УБИ.041 Угроза межсайтового скриптинга",
|
||||
"УБИ.042 Угроза межсайтовой подделки запроса",
|
||||
"УБИ.043 Угроза нарушения доступности облачного сервера",
|
||||
"УБИ.044 Угроза нарушения изоляции пользовательских данных внутри виртуальной машины",
|
||||
"УБИ.045 Угроза нарушения изоляции среды исполнения BIOS",
|
||||
"УБИ.046 Угроза нарушения процедуры аутентификации субъектов виртуального информационного взаимодействия",
|
||||
"УБИ.047 Угроза нарушения работоспособности грид-системы при нетипичной сетевой нагрузке",
|
||||
"УБИ.048 Угроза нарушения технологии обработки информации путём несанкционированного внесения изменений в образы виртуальных машин",
|
||||
"УБИ.049 Угроза нарушения целостности данных кеша",
|
||||
"УБИ.050 Угроза неверного определения формата входных данных, поступающих в хранилище больших данных",
|
||||
"УБИ.051 Угроза невозможности восстановления сессии работы на ПЭВМ при выводе из промежуточных состояний питания",
|
||||
"УБИ.052 Угроза невозможности миграции образов виртуальных машин из-за несовместимости аппаратного и программного обеспечения",
|
||||
"УБИ.053 Угроза невозможности управления правами пользователей BIOS",
|
||||
"УБИ.054 Угроза недобросовестного исполнения обязательств поставщиками облачных услуг",
|
||||
"УБИ.055 Угроза незащищённого администрирования облачных услуг",
|
||||
"УБИ.056 Угроза некачественного переноса инфраструктуры в облако",
|
||||
"УБИ.057 Угроза неконтролируемого копирования данных внутри хранилища больших данных",
|
||||
"УБИ.058 Угроза неконтролируемого роста числа виртуальных машин",
|
||||
"УБИ.059 Угроза неконтролируемого роста числа зарезервированных вычислительных ресурсов",
|
||||
"УБИ.060 Угроза неконтролируемого уничтожения информации хранилищем больших данных",
|
||||
"УБИ.061 Угроза некорректного задания структуры данных транзакции",
|
||||
"УБИ.062 Угроза некорректного использования прозрачного прокси-сервера за счёт плагинов браузера",
|
||||
"УБИ.063 Угроза некорректного использования функционала программного и аппаратного обеспечения",
|
||||
"УБИ.064 Угроза некорректной реализации политики лицензирования в облаке",
|
||||
"УБИ.065 Угроза неопределённости в распределении ответственности между ролями в облаке",
|
||||
"УБИ.066 Угроза неопределённости ответственности за обеспечение безопасности облака",
|
||||
"УБИ.067 Угроза неправомерного ознакомления с защищаемой информацией",
|
||||
"УБИ.068 Угроза неправомерного/некорректного использования интерфейса взаимодействия с приложением",
|
||||
"УБИ.069 Угроза неправомерных действий в каналах связи",
|
||||
"УБИ.070 Угроза непрерывной модернизации облачной инфраструктуры",
|
||||
"УБИ.071 Угроза несанкционированного восстановления удалённой защищаемой информации",
|
||||
"УБИ.072 Угроза несанкционированного выключения или обхода механизма защиты от записи в BIOS",
|
||||
"УБИ.073 Угроза несанкционированного доступа к активному и (или) пассивному виртуальному и (или) физическому сетевому оборудованию из физической и (или) виртуальной сети",
|
||||
"УБИ.074 Угроза несанкционированного доступа к аутентификационной информации",
|
||||
"УБИ.075 Угроза несанкционированного доступа к виртуальным каналам передачи",
|
||||
"УБИ.076 Угроза несанкционированного доступа к гипервизору из виртуальной машины и (или) физической сети",
|
||||
"УБИ.077 Угроза несанкционированного доступа к данным за пределами зарезервированного адресного пространства, в том числе выделенного под виртуальное аппаратное обеспечение",
|
||||
"УБИ.078 Угроза несанкционированного доступа к защищаемым виртуальным машинам из виртуальной и (или) физической сети",
|
||||
"УБИ.079 Угроза несанкционированного доступа к защищаемым виртуальным машинам со стороны других виртуальных машин",
|
||||
"УБИ.080 Угроза несанкционированного доступа к защищаемым виртуальным устройствам из виртуальной и (или) физической сети",
|
||||
"УБИ.081 Угроза несанкционированного доступа к локальному компьютеру через клиента грид-системы",
|
||||
"УБИ.082 Угроза несанкционированного доступа к сегментам вычислительного поля",
|
||||
"УБИ.083 Угроза несанкционированного доступа к системе по беспроводным каналам",
|
||||
"УБИ.084 Угроза несанкционированного доступа к системе хранения данных из виртуальной и (или) физической сети",
|
||||
"УБИ.085 Угроза несанкционированного доступа к хранимой в виртуальном пространстве защищаемой информации",
|
||||
"УБИ.086 Угроза несанкционированного изменения аутентификационной информации",
|
||||
"УБИ.087 Угроза несанкционированного использования привилегированных функций BIOS",
|
||||
"УБИ.088 Угроза несанкционированного копирования защищаемой информации",
|
||||
"УБИ.089 Угроза несанкционированного редактирования реестра",
|
||||
"УБИ.090 Угроза несанкционированного создания учётной записи пользователя",
|
||||
"УБИ.091 Угроза несанкционированного удаления защищаемой информации",
|
||||
"УБИ.092 Угроза несанкционированного удалённого внеполосного доступа к аппаратным средствам",
|
||||
"УБИ.093 Угроза несанкционированного управления буфером",
|
||||
"УБИ.094 Угроза несанкционированного управления синхронизацией и состоянием",
|
||||
"УБИ.095 Угроза несанкционированного управления указателями",
|
||||
"УБИ.096 Угроза несогласованности политик безопасности элементов облачной инфраструктуры",
|
||||
"УБИ.097 Угроза несогласованности правил доступа к большим данным",
|
||||
"УБИ.098 Угроза обнаружения открытых портов и идентификации привязанных к ним сетевых служб",
|
||||
"УБИ.099 Угроза обнаружения хостов",
|
||||
"УБИ.100 Угроза обхода некорректно настроенных механизмов аутентификации",
|
||||
"УБИ.101 Угроза общедоступности облачной инфраструктуры",
|
||||
"УБИ.102 Угроза опосредованного управления группой программ через совместно используемые данные",
|
||||
"УБИ.103 Угроза определения типов объектов защиты",
|
||||
"УБИ.104 Угроза определения топологии вычислительной сети",
|
||||
"УБИ.105 Угроза отказа в загрузке входных данных неизвестного формата хранилищем больших данных",
|
||||
"УБИ.106 Угроза отказа в обслуживании системой хранения данных суперкомпьютера",
|
||||
"УБИ.107 Угроза отключения контрольных датчиков",
|
||||
"УБИ.108 Угроза ошибки обновления гипервизора",
|
||||
"УБИ.109 Угроза перебора всех настроек и параметров приложения",
|
||||
"УБИ.110 Угроза перегрузки грид-системы вычислительными заданиями",
|
||||
"УБИ.111 Угроза передачи данных по скрытым каналам",
|
||||
"УБИ.112 Угроза передачи запрещённых команд на оборудование с числовым программным управлением",
|
||||
"УБИ.113 Угроза перезагрузки аппаратных и программно-аппаратных средств вычислительной техники",
|
||||
"УБИ.114 Угроза переполнения целочисленных переменных",
|
||||
"УБИ.115 Угроза перехвата вводимой и выводимой на периферийные устройства информации",
|
||||
"УБИ.116 Угроза перехвата данных, передаваемых по вычислительной сети",
|
||||
"УБИ.117 Угроза перехвата привилегированного потока",
|
||||
"УБИ.118 Угроза перехвата привилегированного процесса",
|
||||
"УБИ.119 Угроза перехвата управления гипервизором",
|
||||
"УБИ.120 Угроза перехвата управления средой виртуализации",
|
||||
"УБИ.121 Угроза повреждения системного реестра",
|
||||
"УБИ.122 Угроза повышения привилегий",
|
||||
"УБИ.123 Угроза подбора пароля BIOS",
|
||||
"УБИ.124 Угроза подделки записей журнала регистрации событий",
|
||||
"УБИ.125 Угроза подключения к беспроводной сети в обход процедуры аутентификации",
|
||||
"УБИ.126 Угроза подмены беспроводного клиента или точки доступа",
|
||||
"УБИ.127 Угроза подмены действия пользователя путём обмана",
|
||||
"УБИ.128 Угроза подмены доверенного пользователя",
|
||||
"УБИ.129 Угроза подмены резервной копии программного обеспечения BIOS",
|
||||
"УБИ.130 Угроза подмены содержимого сетевых ресурсов",
|
||||
"УБИ.131 Угроза подмены субъекта сетевого доступа",
|
||||
"УБИ.132 Угроза получения предварительной информации об объекте защиты",
|
||||
"УБИ.133 Угроза получения сведений о владельце беспроводного устройства",
|
||||
"УБИ.134 Угроза потери доверия к поставщику облачных услуг",
|
||||
"УБИ.135 Угроза потери и утечки данных, обрабатываемых в облаке",
|
||||
"УБИ.136 Угроза потери информации вследствие несогласованности работы узлов хранилища больших данных",
|
||||
"УБИ.137 Угроза потери управления облачными ресурсами",
|
||||
"УБИ.138 Угроза потери управления собственной инфраструктурой при переносе её в облако",
|
||||
"УБИ.139 Угроза преодоления физической защиты",
|
||||
"УБИ.140 Угроза приведения системы в состояние «отказ в обслуживании»",
|
||||
"УБИ.141 Угроза привязки к поставщику облачных услуг",
|
||||
"УБИ.142 Угроза приостановки оказания облачных услуг вследствие технических сбоев",
|
||||
"УБИ.143 Угроза программного выведения из строя средств хранения, обработки и (или) ввода/вывода/передачи информации",
|
||||
"УБИ.144 Угроза программного сброса пароля BIOS",
|
||||
"УБИ.145 Угроза пропуска проверки целостности программного обеспечения",
|
||||
"УБИ.146 Угроза прямого обращения к памяти вычислительного поля суперкомпьютера",
|
||||
"УБИ.147 Угроза распространения несанкционированно повышенных прав на всю грид-систему",
|
||||
"УБИ.148 Угроза сбоя автоматического управления системой разграничения доступа хранилища больших данных",
|
||||
"УБИ.149 Угроза сбоя обработки специальным образом изменённых файлов",
|
||||
"УБИ.150 Угроза сбоя процесса обновления BIOS",
|
||||
"УБИ.151 Угроза сканирования веб-сервисов, разработанных на основе языка описания WSDL",
|
||||
"УБИ.152 Угроза удаления аутентификационной информации",
|
||||
"УБИ.153 Угроза усиления воздействия на вычислительные ресурсы пользователей при помощи сторонних серверов",
|
||||
"УБИ.154 Угроза установки уязвимых версий обновления программного обеспечения BIOS",
|
||||
"УБИ.155 Угроза утраты вычислительных ресурсов",
|
||||
"УБИ.156 Угроза утраты носителей информации",
|
||||
"УБИ.157 Угроза физического выведения из строя средств хранения, обработки и (или) ввода/вывода/передачи информации",
|
||||
"УБИ.158 Угроза форматирования носителей информации",
|
||||
"УБИ.159 Угроза «форсированного веб-браузинга»",
|
||||
"УБИ.160 Угроза хищения средств хранения, обработки и (или) ввода/вывода/передачи информации",
|
||||
"УБИ.161 Угроза чрезмерного использования вычислительных ресурсов суперкомпьютера в ходе интенсивного обмена межпроцессорными сообщениями",
|
||||
"УБИ.162 Угроза эксплуатации цифровой подписи программного кода",
|
||||
"УБИ.163 Угроза перехвата исключения/сигнала из привилегированного блока функций",
|
||||
"УБИ.164 Угроза распространения состояния «отказ в обслуживании» в облачной инфраструктуре",
|
||||
"УБИ.165 Угроза включения в проект не достоверно испытанных компонентов",
|
||||
"УБИ.166 Угроза внедрения системной избыточности",
|
||||
"УБИ.167 Угроза заражения компьютера при посещении неблагонадёжных сайтов",
|
||||
"УБИ.168 Угроза «кражи» учётной записи доступа к сетевым сервисам",
|
||||
"УБИ.169 Угроза наличия механизмов разработчика",
|
||||
"УБИ.170 Угроза неправомерного шифрования информации",
|
||||
"УБИ.171 Угроза скрытного включения вычислительного устройства в состав бот-сети",
|
||||
"УБИ.172 Угроза распространения «почтовых червей»",
|
||||
"УБИ.173 Угроза «спама» веб-сервера",
|
||||
"УБИ.174 Угроза «фарминга»",
|
||||
"УБИ.175 Угроза «фишинга»",
|
||||
"УБИ.176 Угроза нарушения технологического/производственного процесса из-за временны́х задержек, вносимых средством защиты",
|
||||
"УБИ.177 Угроза неподтверждённого ввода данных оператором в систему, связанную с безопасностью",
|
||||
"УБИ.178 Угроза несанкционированного использования системных и сетевых утилит",
|
||||
"УБИ.179 Угроза несанкционированной модификации защищаемой информации",
|
||||
"УБИ.180 Угроза отказа подсистемы обеспечения температурного режима",
|
||||
"УБИ.181 Угроза перехвата одноразовых паролей в режиме реального времени",
|
||||
"УБИ.182 Угроза физического устаревания аппаратных компонентов",
|
||||
"УБИ.183 Угроза перехвата управления автоматизированной системой управления технологическими процессами",
|
||||
"УБИ.184 Угроза агрегирования данных, обрабатываемых с помощью мобильного устройства",
|
||||
"УБИ.185 Угроза несанкционированного изменения параметров настройки средств защиты информации",
|
||||
"УБИ.186 Угроза внедрения вредоносного кода через рекламу, сервисы и контент",
|
||||
"УБИ.187 Угроза несанкционированного воздействия на средство защиты информации",
|
||||
"УБИ.188 Угроза подмены программного обеспечения",
|
||||
"УБИ.189 Угроза маскирования действий вредоносного кода",
|
||||
"УБИ.190 Угроза внедрения вредоносного кода за счет посещения зараженных сайтов в сети Интернет",
|
||||
"УБИ.191 Угроза внедрения вредоносного кода в дистрибутив программного обеспечения",
|
||||
"УБИ.192 Угроза использования уязвимых версий программного обеспечения",
|
||||
"УБИ.193 Угроза утечки информации за счет применения вредоносным программным обеспечением алгоритмов шифрования трафика",
|
||||
"УБИ.194 Угроза несанкционированного использования привилегированных функций мобильного устройства",
|
||||
"УБИ.195 Угроза удаленного запуска вредоносного кода в обход механизмов защиты операционной системы",
|
||||
"УБИ.196 Угроза контроля вредоносной программой списка приложений, запущенных на мобильном устройстве",
|
||||
"УБИ.197 Угроза хищения аутентификационной информации из временных файлов cookie",
|
||||
"УБИ.198 Угроза скрытной регистрации вредоносной программой учетных записей администраторов",
|
||||
"УБИ.199 Угроза перехвата управления мобильного устройства при использовании виртуальных голосовых ассистентов",
|
||||
"УБИ.200 Угроза хищения информации с мобильного устройства при использовании виртуальных голосовых ассистентов",
|
||||
"УБИ.201 Угроза утечки пользовательских данных при использовании функций автоматического заполнения аутентификационной информации в браузере",
|
||||
"УБИ.202 Угроза несанкционированной установки приложений на мобильные устройства",
|
||||
"УБИ.203 Угроза утечки информации с неподключенных к сети Интернет компьютеров",
|
||||
"УБИ.204 Угроза несанкционированного изменения вредоносной программой значений параметров программируемых логических контроллеров",
|
||||
"УБИ.205 Угроза нарушения работы компьютера и блокирования доступа к его данным из-за некорректной работы установленных на нем средств защиты",
|
||||
"УБИ.206 Угроза отказа в работе оборудования из-за изменения геолокационной информации о нем",
|
||||
"УБИ.207 Угроза несанкционированного доступа к параметрам настройки оборудования за счет использования «мастер-кодов» (инженерных паролей)",
|
||||
"УБИ.208 Угроза нецелевого использования вычислительных ресурсов средства вычислительной техники",
|
||||
"УБИ.209 Угроза несанкционированного доступа к защищаемой памяти \r\nядра процессора",
|
||||
"УБИ.210 Угроза нарушения работы информационной системы, вызванного обновлением используемого в ней программного обеспечения",
|
||||
"УБИ.211 Угроза использования непроверенных пользовательских данных при формировании конфигурационного файла, используемого программным обеспечением администрирования информационных систем",
|
||||
"УБИ.212 Угроза перехвата управления информационной системой",
|
||||
"УБИ.213 Угроза обхода многофакторной аутентификации",
|
||||
"УБИ.214 Угроза несвоевременного выявления и реагирования компонентами информационной (автоматизированной) системы (в том числе средствами защиты информации) на события безопасности информации",
|
||||
"УБИ.215 Угроза несанкционированного доступа к системе при помощи сторонних сервисов",
|
||||
"УБИ.216 Угроза получения несанкционированного доступа к приложениям, установленным на Smart-картах",
|
||||
"УБИ.217 Угроза использования скомпрометированного доверенного источника обновлений программного обеспечения",
|
||||
"УБИ.218 Угроза раскрытия информации о модели машинного обучения",
|
||||
"УБИ.219 Угроза хищения обучающих данных",
|
||||
"УБИ.220 Угроза нарушения функционирования («обхода») средств, реализующих технологии искусственного интеллекта",
|
||||
"УБИ.221 Угроза модификации модели машинного обучения путем искажения («отравления») обучающих данных",
|
||||
"УБИ.222 Угроза подмены модели машинного обучения",
|
||||
"УБИ.223 Угроза несанкционированного доступа к контейнерам, предоставляющего пользователям расширенные привилегии",
|
||||
"УБИ.224 Угроза нарушения целостности (подмены) контейнеров",
|
||||
"УБИ.225 Угроза нарушения изоляции контейнеров",
|
||||
"УБИ.226 Угроза внедрения вредоносного программного обеспечения в контейнеры",
|
||||
"УБИ.227 Угроза модификации (подмены) образов контейнеров"
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
2026-08-04
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
[
|
||||
"Информационная система управления",
|
||||
"Автоматизированная система управления технологическим процессом",
|
||||
"Информационно-телекоммуникационная сеть",
|
||||
"Автоматизированная система управления производством",
|
||||
"Система диспетчерского управления",
|
||||
"Система управления базами данных",
|
||||
"Автоматизированная система научных исследований",
|
||||
"Система электронного документооборота",
|
||||
"Информационная система расчетов",
|
||||
"Система управления финансами",
|
||||
"Иной типовой отраслевой объект КИИ"
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,162 @@
|
||||
# -*- 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 _normalize_ubi(code):
|
||||
"""«УБИ. 001» / «УБИ.001» → «УБИ.001» (без пробела после точки)."""
|
||||
m = re.search(r'(УБИ\.?)\s*([\d.]+)', code or '', re.I)
|
||||
if m:
|
||||
return f"{m.group(1).replace('.', '').upper()}.{m.group(2)}"
|
||||
return (code or '').strip()
|
||||
|
||||
|
||||
def _parse_threats(html):
|
||||
"""Извлекает пары (код УБИ, название) из HTML-таблицы БДУ.
|
||||
|
||||
Строки вида: <tr>...<td>УБИ. 001</td><td>Угроза ...</td>...</tr>
|
||||
Возвращает список строк: «УБИ.001 Угроза ...» (код + название).
|
||||
"""
|
||||
items = []
|
||||
if not html:
|
||||
return items
|
||||
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]
|
||||
if not cleaned:
|
||||
continue
|
||||
# первая ячейка — код (УБИ.001), вторая — название
|
||||
code = _normalize_ubi(cleaned[0]) if cleaned else ''
|
||||
name = ''
|
||||
for cell in cleaned[1:]:
|
||||
if cell and cell not in ('№', 'Идентификатор', 'Наименование', 'Дата', 'Уровень опасности'):
|
||||
name = cell
|
||||
break
|
||||
if not name:
|
||||
continue
|
||||
if code.startswith('УБИ.'):
|
||||
items.append(f"{code} {name}")
|
||||
else:
|
||||
items.append(name)
|
||||
return items
|
||||
|
||||
|
||||
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 []
|
||||
+10
-7
@@ -310,10 +310,14 @@ def _subst_var_xml(xml: str, var: str, val: str) -> str:
|
||||
+ '</w:r><w:r><w:t xml:space="preserve">')
|
||||
|
||||
|
||||
def _expand_nested_loops(template: str, item: Dict) -> str:
|
||||
def _expand_nested_loops(template: str, item: Dict, loops: Dict = None) -> str:
|
||||
"""Разворачивает вложенные циклы вида <!-- loop:users_loop --> ... <!-- loop_end -->
|
||||
данными из item (список словарей). Используется для циклов внутри цикла ИС,
|
||||
например пользователи ИС. Переменные внутри: {{user_fio}}, {{user_position}}."""
|
||||
данными из item (список словарей) или из общего словаря циклов loops.
|
||||
Используется для циклов внутри цикла ИС, например пользователи ИС,
|
||||
а также для вложенных таблиц (показатели значимости внутри объекта КИИ).
|
||||
Переменные внутри: {{user_fio}}, {{user_position}}, {{item.*}}."""
|
||||
if loops is None:
|
||||
loops = {}
|
||||
result = template
|
||||
for _ in range(20):
|
||||
m_start = LOOP_START.search(result)
|
||||
@@ -321,6 +325,8 @@ def _expand_nested_loops(template: str, item: Dict) -> str:
|
||||
break
|
||||
key = m_start.group(1)
|
||||
data = item.get(key)
|
||||
if not isinstance(data, list):
|
||||
data = loops.get(key)
|
||||
if not isinstance(data, list):
|
||||
# Нет данных для вложенного цикла — удаляем блок целиком
|
||||
depth = 1
|
||||
@@ -478,7 +484,7 @@ def expand_loops_in_zip(docx_path: str, loops: Dict[str, List[Dict]]) -> str:
|
||||
clone = template_clean
|
||||
# Вложенные циклы (users_loop и т.п.) — разворачиваем ДО подстановки
|
||||
# обычных полей, чтобы {{user_fio}} внутри них не затёрлись алиасами
|
||||
clone = _expand_nested_loops(clone, item)
|
||||
clone = _expand_nested_loops(clone, item, loops)
|
||||
for field, value in item.items():
|
||||
if isinstance(value, list):
|
||||
continue # списки — данные вложенных циклов, не подставляем
|
||||
@@ -692,9 +698,6 @@ def _highlight_unmatched(doc):
|
||||
pos = m.end()
|
||||
if pos < len(full):
|
||||
_add_run_with_tabs(para, full[pos:], first)
|
||||
run = para.add_run(full[pos:])
|
||||
if first is not None:
|
||||
_copy_run_style(first, run)
|
||||
|
||||
for para in doc.paragraphs:
|
||||
_process_para(para)
|
||||
|
||||
+18
-18
@@ -5,7 +5,7 @@
|
||||
рядом с программой (при сборке .exe — рядом с exe):
|
||||
- Инструкция_пользователя.docx
|
||||
- Инструкция_администратора.docx
|
||||
- Опросный_лист_152_ФЗ.xlsx (пустой шаблон)
|
||||
- Опросный_лист_187_ФЗ.xlsx (пустой шаблон)
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -45,7 +45,7 @@ def variables_reference_path():
|
||||
|
||||
|
||||
def survey_path():
|
||||
return os.path.join(docs_dir(), 'Опросный_лист_152_ФЗ.xlsx')
|
||||
return os.path.join(docs_dir(), 'Опросный_лист_187_ФЗ.xlsx')
|
||||
|
||||
|
||||
def _h(doc, text, size=14):
|
||||
@@ -83,7 +83,7 @@ def _generate_variables_reference(path):
|
||||
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
|
||||
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
|
||||
|
||||
groups = {'A': 'Общие переменные', 'B': '152-ФЗ (персональные данные)'}
|
||||
groups = {'A': 'Общие переменные', 'B': '187-ФЗ (критическая информационная инфраструктура)'}
|
||||
by_group = {'A': [], 'B': []}
|
||||
for var, group, desc, _ in VARIABLE_DEFS:
|
||||
by_group.setdefault(group, []).append((var, desc))
|
||||
@@ -97,10 +97,10 @@ def _generate_variables_reference(path):
|
||||
_bullet(doc, f'{var} — {desc}')
|
||||
|
||||
_h(doc, 'Циклы', 12)
|
||||
_bullet(doc, 'information_systems — ИС ({{item.name}}, {{item.description}}, {{item.structure}}, {{item.pd_actions}}, {{item.defense_tools_list}}, {{item.users}}, {{item.category}}, {{item.pd_count}} и др.).')
|
||||
_bullet(doc, 'information_systems — объекты КИИ ({{item.name}}, {{item.description}}, {{item.category}}, {{item.purpose}}, {{item.sphere}}, {{item.object_type}}, {{item.architecture}}, {{item.net_category}}, {{item.hw_sw}}, {{item.os_sw}}, {{item.app_sw}}, {{item.defense_tools_list}}, {{item.users}} и др.).')
|
||||
_bullet(doc, 'commission — комиссия ({{item.role}}, {{item.position}}, {{item.fio}}, {{item.fio_initials}}, {{item.fio_short}}, {{item.signature}}).')
|
||||
_bullet(doc, 'employees_access — сотрудники с доступом ({{item.position}}, {{item.fio}}).')
|
||||
_bullet(doc, 'paper_documents — бумажные документы ({{item.document}}, {{item.storage}}).')
|
||||
|
||||
|
||||
doc.save(path)
|
||||
|
||||
@@ -108,11 +108,11 @@ def _generate_variables_reference(path):
|
||||
def _generate_user_manual(path):
|
||||
"""Инструкция пользователя: программа, циклы, переменные, проверки."""
|
||||
doc = docx.Document()
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ')
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 187-ФЗ')
|
||||
_h(doc, 'Инструкция пользователя', 13)
|
||||
|
||||
_h(doc, '1. Что это за программа', 12)
|
||||
_p(doc, 'Программа автоматически заполняет шаблоны документов по 152-ФЗ '
|
||||
_p(doc, 'Программа автоматически заполняет шаблоны документов по 187-ФЗ '
|
||||
'(акты обследования, политики, инструкции, приказы и т.д.) данными '
|
||||
'об организации, информационных системах, комиссии и сотрудниках. '
|
||||
'Данные можно ввести вручную на вкладках или импортировать из '
|
||||
@@ -120,10 +120,10 @@ def _generate_user_manual(path):
|
||||
|
||||
_h(doc, '2. Основные вкладки', 12)
|
||||
_bullet(doc, 'Организация — реквизиты, адреса, руководитель, склонения названий, проверка данных (Морфер / DaData / ИИ).')
|
||||
_bullet(doc, 'Информационные системы — ИС, категории ПДн, действия с ПДн, средства защиты, пользователи.')
|
||||
_bullet(doc, 'Комиссия — состав комиссии по классификации ИСПДн.')
|
||||
_bullet(doc, 'Пользователи — сотрудники с доступом к ПДн.')
|
||||
_bullet(doc, 'Бумажные документы — бумажные носители ПДн и места хранения.')
|
||||
_bullet(doc, 'Объекты КИИ — наименование, описание, категория значимости, средства защиты, пользователи ОКИИ.')
|
||||
_bullet(doc, 'Комиссия — состав комиссии по категорированию объектов КИИ.')
|
||||
_bullet(doc, 'Пользователи — сотрудники с доступом к объектам КИИ.')
|
||||
|
||||
_bullet(doc, 'Генерация — выбор шаблонов и запуск генерации.')
|
||||
_bullet(doc, 'Лог — журнал действий, экспорт в файл.')
|
||||
|
||||
@@ -137,13 +137,13 @@ def _generate_user_manual(path):
|
||||
|
||||
_h(doc, '4. Циклы (повторяющиеся блоки)', 12)
|
||||
_p(doc, 'Если в документе нужно повторить блок для каждой ИС, комиссии, '
|
||||
'сотрудника или бумажного документа — используются маркеры цикла:')
|
||||
'сотрудника или объекта КИИ — используются маркеры цикла:')
|
||||
_p(doc, '<!-- loop:information_systems --> ... {{item.name}} ... <!-- loop_end -->', bold=True)
|
||||
_p(doc, 'Доступные циклы:')
|
||||
_bullet(doc, 'information_systems — ИС ({{item.name}}, {{item.description}}, {{item.structure}}, {{item.pd_actions}}, {{item.defense_tools_list}}, {{item.users}}, {{item.category}}, {{item.pd_count}} и др.).')
|
||||
_bullet(doc, 'information_systems — объекты КИИ ({{item.name}}, {{item.description}}, {{item.category}}, {{item.purpose}}, {{item.sphere}}, {{item.object_type}}, {{item.architecture}}, {{item.net_category}}, {{item.hw_sw}}, {{item.os_sw}}, {{item.app_sw}}, {{item.defense_tools_list}}, {{item.users}} и др.).')
|
||||
_bullet(doc, 'commission — комиссия ({{item.role}}, {{item.position}}, {{item.fio}}, {{item.fio_initials}}, {{item.fio_short}}, {{item.signature}}).')
|
||||
_bullet(doc, 'employees_access — сотрудники с доступом ({{item.position}}, {{item.fio}}).')
|
||||
_bullet(doc, 'paper_documents — бумажные документы ({{item.document}}, {{item.storage}}).')
|
||||
|
||||
_p(doc, 'Внутри цикла переменные пишутся через точку: {{item.поле}}. '
|
||||
'Допускается и синтаксис без точки: {{item_поле}}.')
|
||||
|
||||
@@ -173,13 +173,13 @@ def _generate_user_manual(path):
|
||||
def _generate_admin_manual(path):
|
||||
"""Инструкция администратора: словари, API-ключи, лимиты."""
|
||||
doc = docx.Document()
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ')
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 187-ФЗ')
|
||||
_h(doc, 'Инструкция администратора', 13)
|
||||
|
||||
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
||||
_bullet(doc, 'api_settings.json — API-ключи (DaData, ИИ).')
|
||||
_bullet(doc, 'import_settings.json — словарь импорта: что и откуда брать из опросника.')
|
||||
_bullet(doc, 'dictionaries/ — справочники: defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json.')
|
||||
_bullet(doc, 'dictionaries/ — справочники: defense_tools.json, okved.json (словарь ОКВЭД).')
|
||||
_bullet(doc, 'dokogen.log — журнал работы (пишется автоматически).')
|
||||
_bullet(doc, 'docs/ — инструкции и пустой опросник (создаются автоматически).')
|
||||
|
||||
@@ -240,8 +240,8 @@ def _ensure_survey():
|
||||
return target
|
||||
# Ищем шаблон опросника: рядом с программой или встроенный
|
||||
candidates = [
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_152_ФЗ.xlsx'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_152_ФЗ.xlsx'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_187_ФЗ.xlsx'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_187_ФЗ.xlsx'),
|
||||
]
|
||||
for c in candidates:
|
||||
if os.path.exists(c):
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
+309
-73
@@ -31,24 +31,36 @@
|
||||
"cell": "",
|
||||
"keywords": "адрес организации \\(фактический\\)"
|
||||
},
|
||||
{
|
||||
"name": "Электронная почта",
|
||||
"var": "email",
|
||||
"cell": "",
|
||||
"keywords": "электронная почта"
|
||||
},
|
||||
{
|
||||
"name": "Телефон",
|
||||
"var": "phone",
|
||||
"cell": "",
|
||||
"keywords": "телефон"
|
||||
},
|
||||
{
|
||||
"name": "ИНН",
|
||||
"var": "inn",
|
||||
"cell": "",
|
||||
"keywords": "инн"
|
||||
},
|
||||
{
|
||||
"name": "Дата ОГРН",
|
||||
"var": "ogrnDate",
|
||||
"cell": "",
|
||||
"keywords": "дата.*огрн"
|
||||
},
|
||||
{
|
||||
"name": "ОГРН",
|
||||
"var": "ogrn",
|
||||
"cell": "",
|
||||
"keywords": "огрн"
|
||||
},
|
||||
{
|
||||
"name": "Дата получения ОГРН",
|
||||
"var": "ogrnDate",
|
||||
"cell": "",
|
||||
"keywords": "дата получения огрн|дата.*огрн"
|
||||
},
|
||||
{
|
||||
"name": "КПП",
|
||||
"var": "kpp",
|
||||
@@ -61,6 +73,12 @@
|
||||
"cell": "",
|
||||
"keywords": "основной оквэд"
|
||||
},
|
||||
{
|
||||
"name": "Дополнительные ОКВЭД",
|
||||
"var": "okvedExtra",
|
||||
"cell": "",
|
||||
"keywords": "дополнительные оквэд"
|
||||
},
|
||||
{
|
||||
"name": "Должность руководителя",
|
||||
"var": "chiefPosition",
|
||||
@@ -74,82 +92,70 @@
|
||||
"keywords": "фио руководителя"
|
||||
},
|
||||
{
|
||||
"name": "Должность ответственного за ПДн",
|
||||
"var": "ispdnPosition",
|
||||
"name": "Должность ответственного лица (безопасность ЗОКИИ)",
|
||||
"var": "responsiblePosition",
|
||||
"cell": "",
|
||||
"keywords": "должность.*защит.*информ|должность.*администрат"
|
||||
"keywords": "должность ответственного лица"
|
||||
},
|
||||
{
|
||||
"name": "ФИО ответственного за ПДн",
|
||||
"var": "ispdnFio",
|
||||
"name": "ФИО ответственного лица (безопасность ЗОКИИ)",
|
||||
"var": "responsibleFio",
|
||||
"cell": "",
|
||||
"keywords": "фио.*защит.*информ|фио.*администрат"
|
||||
"keywords": "фио ответственного лица"
|
||||
},
|
||||
{
|
||||
"name": "Email ответственного за ПДн",
|
||||
"var": "ispdnEmail",
|
||||
"cell": "",
|
||||
"keywords": "электронная почта.*защит|электронная почта.*ответств|электронная почта.*специалист"
|
||||
},
|
||||
{
|
||||
"name": "Телефон ответственного за ПДн",
|
||||
"var": "ispdnPhone",
|
||||
"cell": "",
|
||||
"keywords": "телефон.*защит|телефон.*ответств|телефон.*специалист"
|
||||
},
|
||||
{
|
||||
"name": "Структурное подразделение по безопасности",
|
||||
"name": "Подразделение по безопасности ОКИИ",
|
||||
"var": "ispdnDepartment",
|
||||
"cell": "",
|
||||
"keywords": "структур.*подраздел"
|
||||
},
|
||||
{
|
||||
"name": "Номер договора",
|
||||
"var": "contractNumber",
|
||||
"name": "Рабочий телефон ответственного",
|
||||
"var": "ispdnPhone",
|
||||
"cell": "",
|
||||
"keywords": "номер.*договора|номер.*контракта"
|
||||
"keywords": "рабочий телефон ответственного|мобильный телефон ответственного"
|
||||
},
|
||||
{
|
||||
"name": "Дата договора",
|
||||
"var": "contractDate",
|
||||
"name": "Электронная почта ответственного",
|
||||
"var": "ispdnEmail",
|
||||
"cell": "",
|
||||
"keywords": "дата.*договора|дата.*контракта"
|
||||
"keywords": "электронная почта ответственного"
|
||||
},
|
||||
{
|
||||
"name": "Перечень бумажных документов",
|
||||
"var": "paperDocuments",
|
||||
"cell": "",
|
||||
"keywords": "названи.*документ"
|
||||
},
|
||||
{
|
||||
"name": "Место хранения документов",
|
||||
"var": "paperStorage",
|
||||
"cell": "",
|
||||
"keywords": "место.*хранен"
|
||||
},
|
||||
{
|
||||
"name": "Должность ответственного за обработку ПДн",
|
||||
"var": "responsiblePosition",
|
||||
"cell": "",
|
||||
"keywords": "должность.*ответственн"
|
||||
},
|
||||
{
|
||||
"name": "ФИО ответственного за обработку ПДн",
|
||||
"var": "responsibleFio",
|
||||
"cell": "",
|
||||
"keywords": "фио.*ответственн"
|
||||
},
|
||||
{
|
||||
"name": "Должность администратора безопасности",
|
||||
"name": "Должность специалиста по ЗИ/администратора",
|
||||
"var": "administratorPosition",
|
||||
"cell": "",
|
||||
"keywords": "должность.*администрат.*(?:безопас|защит|пд)"
|
||||
"keywords": "должность специалиста по защите информации|должность.*администрат"
|
||||
},
|
||||
{
|
||||
"name": "ФИО администратора безопасности",
|
||||
"name": "ФИО специалиста по ЗИ/администратора",
|
||||
"var": "administratorFio",
|
||||
"cell": "",
|
||||
"keywords": "фио.*администрат.*(?:безопас|защит|пд)"
|
||||
"keywords": "фио специалиста по защите информации|фио.*администрат"
|
||||
},
|
||||
{
|
||||
"name": "Право владения зданием",
|
||||
"var": "buildingRight",
|
||||
"cell": "",
|
||||
"keywords": "право владения зданием"
|
||||
},
|
||||
{
|
||||
"name": "Сумма прибыли за год",
|
||||
"var": "profit",
|
||||
"cell": "",
|
||||
"keywords": "сумма прибыли"
|
||||
},
|
||||
{
|
||||
"name": "Сумма налога с прибыли",
|
||||
"var": "profitTax",
|
||||
"cell": "",
|
||||
"keywords": "сумма налога с прибыли"
|
||||
},
|
||||
{
|
||||
"name": "Доменное имя и внешний IP",
|
||||
"var": "domainName",
|
||||
"cell": "",
|
||||
"keywords": "доменное имя и внешний сетевой адрес"
|
||||
},
|
||||
{
|
||||
"name": "Сайт организации",
|
||||
@@ -161,49 +167,279 @@
|
||||
"name": "Должность ответственного за сайт",
|
||||
"var": "site_responsible_position",
|
||||
"cell": "",
|
||||
"keywords": "должность ответственного за сайт|должность.*отв.*сайт"
|
||||
"keywords": "должность ответственного за сайт"
|
||||
},
|
||||
{
|
||||
"name": "ФИО ответственного за сайт",
|
||||
"var": "site_responsible_fio",
|
||||
"cell": "",
|
||||
"keywords": "фио.*отв.*сайт|фио.*сайт"
|
||||
"keywords": "фио ответственного за сайт"
|
||||
},
|
||||
{
|
||||
"name": "Разработчик сайта",
|
||||
"var": "site_service_provider",
|
||||
"cell": "",
|
||||
"keywords": "наименование организации.*разрабат.*сайт|наименование.*размещала.*сайт|разрабат.*сайт.*организац"
|
||||
"keywords": "организации, которая разрабатывала и размещала корпоративный сайт"
|
||||
},
|
||||
{
|
||||
"name": "ИНН разработчика сайта",
|
||||
"var": "site_service_provider_inn",
|
||||
"cell": "",
|
||||
"keywords": "инн.*разрабат.*сайт|инн.*размещала.*сайт"
|
||||
"keywords": "инн организации, которая разрабатывала"
|
||||
},
|
||||
{
|
||||
"name": "Размещение сайта (хостинг)",
|
||||
"name": "Размещение сайта",
|
||||
"var": "site_hosting",
|
||||
"cell": "",
|
||||
"keywords": "размещение сайта|адрес расположения серверов|адрес.*сервер.*сайт"
|
||||
"keywords": "размещение сайта"
|
||||
},
|
||||
{
|
||||
"name": "Адрес хостинга",
|
||||
"name": "Адрес расположения серверов",
|
||||
"var": "site_hosting_address",
|
||||
"cell": "",
|
||||
"keywords": "адрес.*хостинг|адрес.*располож.*сервер"
|
||||
"keywords": "адрес расположения серверов"
|
||||
},
|
||||
{
|
||||
"name": "Электронная почта",
|
||||
"var": "email",
|
||||
"name": "Облачная платформа",
|
||||
"var": "cloud_platform",
|
||||
"cell": "",
|
||||
"keywords": "электронная почта"
|
||||
"keywords": "облачную платформу"
|
||||
},
|
||||
{
|
||||
"name": "Телефон",
|
||||
"var": "phone",
|
||||
"name": "Аренда выделенного сервера",
|
||||
"var": "cloud_dedicated_server",
|
||||
"cell": "",
|
||||
"keywords": "телефон"
|
||||
"keywords": "выделенный сервер"
|
||||
},
|
||||
{
|
||||
"name": "Аренда виртуального ЦОД",
|
||||
"var": "cloud_vdc",
|
||||
"cell": "",
|
||||
"keywords": "виртуальный дата-центр"
|
||||
},
|
||||
{
|
||||
"name": "Серверы в дата-центре",
|
||||
"var": "cloud_datacenter",
|
||||
"cell": "",
|
||||
"keywords": "сервера в дата-центре"
|
||||
},
|
||||
{
|
||||
"name": "Средства контейнеризации",
|
||||
"var": "cloud_container",
|
||||
"cell": "",
|
||||
"keywords": "контейнеризации"
|
||||
},
|
||||
{
|
||||
"name": "Собственная серверная (ЦОД)",
|
||||
"var": "cloud_own_server",
|
||||
"cell": "",
|
||||
"keywords": "собственная серверная"
|
||||
},
|
||||
{
|
||||
"name": "Общее количество компьютеров",
|
||||
"var": "computers_total",
|
||||
"cell": "",
|
||||
"keywords": "общее количество компьютеров"
|
||||
},
|
||||
{
|
||||
"name": "Компьютеры, подключённые к объектам КИИ",
|
||||
"var": "computers_kii",
|
||||
"cell": "",
|
||||
"keywords": "компьютеров подключенных к объектам кии"
|
||||
},
|
||||
{
|
||||
"name": "Центр мониторинга",
|
||||
"var": "monitoring_center",
|
||||
"cell": "",
|
||||
"keywords": "к какому центру подключены"
|
||||
},
|
||||
{
|
||||
"name": "Детали подключения к центру мониторинга",
|
||||
"var": "monitoring_details",
|
||||
"cell": "",
|
||||
"keywords": "детали подключения"
|
||||
},
|
||||
{
|
||||
"name": "Дополнительная информация",
|
||||
"var": "additionalInfo",
|
||||
"cell": "",
|
||||
"keywords": "дополнительная информация"
|
||||
},
|
||||
{
|
||||
"name": "Физическая охрана (ЧОП, сторожа, охранники)",
|
||||
"var": "physicalGuard",
|
||||
"cell": "",
|
||||
"keywords": "физическая охрана"
|
||||
},
|
||||
{
|
||||
"name": "Пожарная сигнализация",
|
||||
"var": "physicalFireAlarm",
|
||||
"cell": "",
|
||||
"keywords": "пожарная сигнализация"
|
||||
},
|
||||
{
|
||||
"name": "Тревожная кнопка",
|
||||
"var": "physicalPanicButton",
|
||||
"cell": "",
|
||||
"keywords": "тревожная кнопка"
|
||||
},
|
||||
{
|
||||
"name": "Решётки на окнах",
|
||||
"var": "physicalBars",
|
||||
"cell": "",
|
||||
"keywords": "решетки на окнах"
|
||||
},
|
||||
{
|
||||
"name": "Металлические двери",
|
||||
"var": "physicalMetalDoors",
|
||||
"cell": "",
|
||||
"keywords": "металлические двери"
|
||||
},
|
||||
{
|
||||
"name": "Система видеонаблюдения",
|
||||
"var": "physicalVideoSurveillance",
|
||||
"cell": "",
|
||||
"keywords": "система видеонаблюдения"
|
||||
},
|
||||
{
|
||||
"name": "Видеодомофон",
|
||||
"var": "physicalVideoIntercom",
|
||||
"cell": "",
|
||||
"keywords": "видеодомофон"
|
||||
},
|
||||
{
|
||||
"name": "Турникет на входе",
|
||||
"var": "physicalTurnstile",
|
||||
"cell": "",
|
||||
"keywords": "турникет на входе"
|
||||
},
|
||||
{
|
||||
"name": "Журнал учёта посетителей",
|
||||
"var": "physicalVisitorLog",
|
||||
"cell": "",
|
||||
"keywords": "журнал учета посетителей|журнал учёта посетителей"
|
||||
}
|
||||
],
|
||||
"criterion_fields": [
|
||||
{
|
||||
"num": "1",
|
||||
"name": "Максимальное число пострадавших людей",
|
||||
"keywords": "пострадавш",
|
||||
"unit": "человек"
|
||||
},
|
||||
{
|
||||
"num": "2б",
|
||||
"name": "Люди с нарушенными условиями жизнедеятельности",
|
||||
"keywords": "условия жизнедеятельности",
|
||||
"unit": "тыс. человек"
|
||||
},
|
||||
{
|
||||
"num": "3б",
|
||||
"name": "Люди, для которых недоступны транспортные услуги",
|
||||
"keywords": "транспортн",
|
||||
"unit": "тыс. человек"
|
||||
},
|
||||
{
|
||||
"num": "4а",
|
||||
"name": "Абоненты, для которых недоступны услуги связи",
|
||||
"keywords": "абонент",
|
||||
"unit": "тыс. человек"
|
||||
},
|
||||
{
|
||||
"num": "5а",
|
||||
"name": "Максимальное время недоступности государственной услуги",
|
||||
"keywords": "недоступна государственная услуга|недоступности государственной",
|
||||
"unit": "часов"
|
||||
},
|
||||
{
|
||||
"num": "5б",
|
||||
"name": "Время, в течение которого госуслуга не может быть оказана",
|
||||
"keywords": "регламентом|административным регламентом",
|
||||
"unit": "%"
|
||||
},
|
||||
{
|
||||
"num": "11б",
|
||||
"name": "Люди, подверженные вредным воздействиям на окружающую среду",
|
||||
"keywords": "вредным воздействиям",
|
||||
"unit": "тыс. человек"
|
||||
},
|
||||
{
|
||||
"num": "13а",
|
||||
"name": "Снижение объёма продукции ГОЗ",
|
||||
"keywords": "объем продукции|объём продукции",
|
||||
"unit": "%"
|
||||
},
|
||||
{
|
||||
"num": "13б",
|
||||
"name": "Увеличение времени изготовления единицы продукции ГОЗ",
|
||||
"keywords": "время изготовления",
|
||||
"unit": "%"
|
||||
},
|
||||
{
|
||||
"num": "14",
|
||||
"name": "Максимальное время недоступности ИС (оборона)",
|
||||
"keywords": "недоступна пользователю",
|
||||
"unit": "часов"
|
||||
},
|
||||
{
|
||||
"num": "2а",
|
||||
"name": "Территория нарушения жизнеобеспечения населения",
|
||||
"keywords": "территория.*жизнедеятельности|жизнедеятельности населения",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "3а",
|
||||
"name": "Территория нарушения транспортного сообщения",
|
||||
"keywords": "территория.*транспорт",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "3в",
|
||||
"name": "Типы нарушенных перевозок и грузов",
|
||||
"keywords": "типам нарушенных перевозок|типы нарушенных перевозок",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "3г",
|
||||
"name": "Категория объекта транспортной инфраструктуры",
|
||||
"keywords": "категорированию объектов транспортной|категорию объекта транспортной",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "4б",
|
||||
"name": "Уровень органа власти, для которого недоступны услуги связи",
|
||||
"keywords": "уровню органа государственной власти",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "6",
|
||||
"name": "Уровень государственного органа (невыполнение функции)",
|
||||
"keywords": "функционирования государственного органа|государственного органа в части",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "7",
|
||||
"name": "Уровень международного договора",
|
||||
"keywords": "международного договора",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "11а",
|
||||
"name": "Территория вредных воздействий на окружающую среду",
|
||||
"keywords": "территория.*окружающую среду|окружающая среда может",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "12",
|
||||
"name": "Уровень пункта управления (ситуационного центра)",
|
||||
"keywords": "пункта управления|ситуационного центра",
|
||||
"unit": "вариант"
|
||||
},
|
||||
{
|
||||
"num": "13.1",
|
||||
"name": "Статус в кооперации головного исполнителя ГОЗ",
|
||||
"keywords": "кооперации головного исполнителя|статусу в кооперации",
|
||||
"unit": "вариант"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -67,6 +67,8 @@ DEFAULT_SETTINGS = {
|
||||
_f('ОГРН', 'ogrn', keywords=r'огрн'),
|
||||
_f('КПП', 'kpp', keywords=r'кпп'),
|
||||
_f('Основной ОКВЭД', 'okved', keywords=r'основной оквэд'),
|
||||
_f('Дополнительные ОКВЭД', 'okvedExtra', keywords=r'дополнительные оквэд'),
|
||||
_f('Доменное имя и внешний сетевой адрес', 'domainName', keywords=r'доменное имя и внешний сетевой адрес'),
|
||||
_f('Должность руководителя', 'chiefPosition', keywords=r'должность руководителя'),
|
||||
_f('ФИО руководителя', 'chiefFio', keywords=r'фио руководителя'),
|
||||
_f('Должность ответственного за ПДн', 'ispdnPosition', keywords=r'должность.*защит.*информ|должность.*администрат'),
|
||||
@@ -117,7 +119,13 @@ def load_settings(path=None):
|
||||
merged = dict(DEFAULT_SETTINGS)
|
||||
for k, v in loaded.items():
|
||||
if k == 'fields' and isinstance(v, list):
|
||||
merged['fields'] = v
|
||||
# Мержим поля по var: поля из файла + недостающие из дефолта
|
||||
by_var = {f.get('var'): f for f in v}
|
||||
for df in DEFAULT_SETTINGS.get('fields', []):
|
||||
dv = df.get('var')
|
||||
if dv and dv not in by_var:
|
||||
by_var[dv] = df
|
||||
merged['fields'] = list(by_var.values())
|
||||
else:
|
||||
merged[k] = v
|
||||
return merged
|
||||
|
||||
+336
-168
@@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Модуль импорта из Excel (опросный лист 152-ФЗ)"""
|
||||
"""DokoGen — Модуль импорта из Excel (опросный лист 187-ФЗ)"""
|
||||
|
||||
import re
|
||||
import traceback
|
||||
import warnings
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Callable, Optional
|
||||
|
||||
@@ -96,13 +97,18 @@ def normalize_address(text: str) -> str:
|
||||
return ''.join(out)
|
||||
|
||||
|
||||
def clean_value(val):
|
||||
"""Очистка и нормализация значения ячейки Excel."""
|
||||
def clean_value(val, as_date=False):
|
||||
"""Очистка и нормализация значения ячейки Excel.
|
||||
|
||||
as_date=True — конвертировать серийный номер Excel в дату (только для
|
||||
полей-дат, напр. ogrnDate). Для финансовых полей НЕ вызывается,
|
||||
иначе число 50 000 руб. превратится в «дату» (см. аудит importer.py:105-109).
|
||||
"""
|
||||
if val is None:
|
||||
return ''
|
||||
if isinstance(val, (int, float)):
|
||||
# Попытка распознать дату (серийный номер Excel)
|
||||
if 10000 < val < 80000:
|
||||
# Попытка распознать дату (серийный номер Excel) — только для полей-дат
|
||||
if as_date and 10000 < val < 80000:
|
||||
serial = int(val)
|
||||
if serial > 60:
|
||||
serial -= 1
|
||||
@@ -122,8 +128,8 @@ def clean_value(val):
|
||||
return text
|
||||
|
||||
|
||||
def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
||||
"""Импорт данных из опросного листа Excel (152-ФЗ).
|
||||
def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
||||
"""Импорт данных из опросного листа Excel (187-ФЗ).
|
||||
|
||||
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
|
||||
Если None — файл import_settings.json рядом с программой; если его нет —
|
||||
@@ -141,12 +147,17 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
||||
data = {
|
||||
'fullName': '', 'shortName': '', 'addressLegal': '', 'addressActual': '',
|
||||
'email': '', 'phone': '', 'inn': '', 'ogrn': '', 'kpp': '',
|
||||
'okved': '', 'chiefPosition': '', 'chiefFio': '',
|
||||
'ispdnFio': '', 'ispdnPosition': '', 'paperDocuments': '', 'paperStorage': '',
|
||||
'okved': '', 'okvedExtra': '', 'chiefPosition': '', 'chiefFio': '',
|
||||
'ispdnFio': '', 'ispdnPosition': '',
|
||||
'ispdnDepartment': '', 'ispdnEmail': '', 'ispdnPhone': '',
|
||||
'informationSystems': [], 'commission': [], 'employeesAccess': [],
|
||||
}
|
||||
|
||||
# openpyxl не поддерживает расширение Data Validation (выпадающие списки ячеек)
|
||||
# и предупреждает об этом при каждом импорте — подавляем только это сообщение
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
'ignore', message='Data Validation extension is not supported')
|
||||
try:
|
||||
wb = openpyxl.load_workbook(filepath, data_only=True, read_only=False)
|
||||
except Exception as e:
|
||||
@@ -154,15 +165,30 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
||||
return data
|
||||
|
||||
try:
|
||||
_import_152_common(wb, data, settings, log_fn)
|
||||
_import_152_is_list(wb, data, settings, log_fn)
|
||||
_import_152_employees(wb, data, settings, log_fn)
|
||||
_import_187_common(wb, data, settings, log_fn)
|
||||
_import_187_is_list(wb, data, settings, log_fn)
|
||||
_import_187_employees(wb, data, settings, log_fn)
|
||||
except Exception as e:
|
||||
log_fn(f"❌ Ошибка при импорте: {e}")
|
||||
log_fn(traceback.format_exc())
|
||||
finally:
|
||||
wb.close()
|
||||
|
||||
# Ответы раздела «Физическая безопасность» (да/нет) — собираем в словарь,
|
||||
# чтобы текст организационных мер генерировался с учётом реальных средств.
|
||||
ps_keys = [
|
||||
'physicalGuard', 'physicalFireAlarm', 'physicalPanicButton',
|
||||
'physicalBars', 'physicalMetalDoors', 'physicalVideoSurveillance',
|
||||
'physicalVideoIntercom', 'physicalTurnstile', 'physicalVisitorLog',
|
||||
]
|
||||
ps = {}
|
||||
for k in ps_keys:
|
||||
v = str(data.get(k) or '').strip()
|
||||
if v:
|
||||
ps[k] = v
|
||||
if ps:
|
||||
data['physicalSecurity'] = ps
|
||||
|
||||
# Копирование полей ИСПДн в админа, если админ не заполнен
|
||||
for src, dst in [('ispdnFio', 'administratorFio'), ('ispdnFio', 'adminFio'),
|
||||
('ispdnPosition', 'administratorPosition'), ('ispdnPosition', 'adminPosition'),
|
||||
@@ -179,7 +205,28 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
||||
return data
|
||||
|
||||
|
||||
def _import_152_common(wb, data, settings, log_fn):
|
||||
def _extract_single_fio(text):
|
||||
"""Из строки, где может быть несколько ФИО (через запятую, точку с запятой,
|
||||
перевод строки или просто подряд), выделяет одно первое полное ФИО.
|
||||
|
||||
Пример: «Коростелев Павел Сергеевич Хрящев Сергей Владимирович …» → «Коростелев Павел Сергеевич»
|
||||
"""
|
||||
if not text:
|
||||
return text
|
||||
text = str(text).strip()
|
||||
# 1) Явные разделители — берём первую часть
|
||||
for sep in (',', ';', '\n', '\r'):
|
||||
if sep in text:
|
||||
text = text.split(sep)[0].strip()
|
||||
break
|
||||
# 2) Если осталось больше 3 слов — это несколько ФИО подряд, оставляем первые 3
|
||||
words = [w for w in text.split() if w]
|
||||
if len(words) > 3:
|
||||
text = ' '.join(words[:3])
|
||||
return text.strip()
|
||||
|
||||
|
||||
def _import_187_common(wb, data, settings, log_fn):
|
||||
"""Импорт общих сведений по словарю настроек.
|
||||
|
||||
Для каждого поля: сначала пробуем конкретную ячейку (cell),
|
||||
@@ -226,8 +273,10 @@ def _import_152_common(wb, data, settings, log_fn):
|
||||
if sn.lower() == sheet_part.lower():
|
||||
target_ws = wb[sn]
|
||||
break
|
||||
val = clean_value(cell_value(target_ws, coord))
|
||||
val = clean_value(cell_value(target_ws, coord), as_date=('дата' in name.lower() or 'date' in key.lower()))
|
||||
if val:
|
||||
if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'):
|
||||
val = _extract_single_fio(val)
|
||||
data[key] = val
|
||||
log_fn(f" {name} [ячейка {coord}]: {val}")
|
||||
continue
|
||||
@@ -242,13 +291,21 @@ def _import_152_common(wb, data, settings, log_fn):
|
||||
for field_lower, val in row_cache.items():
|
||||
if rx.search(field_lower):
|
||||
if val:
|
||||
if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'):
|
||||
val = _extract_single_fio(val)
|
||||
elif 'дата' in name.lower() or 'date' in key.lower():
|
||||
val = clean_value(val, as_date=True)
|
||||
data[key] = val
|
||||
log_fn(f" {name} [по ключу]: {val}")
|
||||
break
|
||||
|
||||
|
||||
def _import_152_is_list(wb, data, settings, log_fn):
|
||||
"""Импорт информационных систем из остальных листов."""
|
||||
def _import_187_is_list(wb, data, settings, log_fn):
|
||||
"""Импорт объектов КИИ из остальных листов (ОБЪЕКТ №1, №2, ...).
|
||||
|
||||
Заполняет поля 236 приказа ФСТЭК: сфера, назначение, тип, архитектура,
|
||||
сети электросвязи, ПО, СЗИ, сведения о субъекте.
|
||||
"""
|
||||
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
||||
is_sheets = []
|
||||
for sn in wb.sheetnames:
|
||||
@@ -257,134 +314,42 @@ def _import_152_is_list(wb, data, settings, log_fn):
|
||||
continue
|
||||
is_sheets.append(sn)
|
||||
if not is_sheets:
|
||||
log_fn("⚠ Листы ИС не найдены")
|
||||
log_fn("⚠ Листы объектов КИИ не найдены")
|
||||
return
|
||||
|
||||
is_list = []
|
||||
for sheet_name in is_sheets:
|
||||
ws = wb[sheet_name]
|
||||
rows = []
|
||||
raw_rows = []
|
||||
for row in ws.iter_rows(min_row=1, values_only=True):
|
||||
raw_vals = list(row)
|
||||
vals = [clean_value(c) for c in row]
|
||||
field = (vals[0] or '').strip() if vals else ''
|
||||
if not field:
|
||||
continue
|
||||
rows.append(vals)
|
||||
raw_rows.append(raw_vals)
|
||||
|
||||
if len(rows) < 2:
|
||||
continue
|
||||
|
||||
is_obj = {
|
||||
'name': '', 'description': '', 'room': '', 'employee': '', 'position': '',
|
||||
'personalDataList': [], 'pd_subjects_list': [], 'pd_actions': [],
|
||||
'usersList': [], 'isInternet': False, 'personalDataCount': 'менее 100 000',
|
||||
'defense_tools': [], 'processing_mode': '',
|
||||
'name': '', 'description': '', 'address': '', 'usersList': [],
|
||||
'isInternet': False, 'defense_tools': [], 'software': '',
|
||||
# 236 приказ
|
||||
'sphere': '', 'purpose': '', 'object_type': '', 'architecture': '',
|
||||
'typical_object': '',
|
||||
'subject_name': '', 'subject_address': '',
|
||||
'subject_element': '',
|
||||
'subject_inn': '', 'subject_kpp': '',
|
||||
'net_category': '', 'net_operator': '', 'net_purpose': '', 'net_method': '',
|
||||
'hw_sw': '', 'os_sw': '', 'app_sw': '',
|
||||
'attacker_category': '', 'threats': '', 'incidents': '',
|
||||
'criteria_values': '', 'criteria_justification': '',
|
||||
'org_measures': '', 'tech_measures': '',
|
||||
}
|
||||
|
||||
current_section = 'header'
|
||||
pd_items_raw = []
|
||||
subjects_raw = []
|
||||
actions_raw = []
|
||||
|
||||
for vals in rows:
|
||||
field = (vals[0] or '').strip()
|
||||
field_lower = field.lower()
|
||||
val_b = clean_value(vals[1]) if len(vals) > 1 else ''
|
||||
is_checked = val_b.upper() in ('ДА', 'YES', '✓', '✔', '1', 'TRUE', 'ЕСТЬ')
|
||||
|
||||
if 'цель обработки пд' in field_lower or ('цель обработки' in field_lower and ':' in field):
|
||||
current_section = 'purpose'
|
||||
continue
|
||||
if 'перечисленные пдн принадлежат' in field_lower or 'принадлеж' in field_lower:
|
||||
current_section = 'subjects'
|
||||
continue
|
||||
if 'структура ис' in field_lower or 'структура информационной' in field_lower:
|
||||
if val_b:
|
||||
is_obj['structure'] = val_b
|
||||
elif ':' in field:
|
||||
# Значение может быть записано в той же ячейке после двоеточия
|
||||
tail = field.split(':', 1)[1].strip()
|
||||
if tail and 'выбрать' not in tail.lower():
|
||||
is_obj['structure'] = tail
|
||||
continue
|
||||
if 'названия программ' in field_lower or ('программ' in field_lower and 'ос взаимодейств' in field_lower):
|
||||
if val_b:
|
||||
is_obj['software'] = val_b
|
||||
continue
|
||||
if 'локальную сеть' in field_lower:
|
||||
is_obj['is_has_lan'] = is_checked
|
||||
continue
|
||||
if 'количество записей' in field_lower or 'количество субъект' in field_lower:
|
||||
if val_b:
|
||||
is_obj['personalDataCount'] = val_b
|
||||
continue
|
||||
if 'перечень действий с пд' in field_lower or ('действи' in field_lower and 'пд' in field_lower):
|
||||
current_section = 'actions'
|
||||
continue
|
||||
if 'способ обработки пд' in field_lower:
|
||||
if val_b:
|
||||
is_obj['processing_mode'] = val_b
|
||||
continue
|
||||
if 'интернет' in field_lower:
|
||||
is_obj['isInternet'] = is_checked
|
||||
continue
|
||||
|
||||
if current_section == 'header':
|
||||
if 'перечень пд' in field_lower:
|
||||
current_section = 'pd_items'
|
||||
elif 'название ис' in field_lower or 'наименование ис' in field_lower or 'наименование информационной' in field_lower:
|
||||
is_obj['name'] = val_b
|
||||
log_fn(f" ИС: {val_b}")
|
||||
elif 'описание ис' in field_lower or 'описание информационной' in field_lower:
|
||||
if val_b:
|
||||
is_obj['description'] = val_b
|
||||
log_fn(f" Описание: {val_b[:60]}")
|
||||
continue
|
||||
elif 'сотрудник' in field_lower and 'должност' not in field_lower:
|
||||
is_obj['employee'] = val_b
|
||||
if val_b:
|
||||
parts = [p.strip() for p in val_b.replace('\n', ';').split(';') if p.strip()]
|
||||
for part in parts:
|
||||
cleaned = part.strip()
|
||||
if ':' in cleaned and len(cleaned.split(':')[0]) < 20:
|
||||
cleaned = cleaned.split(':', 1)[1].strip()
|
||||
if cleaned and cleaned not in is_obj['usersList']:
|
||||
is_obj['usersList'].append(cleaned)
|
||||
elif 'должность сотрудника' in field_lower:
|
||||
is_obj['position'] = val_b
|
||||
elif 'наименование отдела' in field_lower:
|
||||
pass
|
||||
elif 'номер кабинета' in field_lower:
|
||||
is_obj['room'] = val_b
|
||||
elif 'значение' in field_lower or 'примечание' in field_lower:
|
||||
continue
|
||||
elif val_b and 'выбрать' not in field_lower:
|
||||
if not is_obj['description']:
|
||||
is_obj['description'] = f"{field}: {val_b}"
|
||||
else:
|
||||
is_obj['description'] += f"\n{field}: {val_b}"
|
||||
elif current_section == 'pd_items':
|
||||
if is_checked and 'выбрать' not in field_lower and field_lower != 'значение':
|
||||
pd_items_raw.append(field)
|
||||
if 'цель обработки' in field_lower:
|
||||
current_section = 'purpose'
|
||||
elif current_section == 'purpose':
|
||||
purpose_val = val_b or field or ''
|
||||
if purpose_val and 'выбрать' not in purpose_val.lower() and ':' not in purpose_val[:40]:
|
||||
is_obj['purpose'] = purpose_val
|
||||
log_fn(f" Цель: {purpose_val[:80]}")
|
||||
current_section = 'header'
|
||||
elif current_section == 'subjects':
|
||||
if is_checked and 'выбрать' not in field_lower:
|
||||
subjects_raw.append(field)
|
||||
if 'количество записей' in field_lower or 'действи' in field_lower:
|
||||
continue
|
||||
elif current_section == 'actions':
|
||||
if is_checked and 'выбрать' not in field_lower and 'защита' not in field_lower[:20]:
|
||||
actions_raw.append(field)
|
||||
if 'защита' in field_lower and 'действи' not in field_lower:
|
||||
current_section = 'defense'
|
||||
elif current_section == 'defense':
|
||||
defense_keywords = {
|
||||
'антивирус': 'Антивирус',
|
||||
'крипто': 'СКЗИ',
|
||||
@@ -395,59 +360,262 @@ def _import_152_is_list(wb, data, settings, log_fn):
|
||||
'доверен.*загрузк': 'Доверенная загрузка',
|
||||
'гарантирован': 'Доверенная загрузка',
|
||||
}
|
||||
matched_label = None
|
||||
|
||||
HINT_MARKERS = [
|
||||
'формулировка должна', 'например', 'пример:', 'формат:', 'выберите из',
|
||||
'если не используются', 'при необходимости можно', 'указать', 'вписать',
|
||||
'если адресов несколько', 'по логике', 'произвольное описание',
|
||||
]
|
||||
|
||||
def _is_hint(text):
|
||||
"""True, если значение похоже на подсказку/образец из примечания."""
|
||||
low = (text or '').lower()
|
||||
for m in HINT_MARKERS:
|
||||
if m in low:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _set(field_lower, key, val):
|
||||
"""Записать значение, если ключевое слово найдено в вопросе
|
||||
и значение не является подсказкой из примечания."""
|
||||
if val and 'выбрать' not in field_lower and not _is_hint(val):
|
||||
is_obj[key] = val
|
||||
return True
|
||||
return False
|
||||
|
||||
def _clean_multiline(text):
|
||||
"""Чистит многострочное значение, сохраняя переносы строк."""
|
||||
if text is None:
|
||||
return ''
|
||||
s = str(text).replace('\r\n', '\n').replace('\r', '\n')
|
||||
lines = [ln.strip() for ln in s.split('\n')]
|
||||
return '\n'.join(ln for ln in lines if ln)
|
||||
|
||||
def _split_entries(text):
|
||||
"""Разбивает список средств защиты на ОТДЕЛЬНЫЕ программы.
|
||||
|
||||
В опроснике каждая программа записана с новой строки (разделитель \n),
|
||||
также поддерживаются ';' и ','. Название программы при этом НЕ режется
|
||||
(напр. 'Kaspersky Endpoint Security' остаётся одним средством, KES ≠ KSC).
|
||||
"""
|
||||
if text is None:
|
||||
return []
|
||||
# 1) разбиваем по переносам строк и ';'
|
||||
parts = re.split(r'[\n\r;]+', str(text))
|
||||
out = []
|
||||
for p in parts:
|
||||
# 2) дополнительно по запятой (если список через запятую)
|
||||
for sub in re.split(r'\s*,\s*', p):
|
||||
sub = sub.strip()
|
||||
if not sub:
|
||||
continue
|
||||
# 3) запасной вариант: слипшиеся без разделителя «15.1.0.22239Kaspersky»
|
||||
sub_parts = re.split(r'(?<=[0-9.])(?=[А-ЯЁA-Z])', sub)
|
||||
for s in sub_parts:
|
||||
s = s.strip()
|
||||
if not s:
|
||||
continue
|
||||
cleaned = clean_value(s)
|
||||
if cleaned and cleaned not in out:
|
||||
out.append(cleaned)
|
||||
return out
|
||||
|
||||
for idx, vals in enumerate(rows):
|
||||
field = (vals[0] or '').strip()
|
||||
field_lower = field.lower()
|
||||
raw_row = raw_rows[idx] if idx < len(raw_rows) else vals
|
||||
raw_b = raw_row[1] if len(raw_row) > 1 else None # сырое значение (с переносами строк!)
|
||||
val_b = clean_value(raw_b) if raw_b is not None else ''
|
||||
is_checked = val_b.upper() in ('ДА', 'YES', '✓', '✔', '1', 'TRUE', 'ЕСТЬ')
|
||||
|
||||
# --- Наименование объекта ---
|
||||
if 'наименование объекта' in field_lower or 'название объекта' in field_lower:
|
||||
is_obj['name'] = val_b
|
||||
log_fn(f" Объект КИИ: {val_b}")
|
||||
continue
|
||||
# --- Адрес размещения объекта (в отдельное поле, не в описание) ---
|
||||
if 'адрес размещения' in field_lower or 'адрес расположения' in field_lower or 'местонахождение объекта' in field_lower:
|
||||
is_obj['address'] = val_b
|
||||
log_fn(f" Адрес объекта: {val_b[:60]}")
|
||||
continue
|
||||
# --- Описание ---
|
||||
if 'описание окки' in field_lower or 'описание объекта' in field_lower:
|
||||
if val_b:
|
||||
is_obj['description'] = val_b
|
||||
continue
|
||||
# --- Сфера деятельности ---
|
||||
if 'сфера' in field_lower and 'деятельност' in field_lower:
|
||||
if _set(field_lower, 'sphere', val_b):
|
||||
log_fn(f" Сфера: {val_b[:60]}")
|
||||
continue
|
||||
# --- Назначение объекта ---
|
||||
if 'назначение объекта' in field_lower:
|
||||
if _set(field_lower, 'purpose', val_b):
|
||||
log_fn(f" Назначение: {val_b[:60]}")
|
||||
continue
|
||||
# --- Тип объекта ---
|
||||
if 'тип объекта' in field_lower:
|
||||
if _set(field_lower, 'object_type', val_b):
|
||||
log_fn(f" Тип объекта: {val_b[:60]}")
|
||||
continue
|
||||
# --- Архитектура ---
|
||||
if 'архитектура' in field_lower:
|
||||
if _set(field_lower, 'architecture', val_b):
|
||||
log_fn(f" Архитектура: {val_b[:60]}")
|
||||
continue
|
||||
# --- Типовой отраслевой объект ---
|
||||
if 'типового отраслевого' in field_lower or 'типовой отраслевой' in field_lower:
|
||||
if _set(field_lower, 'typical_object', val_b):
|
||||
log_fn(f" Типовой объект: {val_b[:60]}")
|
||||
continue
|
||||
# --- Субъект ---
|
||||
if 'наименование организации эксплуатирующ' in field_lower or ('наименование субъекта' in field_lower):
|
||||
if _set(field_lower, 'subject_name', val_b):
|
||||
log_fn(f" Субъект: {val_b[:60]}")
|
||||
continue
|
||||
if 'адрес местонахождения' in field_lower:
|
||||
if _set(field_lower, 'subject_address', val_b):
|
||||
log_fn(f" Адрес субъекта: {val_b[:60]}")
|
||||
continue
|
||||
if 'инн лица, эксплуатирующего' in field_lower or 'инн субъекта' in field_lower:
|
||||
if _set(field_lower, 'subject_inn', val_b):
|
||||
log_fn(f" ИНН субъекта: {val_b[:60]}")
|
||||
continue
|
||||
if 'кпп его обособленных' in field_lower or 'кпп субъекта' in field_lower:
|
||||
if _set(field_lower, 'subject_kpp', val_b):
|
||||
log_fn(f" КПП субъекта: {val_b[:60]}")
|
||||
continue
|
||||
if 'элемент (компонент) объекта' in field_lower or 'элемент объекта' in field_lower:
|
||||
if _set(field_lower, 'subject_element', val_b):
|
||||
log_fn(f" Элемент объекта: {val_b[:60]}")
|
||||
continue
|
||||
# --- Сети электросвязи ---
|
||||
if 'категория сети электросвязи' in field_lower:
|
||||
if _set(field_lower, 'net_category', val_b):
|
||||
log_fn(f" Категория сети: {val_b[:60]}")
|
||||
continue
|
||||
if 'наименование провайдера' in field_lower or 'оператор связи' in field_lower:
|
||||
if _set(field_lower, 'net_operator', val_b):
|
||||
log_fn(f" Оператор: {val_b[:60]}")
|
||||
continue
|
||||
if 'цель взаимодействия' in field_lower:
|
||||
if _set(field_lower, 'net_purpose', val_b):
|
||||
log_fn(f" Цель взаимодействия: {val_b[:60]}")
|
||||
continue
|
||||
if 'способ взаимодействия' in field_lower:
|
||||
if _set(field_lower, 'net_method', val_b):
|
||||
log_fn(f" Способ взаимодействия: {val_b[:60]}")
|
||||
continue
|
||||
# --- ПО и аппаратные средства (сохраняем переносы строк) ---
|
||||
if 'наименования программно-аппаратных средств' in field_lower:
|
||||
if raw_b and 'выбрать' not in field_lower and not _is_hint(raw_b):
|
||||
is_obj['hw_sw'] = _clean_multiline(raw_b)
|
||||
log_fn(f" ПО/аппаратные средства: {str(raw_b)[:60]}")
|
||||
continue
|
||||
if 'операционная' in field_lower and 'система' in field_lower:
|
||||
if raw_b and 'выбрать' not in field_lower and not _is_hint(raw_b):
|
||||
is_obj['os_sw'] = _clean_multiline(raw_b)
|
||||
log_fn(f" ОС: {str(raw_b)[:60]}")
|
||||
continue
|
||||
if 'прикладные программы' in field_lower:
|
||||
if raw_b and 'выбрать' not in field_lower and not _is_hint(raw_b):
|
||||
is_obj['app_sw'] = _clean_multiline(raw_b)
|
||||
log_fn(f" Прикладные программы: {str(raw_b)[:60]}")
|
||||
continue
|
||||
if 'общесистемн' in field_lower:
|
||||
if raw_b and 'выбрать' not in field_lower and not _is_hint(raw_b):
|
||||
is_obj['os_sw'] = _clean_multiline(raw_b)
|
||||
log_fn(f" Общесистемное ПО: {str(raw_b)[:60]}")
|
||||
continue
|
||||
# --- Безопасность / 236 приказ ---
|
||||
if 'категория нарушителя' in field_lower:
|
||||
if _set(field_lower, 'attacker_category', val_b):
|
||||
log_fn(f" Категория нарушителя: {val_b[:60]}")
|
||||
continue
|
||||
if 'основные угрозы' in field_lower or 'угрозы безопасности' in field_lower:
|
||||
if _set(field_lower, 'threats', val_b):
|
||||
log_fn(f" Угрозы: {val_b[:60]}")
|
||||
continue
|
||||
if 'компьютерных инцидентов' in field_lower or 'инцидент' in field_lower:
|
||||
if _set(field_lower, 'incidents', val_b):
|
||||
log_fn(f" Инциденты: {val_b[:60]}")
|
||||
continue
|
||||
if 'показателей критериев' in field_lower or 'показатели критериев' in field_lower:
|
||||
if _set(field_lower, 'criteria_values', val_b):
|
||||
log_fn(f" Показатели: {val_b[:60]}")
|
||||
continue
|
||||
if 'обоснование полученных' in field_lower or 'обоснование значений' in field_lower:
|
||||
if _set(field_lower, 'criteria_justification', val_b):
|
||||
log_fn(f" Обоснование: {val_b[:60]}")
|
||||
continue
|
||||
if 'организационные меры' in field_lower:
|
||||
if _set(field_lower, 'org_measures', val_b):
|
||||
log_fn(f" Орг. меры: {val_b[:60]}")
|
||||
continue
|
||||
if 'технические меры' in field_lower:
|
||||
if _set(field_lower, 'tech_measures', val_b):
|
||||
log_fn(f" Тех. меры: {val_b[:60]}")
|
||||
continue
|
||||
# --- Показатели критериев значимости (ПП-127) → criterion_input ---
|
||||
# Строки вида «Показатель 1. …» / «Показатель 2а. …» / «Показатель 13.1. …»
|
||||
m_crit = re.match(r'^показател[ья]?\s*(\d+(?:\.\d+)?)([а-яА-Я]?)', field_lower)
|
||||
if m_crit:
|
||||
_num_crit = m_crit.group(1) + m_crit.group(2)
|
||||
# нормализация: «10.1» → «10.1», «13.1» → «13.1», «2а» → «2а»
|
||||
_num_crit = _num_crit.replace(' .', '.').strip()
|
||||
if val_b and 'выбрать' not in field_lower and not _is_hint(val_b):
|
||||
is_obj.setdefault('criterion_input', {})[_num_crit] = str(val_b).strip()
|
||||
log_fn(f" Показатель {_num_crit}: {str(val_b)[:60]}")
|
||||
continue
|
||||
if 'интернет' in field_lower:
|
||||
is_obj['isInternet'] = is_checked
|
||||
continue
|
||||
if 'сотрудник' in field_lower and 'должност' not in field_lower:
|
||||
if val_b:
|
||||
parts = [p.strip() for p in val_b.replace('\n', ';').split(';') if p.strip()]
|
||||
for part in parts:
|
||||
cleaned = part.strip()
|
||||
if ':' in cleaned and len(cleaned.split(':')[0]) < 20:
|
||||
cleaned = cleaned.split(':', 1)[1].strip()
|
||||
if cleaned and cleaned not in is_obj['usersList']:
|
||||
is_obj['usersList'].append(cleaned)
|
||||
continue
|
||||
# --- Средства защиты ---
|
||||
matched = False
|
||||
for kw, label in defense_keywords.items():
|
||||
if re.search(kw, field_lower):
|
||||
matched_label = label
|
||||
if val_b and 'выбрать' not in field_lower:
|
||||
for tool in _split_entries(raw_b if raw_b is not None else val_b):
|
||||
if tool not in is_obj['defense_tools']:
|
||||
is_obj['defense_tools'].append(tool)
|
||||
matched = True
|
||||
break
|
||||
if matched_label and val_b and 'выбрать' not in field_lower:
|
||||
# Без префикса категории: просто название средства (Kaspersky, КриптоПро)
|
||||
is_obj['defense_tools'].append(val_b)
|
||||
elif matched_label:
|
||||
pass
|
||||
elif 'куда' in field_lower or 'передаются' in field_lower:
|
||||
current_section = 'header'
|
||||
elif 'способ обработки' in field_lower or 'локальную сеть' in field_lower or 'интернет' in field_lower:
|
||||
current_section = 'header'
|
||||
|
||||
if pd_items_raw:
|
||||
is_obj['personalDataList'] = pd_items_raw
|
||||
log_fn(f" ПДн: {len(pd_items_raw)}")
|
||||
if subjects_raw:
|
||||
is_obj['pd_subjects_list'] = subjects_raw
|
||||
log_fn(f" Субъектов: {len(subjects_raw)}")
|
||||
if actions_raw:
|
||||
is_obj['pd_actions'] = actions_raw
|
||||
log_fn(f" Действий: {len(actions_raw)}")
|
||||
if matched:
|
||||
continue
|
||||
if val_b and 'выбрать' not in field_lower and field_lower not in ('значение', 'примечание'):
|
||||
# Балансовая стоимость объекта КИИ — в отдельное поле
|
||||
if 'балансовая стоимость' in field_lower or 'стоимость объекта' in field_lower:
|
||||
is_obj['balanceValue'] = val_b
|
||||
continue
|
||||
# Остальные заполненные поля — в описание (адрес и т.д.)
|
||||
if not is_obj['description']:
|
||||
is_obj['description'] = f"{field}: {val_b}"
|
||||
else:
|
||||
is_obj['description'] += f"\n{field}: {val_b}"
|
||||
|
||||
if not is_obj['name']:
|
||||
is_obj['name'] = sheet_name.strip()
|
||||
log_fn(f" ⚠ ИС без названия — использовано имя листа: {is_obj['name']}")
|
||||
|
||||
# Автоопределение категорий ПДн
|
||||
pd_cats = []
|
||||
all_pd_text = ' '.join(p.lower() for p in pd_items_raw)
|
||||
special_kw = ['состоян.*здоров', 'национальн', 'политическ', 'религиозн', 'философ', 'судимост', 'интимн']
|
||||
if any(re.search(kw, all_pd_text) for kw in special_kw):
|
||||
pd_cats.append('специальные')
|
||||
bio_kw = ['биометрическ', 'изображен.*лиц', 'голос.*человек', 'папилляр', 'дактилоскоп', 'фото.*изображен']
|
||||
if any(re.search(kw, all_pd_text) for kw in bio_kw):
|
||||
pd_cats.append('биометрические')
|
||||
if pd_items_raw:
|
||||
pd_cats.append('иные')
|
||||
if pd_cats:
|
||||
is_obj['personalDataCategory'] = pd_cats
|
||||
log_fn(f" Категории ПДн: {', '.join(pd_cats)}")
|
||||
log_fn(f" ⚠ Объект без названия — использовано имя листа: {is_obj['name']}")
|
||||
|
||||
is_list.append(is_obj)
|
||||
log_fn(f" ✅ {is_obj['name']} — импортирована")
|
||||
log_fn(f" ✅ {is_obj['name']} — импортирован")
|
||||
|
||||
if is_list:
|
||||
data['informationSystems'] = is_list
|
||||
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_187_employees(wb, data, settings, log_fn):
|
||||
"""Импорт комиссии и сотрудников из листа «Сотрудники»."""
|
||||
sheet_name = None
|
||||
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
|
||||
@@ -497,7 +665,7 @@ def _import_152_employees(wb, data, settings, log_fn):
|
||||
if 'перечень сотрудников' in a_lower:
|
||||
in_commission = False
|
||||
in_employees = True
|
||||
log_fn(" Секция: сотрудники с доступом к ПДн")
|
||||
log_fn(" Секция: сотрудники с доступом к объектам КИИ")
|
||||
continue
|
||||
if a_lower in ('№ п/п', '№', 'номер', ''):
|
||||
continue
|
||||
@@ -519,7 +687,7 @@ def _import_152_employees(wb, data, settings, log_fn):
|
||||
commission.append({
|
||||
'role': role,
|
||||
'position': col_b,
|
||||
'fio': col_c.replace('\n', ' '),
|
||||
'fio': _extract_single_fio(col_c.replace('\n', ' ')),
|
||||
})
|
||||
|
||||
if in_employees:
|
||||
@@ -528,7 +696,7 @@ def _import_152_employees(wb, data, settings, log_fn):
|
||||
continue
|
||||
employee = {
|
||||
'position': col_b,
|
||||
'fio': fio.replace('\n', ' '),
|
||||
'fio': _extract_single_fio(fio.replace('\n', ' ')),
|
||||
}
|
||||
if col_e:
|
||||
employee['is_list'] = [s.strip() for s in col_e.split('\n') if s.strip()]
|
||||
|
||||
+143
-35
@@ -1,13 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Модели данных (только 152-ФЗ, только нужные поля)"""
|
||||
"""DokoGen — Модели данных (только 187-ФЗ, только нужные поля)"""
|
||||
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import List, Dict, Any
|
||||
|
||||
|
||||
def _clean_single_fio(value):
|
||||
"""Оставить одно первое ФИО, если в строке их несколько."""
|
||||
if not value:
|
||||
return value
|
||||
value = str(value).strip()
|
||||
for sep in (',', ';', '\n', '\r'):
|
||||
if sep in value:
|
||||
value = value.split(sep)[0].strip()
|
||||
break
|
||||
words = [w for w in value.split() if w]
|
||||
if len(words) > 3:
|
||||
value = ' '.join(words[:3])
|
||||
return value.strip()
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommissionMember:
|
||||
"""Член комиссии по ПДн"""
|
||||
"""Член комиссии по категорированию объектов КИИ"""
|
||||
role: str = "член комиссии"
|
||||
position: str = ""
|
||||
fio: str = ""
|
||||
@@ -15,41 +30,66 @@ class CommissionMember:
|
||||
|
||||
@dataclass
|
||||
class InformationSystem:
|
||||
"""Информационная система (обработка ПДн)"""
|
||||
"""Объект КИИ (вместо информационной системы 152-ФЗ)"""
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
is_local_network: bool = False
|
||||
is_internet: bool = False
|
||||
defence_level: str = ""
|
||||
threat_type: str = "3"
|
||||
category: str = ""
|
||||
pd_list: str = ""
|
||||
pd_count: str = "менее 100 000"
|
||||
users: str = ""
|
||||
defense_tools_list: List[str] = field(default_factory=list)
|
||||
processing_modes: str = ""
|
||||
subject_type: str = "работники организации"
|
||||
pd_actions_list: List[str] = field(default_factory=list)
|
||||
pd_subjects_list: List[str] = field(default_factory=list)
|
||||
personal_data_list: List[str] = field(default_factory=list)
|
||||
users_list: List[str] = field(default_factory=list)
|
||||
personal_data_category: List[str] = field(default_factory=list)
|
||||
purpose: str = ""
|
||||
room: str = ""
|
||||
structure: str = "" # Структура информационной системы
|
||||
category: str = "" # Категория значимости объекта КИИ: 1 / 2 / 3 / без категории
|
||||
users_list: List[str] = field(default_factory=list) # Пользователи ОКИИ
|
||||
defense_tools_list: List[str] = field(default_factory=list) # Средства защиты
|
||||
purpose: str = "" # Назначение объекта (п. 236)
|
||||
structure: str = "" # Структура объекта КИИ
|
||||
software: str = "" # Программное обеспечение
|
||||
|
||||
# ---- 236 приказ ФСТЭК: сведения об объекте ----
|
||||
sphere: str = "" # Сфера (область) деятельности
|
||||
object_type: str = "" # Тип объекта (ИС, АСУ, ИТКС)
|
||||
architecture: str = "" # Архитектура объекта
|
||||
typical_object: str = "" # Наименование типового отраслевого объекта КИИ
|
||||
|
||||
@dataclass
|
||||
class PaperDocument:
|
||||
"""Бумажный документ с ПДн"""
|
||||
name: str = ""
|
||||
storage: str = ""
|
||||
# Субъект (организация, эксплуатирующая объект — как в опроснике)
|
||||
subject_name: str = "" # Наименование организации, эксплуатирующей объект
|
||||
subject_address: str = "" # Адрес местонахождения
|
||||
subject_element: str = "" # Элемент (компонент) объекта, который эксплуатируется лицом
|
||||
subject_inn: str = "" # ИНН лица, эксплуатирующего объект
|
||||
subject_kpp: str = "" # КПП его обособленных подразделений
|
||||
|
||||
# Сети электросвязи
|
||||
net_category: str = "" # Категория сети электросвязи
|
||||
net_operator: str = "" # Оператор связи и (или) провайдер хостинга
|
||||
net_purpose: str = "" # Цель взаимодействия с сетью электросвязи
|
||||
net_method: str = "" # Способ взаимодействия с указанием типа доступа
|
||||
|
||||
# Программно-аппаратные средства
|
||||
hw_sw: str = "" # Наименования программно-аппаратных средств и их количество
|
||||
os_sw: str = "" # Общесистемное ПО (ОС, средства виртуализации)
|
||||
app_sw: str = "" # Прикладные программы
|
||||
|
||||
# Балансовая стоимость объекта КИИ (опросник)
|
||||
balance_value: str = "" # Балансовая стоимость объекта КИИ в рублях
|
||||
|
||||
# Безопасность (236 приказ)
|
||||
attacker_category: str = "" # Категория нарушителя (внешний/внутренний)
|
||||
attacker_level: str = "" # Уровень возможностей нарушителя (Н1-Н4, Методика ФСТЭК 05.02.2021)
|
||||
attacker_types: str = "" # Виды нарушителей (\n — разделитель, п. 5.1.3 Методики)
|
||||
threats: str = "" # Основные угрозы безопасности информации (\n — разделитель)
|
||||
incidents: str = "" # Типы компьютерных инцидентов (\n — разделитель)
|
||||
criteria_values: str = "" # Показатели критериев значимости (\n — разделитель)
|
||||
criteria_justification: str = "" # Обоснование полученных значений
|
||||
org_measures: str = "" # Организационные меры (\n — разделитель)
|
||||
tech_measures: str = "" # Технические меры (\n — разделитель)
|
||||
|
||||
# Введённые значения показателей ПП-127 для авторасчёта: {num: значение}
|
||||
# num — «1», «2а», «2б», …, «14»; значение — число (количественные)
|
||||
# или выбранный вариант (качественные). Заполняется окном авторасчёта,
|
||||
# импортом из опросника или вручную; используется в _build_categorization_indicators.
|
||||
criterion_input: Dict[str, str] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Company:
|
||||
"""Организация — основной объект (152-ФЗ)"""
|
||||
"""Организация — основной объект (187-ФЗ)"""
|
||||
# Полное название
|
||||
full_name: str = ""
|
||||
company_name_1: str = "" # именительный
|
||||
@@ -75,6 +115,9 @@ class Company:
|
||||
ogrn: str = ""
|
||||
ogrn_date: str = ""
|
||||
kpp: str = ""
|
||||
okved: str = "" # Основной ОКВЭД
|
||||
okved_extra: List[str] = field(default_factory=list) # Дополнительные ОКВЭД
|
||||
domain_name: str = "" # Доменное имя и внешний сетевой адрес
|
||||
|
||||
# Должностные лица
|
||||
chief_position: str = ""
|
||||
@@ -88,15 +131,45 @@ class Company:
|
||||
contract_number: str = ""
|
||||
contract_date: str = ""
|
||||
|
||||
# Финансовые показатели организации (опросник)
|
||||
profit: str = "" # Сумма прибыли за предыдущий год
|
||||
profit_tax: str = "" # Сумма налога с прибыли за предыдущий год
|
||||
|
||||
# Показатель 8 (ущерб субъекту КИИ): доходы за 5 лет и суммарный ущерб
|
||||
# В форме — одна строка «Доход за 5 лет (суммарно)»; по годам разбивает сам оператор.
|
||||
income_5y: str = "" # Доход за 5 лет суммарно (руб.)
|
||||
income_year_1: str = "" # Доход за 5 лет назад (руб.) — обратная совместимость
|
||||
income_year_2: str = "" # Доход за 4 года назад (руб.)
|
||||
income_year_3: str = "" # Доход за 3 года назад (руб.)
|
||||
income_year_4: str = "" # Доход за 2 года назад (руб.)
|
||||
income_year_5: str = "" # Доход за прошлый год (руб.)
|
||||
income_loss: str = "" # Снижение уровня дохода (суммарный ущерб) от нарушения
|
||||
# критических процессов (руб.) — ΔДоходов для п. 8
|
||||
|
||||
# Показатель 9: компоненты ΔБ (унификация автозаполнения и авторасчёта)
|
||||
ndpi: str = "" # Снижение выплат от налога на добычу ПИ ΔНДПИ (руб.)
|
||||
div: str = "" # Снижение выплат от дивидендов ΔДИВ (руб.)
|
||||
vso: str = "" # Снижение выплат сторонним организациям ΔВСО (руб.)
|
||||
ns: str = "" # Снижение сборов ΔНС (руб.)
|
||||
|
||||
# Показатель 10 (прекращение финансовых операций): операции за год (млн ед.)
|
||||
ops_count: str = "" # Количество операций за прошедший год (млн ед.)
|
||||
calendar_days: str = "" # Количество календарных дней в году (по умолчанию 365)
|
||||
criterion10_value: str = "" # Значение показателя 10.1–10.7 (в своих единицах)
|
||||
|
||||
# Списки
|
||||
commission: List[CommissionMember] = field(default_factory=list)
|
||||
information_systems: List[InformationSystem] = field(default_factory=list)
|
||||
paper_documents_list: List[PaperDocument] = field(default_factory=list)
|
||||
employees_access: List[Dict[str, str]] = field(default_factory=list)
|
||||
|
||||
# Наименование структурного подразделения по безопасности
|
||||
security_department: str = ""
|
||||
|
||||
# Ответы раздела «Физическая безопасность» опросника (да/нет):
|
||||
# охрана, пожарная сигнализация, тревожная кнопка, решётки, металлические двери,
|
||||
# видеонаблюдение, видеодомофон, турникет, журнал посетителей.
|
||||
physical_security: Dict[str, str] = field(default_factory=dict)
|
||||
|
||||
def update_declensions(self, inflect_func):
|
||||
"""Обновить склонения полного названия"""
|
||||
if self.full_name:
|
||||
@@ -126,24 +199,59 @@ class Company:
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "Company":
|
||||
d = dict(data)
|
||||
if "commission" in d and isinstance(d["commission"], list):
|
||||
_cvalid = {f.name for f in CommissionMember.__dataclass_fields__.values()}
|
||||
d["commission"] = [
|
||||
CommissionMember(**c) if isinstance(c, dict) else c
|
||||
CommissionMember(**{k: v for k, v in c.items() if k in _cvalid})
|
||||
if isinstance(c, dict) else c
|
||||
for c in d["commission"]
|
||||
]
|
||||
if "information_systems" in d and isinstance(d["information_systems"], list):
|
||||
_ivalid = {f.name for f in InformationSystem.__dataclass_fields__.values()}
|
||||
d["information_systems"] = [
|
||||
InformationSystem(**isys) if isinstance(isys, dict) else isys
|
||||
InformationSystem(**{k: v for k, v in isys.items() if k in _ivalid})
|
||||
if isinstance(isys, dict) else isys
|
||||
for isys in d["information_systems"]
|
||||
]
|
||||
if "paper_documents_list" in d and isinstance(d["paper_documents_list"], list):
|
||||
d["paper_documents_list"] = [
|
||||
PaperDocument(**p) if isinstance(p, dict) else p
|
||||
for p in d["paper_documents_list"]
|
||||
]
|
||||
if "employees_access" in d and not isinstance(d["employees_access"], list):
|
||||
d["employees_access"] = []
|
||||
# Отбрасываем плейсхолдеры из старых опросников («Формат: Фамилия Имя Отчество») и пустые строки
|
||||
if "employees_access" in d and isinstance(d["employees_access"], list):
|
||||
clean = []
|
||||
for e in d["employees_access"]:
|
||||
if not isinstance(e, dict):
|
||||
continue
|
||||
fio = str(e.get("fio", "")).strip()
|
||||
pos = str(e.get("position", "")).strip()
|
||||
if fio.lower().startswith("формат:") or fio.lower().startswith("пример:"):
|
||||
continue
|
||||
if not fio and not pos:
|
||||
continue
|
||||
clean.append(e)
|
||||
d["employees_access"] = clean
|
||||
if "okved_extra" in d and not isinstance(d.get("okved_extra"), list):
|
||||
d["okved_extra"] = []
|
||||
valid = {f.name for f in cls.__dataclass_fields__.values()}
|
||||
return cls(**{k: v for k, v in d.items() if k in valid})
|
||||
obj = cls(**{k: v for k, v in d.items() if k in valid})
|
||||
# Миграция: старые файлы с доходами по годам → суммарный доход за 5 лет
|
||||
if not (getattr(obj, 'income_5y', '') or '').strip():
|
||||
_sum = 0.0
|
||||
_has = False
|
||||
for i in range(1, 6):
|
||||
v = str(getattr(obj, f'income_year_{i}', '') or '').strip()
|
||||
if v:
|
||||
try:
|
||||
_sum += float(v.replace(' ', '').replace(',', '.').replace('\u00a0', ''))
|
||||
_has = True
|
||||
except Exception:
|
||||
pass
|
||||
if _has:
|
||||
obj.income_5y = f"{_sum:,.0f}".replace(',', ' ')
|
||||
# Если в поле ФИО несколько человек — оставить одного (старые опросники)
|
||||
for fio_field in ('chief_fio', 'responsible_fio', 'admin_fio'):
|
||||
val = getattr(obj, fio_field, '')
|
||||
if val:
|
||||
setattr(obj, fio_field, _clean_single_fio(val))
|
||||
return obj
|
||||
|
||||
def get_folder_name(self) -> str:
|
||||
if self.short_name:
|
||||
|
||||
@@ -0,0 +1,618 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — автоматический подбор угроз безопасности информации (236 приказ).
|
||||
|
||||
Логика программы: по характеристикам объекта КИИ (тип, сеть, нарушитель)
|
||||
подбирает актуальные угрозы из перечня БДУ ФСТЭК (threats_fstec.json).
|
||||
Если перечень не скачан — использует базовый threats.json.
|
||||
|
||||
Правила эвристические; при наличии методического документа (например,
|
||||
«Методика оценки угроз безопасности информации» ФСТЭК) — адаптируются под него.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
|
||||
|
||||
def _ubi_num(text):
|
||||
"""Код УБИ из строки → int: «УБИ.069 Название» → 69. None, если кода нет."""
|
||||
m = re.search(r'уби\.?\s*(\d+)', str(text), re.IGNORECASE)
|
||||
return int(m.group(1)) if m else None
|
||||
|
||||
|
||||
def sort_threats_by_ubi(items):
|
||||
"""Сортировка угроз по возрастанию кода УБИ (УБИ.001, УБИ.002, …).
|
||||
|
||||
Строки без кода УБИ уходят в конец (по алфавиту).
|
||||
"""
|
||||
def _key(t):
|
||||
n = _ubi_num(t)
|
||||
return (0, n) if n is not None else (1, str(t).lower())
|
||||
return sorted(items, key=_key)
|
||||
|
||||
|
||||
def load_threats(dict_dir=None):
|
||||
"""Список угроз: сначала скачанный с ФСТЭК (с кодами УБИ), иначе базовый."""
|
||||
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:
|
||||
data = json.load(f)
|
||||
if data:
|
||||
return data
|
||||
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 []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Ключевые слова (в названии угрозы) по признакам объекта
|
||||
# ---------------------------------------------------------------
|
||||
# Базовый набор — актуален для любого объекта КИИ
|
||||
BASE_KEYWORDS = [
|
||||
'вредоносн', 'несанкционированн', 'утечк', 'перехват', 'модификац',
|
||||
'уничтожен', 'отказ в обслуживании', 'нсд', 'биос', 'пароль',
|
||||
'криптографическ', 'шифрован', 'аутентификац',
|
||||
# базовые угрозы локальных сетей / клиент-серверных архитектур
|
||||
'доступ', 'данн', 'систем', 'конфигурац', 'целостност', 'информац',
|
||||
'программн', 'пользовател', 'авторизац', 'сеанс', 'журнал', 'реестр',
|
||||
'файл', 'память', 'буфер', 'ресурс', 'сервер', 'клиент', 'учётн',
|
||||
'учетн', 'идентификац', 'привилег', 'средств', 'сетев', 'локальн',
|
||||
]
|
||||
|
||||
# Технологии, которые по умолчанию НЕ используются на объекте.
|
||||
# Если в характеристиках объекта есть явные упоминания — группа возвращается.
|
||||
TECH_GROUPS = {
|
||||
'виртуализац': ['виртуал', 'гипервизор', 'контейнер'],
|
||||
'облако': ['облачн', 'облак'],
|
||||
'мобильн': ['мобильн', 'смартфон', 'планшет'],
|
||||
'грид': ['грид', 'суперкомпьютер'],
|
||||
'большие данные': ['больших данных', 'хранилищ'],
|
||||
'беспроводн': ['беспроводн', 'wi-fi', 'wifi'],
|
||||
'smart-карты': ['smart-карт', 'смарт-карт'],
|
||||
'машинное обучение': ['машинного обучения', 'искусственного интеллекта', 'нейросет'],
|
||||
}
|
||||
|
||||
# Объект имеет подключение к сети Интернет / сеть электросвязи
|
||||
INTERNET_KEYWORDS = [
|
||||
'удалённ', 'удаленн', 'сетев', 'фишинг', 'ddos', 'отказ в обслуживании',
|
||||
'веб', 'электронн', 'почт', 'беспроводн', 'wi-fi', 'wifi', 'vpn',
|
||||
'маршрутизац', 'протокол', 'интернет',
|
||||
]
|
||||
|
||||
# Объект изолирован (нет подключения к Интернету)
|
||||
ISOLATED_KEYWORDS = [
|
||||
'съёмн', 'съемн', 'физическ', 'локальн', 'инсайд', 'носител',
|
||||
'биос', 'желез', 'аппаратн',
|
||||
]
|
||||
|
||||
# АСУ / технологический процесс
|
||||
ASU_KEYWORDS = [
|
||||
'технологическ', 'асутп', 'промышленн', 'scada', 'датчик',
|
||||
'исполнительн', 'технологическ', 'производств', 'процесс',
|
||||
]
|
||||
|
||||
# ИТКС / сети связи
|
||||
NETWORK_KEYWORDS = [
|
||||
'сеть', 'канал', 'протокол', 'маршрутизац', 'связ', 'передач',
|
||||
'коммутац', 'трафик',
|
||||
]
|
||||
|
||||
# Внутренний нарушитель
|
||||
INSIDER_KEYWORDS = [
|
||||
'инсайд', 'сотрудник', 'привилегированн', 'администратор',
|
||||
'внутренн', 'персонал',
|
||||
]
|
||||
|
||||
# Внешний нарушитель
|
||||
EXTERNAL_KEYWORDS = [
|
||||
'внешн', 'удалённ', 'удаленн', 'дистанционн',
|
||||
]
|
||||
|
||||
|
||||
def _is_asu(isys) -> bool:
|
||||
t = (getattr(isys, 'object_type', '') or '').lower()
|
||||
return 'асу' in t or 'автоматизированн' in t or 'технологическ' in t
|
||||
|
||||
|
||||
def _is_network(isys) -> bool:
|
||||
t = (getattr(isys, 'object_type', '') or '').lower()
|
||||
return 'телекоммуникац' in t or 'сеть' in t
|
||||
|
||||
|
||||
def _has_internet(isys) -> bool:
|
||||
"""Есть ли подключение к сети Интернет / внешним сетям."""
|
||||
if getattr(isys, 'is_internet', False):
|
||||
return True
|
||||
desc = ((getattr(isys, 'description', '') or '') + ' ').lower()
|
||||
operator = (getattr(isys, 'net_operator', '') or '').lower()
|
||||
net_category = (getattr(isys, 'net_category', '') or '').lower()
|
||||
|
||||
# Явные признаки ОТСУТСТВИЯ внешней сети
|
||||
if 'отсутств' in operator: # оператор «Отсутствует»
|
||||
return False
|
||||
if 'отсутств' in desc and ('интернет' in desc or 'подключ' in desc):
|
||||
return False
|
||||
if 'не требуется' in desc and ('доменн' in desc or 'интернет' in desc):
|
||||
return False
|
||||
if 'изолирован' in desc:
|
||||
return False
|
||||
|
||||
# Сеть общего пользования — это и есть внешняя сеть (Интернет)
|
||||
if 'общего пользован' in net_category:
|
||||
return True
|
||||
|
||||
# Указан реальный оператор/провайдер — внешняя связь есть
|
||||
if operator and operator not in ('нет', 'отсутствует', 'не требуется', '-'):
|
||||
return True
|
||||
|
||||
# Консервативно: без явных признаков внешней сети считаем, что её нет
|
||||
return False
|
||||
|
||||
|
||||
def _is_insider(isys) -> bool:
|
||||
a = (getattr(isys, 'attacker_category', '') or '').lower()
|
||||
return 'внутрен' in a
|
||||
|
||||
|
||||
def _score(threat_lower, keywords):
|
||||
"""Сколько ключевых слов встретилось в названии угрозы."""
|
||||
return sum(1 for kw in keywords if kw in threat_lower)
|
||||
|
||||
|
||||
def suggest_threats_program(isys, threats=None, limit=None):
|
||||
"""Подбор угроз по характеристикам объекта КИИ (инвертированная логика).
|
||||
|
||||
По умолчанию актуальны ВСЕ угрозы из перечня (БДУ ФСТЭК).
|
||||
Исключаются только заведомо неактуальные для данного объекта:
|
||||
- нет сети Интернет → убираем угрозы удалённого доступа, веб, почты,
|
||||
беспроводных сетей, DDoS, облачных и т.п.;
|
||||
- объект типа АСУ → убираем угрозы BIOS, грид-систем, виртуализации
|
||||
(у технологического оборудования их, как правило, нет).
|
||||
Такой подход даёт полный список, а не «15 самых похожих».
|
||||
|
||||
Возвращает список строк (с кодами УБИ, если они есть в перечне).
|
||||
"""
|
||||
if threats is None:
|
||||
threats = load_threats()
|
||||
if not threats:
|
||||
return []
|
||||
|
||||
has_internet = _has_internet(isys)
|
||||
is_asu = _is_asu(isys)
|
||||
is_network = _is_network(isys)
|
||||
|
||||
# --- Технологии, которые явно НЕ используются на объекте ---
|
||||
# Если в характеристиках объекта (описание, архитектура, ПО, сфера) нет
|
||||
# упоминаний технологии — угрозы этой группы исключаем.
|
||||
tech_context = ' '.join([
|
||||
str(getattr(isys, 'description', '') or ''),
|
||||
str(getattr(isys, 'architecture', '') or ''),
|
||||
str(getattr(isys, 'hw_sw', '') or ''),
|
||||
str(getattr(isys, 'os_sw', '') or ''),
|
||||
str(getattr(isys, 'app_sw', '') or ''),
|
||||
str(getattr(isys, 'object_type', '') or ''),
|
||||
str(getattr(isys, 'sphere', '') or ''),
|
||||
str(getattr(isys, 'net_category', '') or ''),
|
||||
str(getattr(isys, 'typical_object', '') or ''),
|
||||
]).lower()
|
||||
|
||||
# --- Слова-исключения (если встретились в названии угрозы — она неактуальна) ---
|
||||
exclude = []
|
||||
|
||||
if not has_internet:
|
||||
# Объект изолирован: угрозы, реализуемые только через внешние сети
|
||||
exclude += [
|
||||
'удалённ', 'удаленн', 'дистанционн', 'веб', 'электронн', 'почт',
|
||||
'фишинг', 'беспроводн', 'wi-fi', 'wifi', 'интернет', 'ddos', 'vpn',
|
||||
'облачн', 'грид', 'внешн',
|
||||
]
|
||||
|
||||
if is_asu:
|
||||
# АСУ: нет BIOS/виртуализации/браузера в привычном понимании
|
||||
exclude += ['биос', 'грид', 'гипервизор', 'виртуальн', 'браузер']
|
||||
else:
|
||||
# Не АСУ — исключаем угрозы АСУ ТП / технологических процессов
|
||||
exclude += ['технологическ', 'датчик', 'исполнительн', 'асутп', 'scada',
|
||||
'промышленн', 'контроллер', 'числовым программным управлением',
|
||||
'программируемых логических']
|
||||
|
||||
# Технологии, не упомянутые в характеристиках объекта → исключаем
|
||||
for tech_name, kws in TECH_GROUPS.items():
|
||||
if any(kw in tech_context for kw in kws):
|
||||
continue # технология используется — угрозы оставляем
|
||||
# Технология не упомянута — исключаем её угрозы
|
||||
for kw in kws:
|
||||
exclude.append(kw)
|
||||
|
||||
if not is_network and not is_asu:
|
||||
# Обычная ИС: неактуальны чисто сетевые/телеком-угрозы без указания
|
||||
# — оставляем всё, кроме специфики каналов связи, если их нет
|
||||
pass
|
||||
|
||||
# --- Положительные ключевые слова: чем больше совпало — тем угроза актуальнее ---
|
||||
pos = list(BASE_KEYWORDS)
|
||||
if has_internet:
|
||||
pos += INTERNET_KEYWORDS
|
||||
else:
|
||||
pos += ISOLATED_KEYWORDS
|
||||
if is_asu:
|
||||
pos += ASU_KEYWORDS
|
||||
if is_network:
|
||||
pos += NETWORK_KEYWORDS
|
||||
if _is_insider(isys):
|
||||
pos += INSIDER_KEYWORDS
|
||||
else:
|
||||
pos += EXTERNAL_KEYWORDS
|
||||
# Убираем дубликаты
|
||||
pos = list(dict.fromkeys(pos))
|
||||
|
||||
scored = []
|
||||
for t in threats:
|
||||
tl = str(t).lower()
|
||||
if any(kw in tl for kw in exclude):
|
||||
continue
|
||||
sc = sum(1 for kw in pos if kw in tl)
|
||||
scored.append((sc, t))
|
||||
|
||||
# Сортируем по возрастанию кода УБИ (УБИ.001 → УБИ.211…)
|
||||
scored = [t for sc, t in scored if sc > 0]
|
||||
result = sort_threats_by_ubi(scored)
|
||||
|
||||
if limit and len(result) > limit:
|
||||
result = result[:limit]
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Автозаполнение вкладки «Безопасность» по данным объекта (логическое)
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
# Ключевые слова: угроза → тип инцидента (словарь incidents.json)
|
||||
INCIDENT_RULES = [
|
||||
('отказ в обслуживании', 'отказ в обслуживании'),
|
||||
('несанкционированн', 'несанкционированный доступ'),
|
||||
('утечк', 'утечка данных (нарушение конфиденциальности)'),
|
||||
('модификац', 'модификация (подмена) данных'),
|
||||
('подмен', 'модификация (подмена) данных'),
|
||||
('функционирован', 'нарушение функционирования технических средств'),
|
||||
('техническ', 'нарушение функционирования технических средств'),
|
||||
('использован', 'несанкционированное использование вычислительных ресурсов объекта'),
|
||||
]
|
||||
|
||||
# Базовый набор орг. мер (всегда актуальны для значимого объекта)
|
||||
BASE_ORG_MEASURES = [
|
||||
'Установление контролируемой зоны',
|
||||
'Контроль физического доступа к объекту',
|
||||
'Разработка документов (регламентов, инструкций, руководств) по обеспечению безопасности объекта',
|
||||
'Назначение ответственных за обеспечение безопасности объекта',
|
||||
'Обучение и инструктаж персонала',
|
||||
'Регламентация действий при компьютерных инцидентах',
|
||||
'Утверждение перечня лиц, имеющих доступ к объекту',
|
||||
]
|
||||
|
||||
# Базовый набор тех. мер — только универсальные, не требующие конкретных СЗИ.
|
||||
# Меры, зависящие от средств защиты (антивирус, СЗИ от НСД, регистрация событий
|
||||
# и т.п.), добавляются отдельно по факту наличия СЗИ (DEFENSE_TOOL_MEASURES).
|
||||
BASE_TECH_MEASURES = [
|
||||
'Идентификация и аутентификация пользователей',
|
||||
'Управление доступом',
|
||||
'Ограничение программной среды',
|
||||
'Резервное копирование и восстановление',
|
||||
'Защита технических средств и линий связи',
|
||||
]
|
||||
|
||||
# Средства защиты → доп. тех. меры (по факту наличия СЗИ у объекта)
|
||||
DEFENSE_TOOL_MEASURES = [
|
||||
('межсетев', 'Межсетевое экранирование'),
|
||||
('firewall', 'Межсетевое экранирование'),
|
||||
('сетев', 'Межсетевое экранирование'),
|
||||
('обнаруж', 'Обнаружение вторжений (СОВ)'),
|
||||
('ids', 'Обнаружение вторжений (СОВ)'),
|
||||
('ips', 'Обнаружение вторжений (СОВ)'),
|
||||
('криптограф', 'Криптографическая защита информации (СКЗИ)'),
|
||||
('vipnet', 'Криптографическая защита информации (СКЗИ)'),
|
||||
('континент', 'Криптографическая защита информации (СКЗИ)'),
|
||||
('виртуализ', 'Защита среды виртуализации'),
|
||||
('vmware', 'Защита среды виртуализации'),
|
||||
('hyper-v', 'Защита среды виртуализации'),
|
||||
('антивирус', 'Антивирусная защита'),
|
||||
('kaspersky', 'Антивирусная защита'),
|
||||
('eset', 'Антивирусная защита'),
|
||||
('dr.web', 'Антивирусная защита'),
|
||||
('аваст', 'Антивирусная защита'),
|
||||
('avast', 'Антивирусная защита'),
|
||||
('сзи', 'Защита информации от несанкционированного доступа (СЗИ от НСД)'),
|
||||
('от нсд', 'Защита информации от несанкционированного доступа (СЗИ от НСД)'),
|
||||
('dallas', 'Защита информации от несанкционированного доступа (СЗИ от НСД)'),
|
||||
('secret net', 'Защита информации от несанкционированного доступа (СЗИ от НСД)'),
|
||||
('аккорд', 'Защита информации от несанкционированного доступа (СЗИ от НСД)'),
|
||||
('siem', 'Регистрация и мониторинг событий безопасности'),
|
||||
('dlp', 'Регистрация и мониторинг событий безопасности'),
|
||||
('мониторинг', 'Регистрация и мониторинг событий безопасности'),
|
||||
('регистрац', 'Регистрация и мониторинг событий безопасности'),
|
||||
('доверенная загрузка', 'Контроль (доверенная) загрузки'),
|
||||
('резервн', 'Резервное копирование и восстановление'),
|
||||
]
|
||||
|
||||
|
||||
def auto_fill_security(isys, threats=None):
|
||||
"""Логическое автозаполнение вкладки «Безопасность» по данным объекта КИИ.
|
||||
|
||||
Заполняет (только если поле пустое):
|
||||
attacker_category — по наличию интернета (внешний/внутренний нарушитель);
|
||||
attacker_level — Н1/Н2 (внутренний — базовые, внешний — базовые повышенные);
|
||||
attacker_types — виды нарушителей по категории/интернету;
|
||||
threats — подбор программой (скоринг по характеристикам);
|
||||
incidents — типы инцидентов, выводимые из подобранных угроз;
|
||||
org_measures / tech_measures — базовый набор + по СЗИ объекта.
|
||||
|
||||
Возвращает dict {поле: новое_значение} только для пустых полей.
|
||||
"""
|
||||
from .models import InformationSystem
|
||||
res = {}
|
||||
|
||||
has_internet = _has_internet(isys)
|
||||
|
||||
# 1. Категория нарушителя
|
||||
if not (getattr(isys, 'attacker_category', '') or '').strip():
|
||||
res['attacker_category'] = 'Внешний нарушитель' if has_internet else 'Внутренний нарушитель'
|
||||
|
||||
# 2. Уровень возможностей
|
||||
if not (getattr(isys, 'attacker_level', '') or '').strip():
|
||||
res['attacker_level'] = 'Н2 — Базовые повышенные возможности' if has_internet else 'Н1 — Базовые возможности'
|
||||
|
||||
# 3. Виды нарушителей
|
||||
if not (getattr(isys, 'attacker_types', '') or '').strip():
|
||||
if has_internet:
|
||||
types = [
|
||||
'Отдельные физические лица (хакеры)',
|
||||
'Преступные группы (криминальные структуры)',
|
||||
'Конкурирующие организации',
|
||||
'Авторизованные пользователи систем и сетей',
|
||||
'Системные администраторы и администраторы безопасности',
|
||||
'Бывшие (уволенные) работники (пользователи)',
|
||||
]
|
||||
else:
|
||||
types = [
|
||||
'Авторизованные пользователи систем и сетей',
|
||||
'Системные администраторы и администраторы безопасности',
|
||||
'Бывшие (уволенные) работники (пользователи)',
|
||||
'Лица, обеспечивающие функционирование систем и сетей или обеспечивающих систем оператора (администрация, охрана, уборщики и др.)',
|
||||
]
|
||||
res['attacker_types'] = '\n'.join(types)
|
||||
|
||||
# 4. Угрозы — подбор программой
|
||||
if not (getattr(isys, 'threats', '') or '').strip():
|
||||
prog = suggest_threats_program(isys, threats=threats)
|
||||
if prog:
|
||||
res['threats'] = '\n'.join(prog)
|
||||
|
||||
# 5. Инциденты — из подобранных угроз
|
||||
if not (getattr(isys, 'incidents', '') or '').strip():
|
||||
threats_text = res.get('threats') or (getattr(isys, 'threats', '') or '')
|
||||
if threats_text:
|
||||
incidents = []
|
||||
low = threats_text.lower()
|
||||
for kw, incident in INCIDENT_RULES:
|
||||
if kw in low and incident not in incidents:
|
||||
incidents.append(incident)
|
||||
if incidents:
|
||||
res['incidents'] = '\n'.join(incidents)
|
||||
|
||||
# 6. Орг. меры — базовый набор
|
||||
if not (getattr(isys, 'org_measures', '') or '').strip():
|
||||
res['org_measures'] = '\n'.join(BASE_ORG_MEASURES)
|
||||
|
||||
# 7. Тех. меры — базовые + по СЗИ объекта.
|
||||
# Только если объекту присвоена категория значимости (1/2/3):
|
||||
# для незначимого объекта технические меры не требуются.
|
||||
if not (getattr(isys, 'tech_measures', '') or '').strip():
|
||||
cat = (getattr(isys, 'category', '') or '').strip()
|
||||
has_category = cat in ('1', '2', '3')
|
||||
if has_category:
|
||||
tech = list(BASE_TECH_MEASURES)
|
||||
defense = ' '.join(getattr(isys, 'defense_tools_list', []) or [])
|
||||
dlow = defense.lower()
|
||||
for kw, measure in DEFENSE_TOOL_MEASURES:
|
||||
if kw in dlow and measure not in tech:
|
||||
tech.append(measure)
|
||||
res['tech_measures'] = '\n'.join(tech)
|
||||
# else: категория не присвоена — технические меры не заполняем
|
||||
|
||||
return res
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Подбор типового отраслевого объекта КИИ по характеристикам объекта
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
# Стоп-слова: не несут смысла при сопоставлении названия объекта и типового объекта
|
||||
STOPWORDS = {
|
||||
'информационные', 'информационно', 'телекоммуникационные', 'автоматизированные',
|
||||
'системы', 'система', 'сети', 'сеть', 'обеспечивающие', 'обеспечивающих',
|
||||
'предназначенные', 'предназначенных', 'управление', 'контроль', 'мониторинг',
|
||||
'объекта', 'объектов', 'объект', 'объекты', 'кии', 'рф', 'российской',
|
||||
'федерации', 'исполнительной', 'власти', 'органов', 'организаций', 'организации',
|
||||
'подведомственных', 'функций', 'функции', 'осуществление', 'полномочий',
|
||||
'обеспечение', 'процессов', 'процесса', 'процессы', 'данных', 'информации',
|
||||
'сбор', 'хранение', 'обработка', 'представление', 'использование', 'передача',
|
||||
'автоматизации', 'средств', 'программных', 'программно', 'аппаратных',
|
||||
'технических', 'деятельность', 'деятельности', 'осуществляющих',
|
||||
'размещенные', 'размещенных', 'расположенных', 'используемых', 'используемые',
|
||||
'проведении', 'проведения', 'выполнении', 'выполнения', 'реализация', 'реализации',
|
||||
'электронных', 'электронного', 'информационных', 'информационного',
|
||||
# Общие словоформы, встречающиеся почти у всех типовых объектов:
|
||||
'контроль', 'контроля', 'контролю', 'контролем', 'контроле',
|
||||
'управление', 'управления', 'управлению', 'управлением', 'управлении',
|
||||
'безопасность', 'безопасности', 'безопасностью', 'безопасном',
|
||||
'обеспечение', 'обеспечения', 'обеспечению', 'обеспечением',
|
||||
'предназначен', 'предназначена', 'предназначено', 'предназначены',
|
||||
'предоставление', 'предоставления', 'предоставлению',
|
||||
'взаимодействие', 'взаимодействия', 'взаимодействию',
|
||||
'функционирование', 'функционирования', 'функционированию',
|
||||
'деятельность', 'деятельности', 'деятельностью',
|
||||
'автоматизирован', 'автоматизированной', 'автоматизированных',
|
||||
'телекоммуникацион', 'телекоммуникационной', 'телекоммуникационных',
|
||||
'информационн', 'информационной', 'информационных', 'информационное',
|
||||
'осуществление', 'осуществления', 'осуществлению',
|
||||
'для', 'систем', 'системы', 'система', 'ресурсов', 'ресурсы',
|
||||
'целью', 'цели', 'помощи', 'помощь', 'также', 'или', 'иных', 'иное',
|
||||
'при', 'на', 'по', 'в', 'с', 'и', 'не', 'то', 'числе',
|
||||
}
|
||||
|
||||
|
||||
def _tokenize(text):
|
||||
"""Значимые слова текста (без стоп-слов)."""
|
||||
import re as _re
|
||||
words = _re.findall(r'[а-яёa-z0-9]+', (text or '').lower())
|
||||
return [w for w in words if len(w) > 2 and w not in STOPWORDS]
|
||||
|
||||
|
||||
def _load_all_typical(dict_dir=None):
|
||||
"""Все типовые отраслевые объекты из typical_industry_objects.json (по сферам)."""
|
||||
import os as _os
|
||||
if dict_dir is None:
|
||||
dict_dir = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)), 'dictionaries')
|
||||
path = _os.path.join(dict_dir, 'typical_industry_objects.json')
|
||||
try:
|
||||
with open(path, encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
except Exception:
|
||||
return []
|
||||
out = []
|
||||
for sphere, items in data.items():
|
||||
if sphere == 'Иная сфера деятельности':
|
||||
continue
|
||||
for it in items:
|
||||
out.append({'sphere': sphere, 'num': it.get('num', ''),
|
||||
'name': it.get('name', '')})
|
||||
return out
|
||||
|
||||
|
||||
_IDF_CACHE = None
|
||||
|
||||
|
||||
def _idf_weights(cands):
|
||||
"""IDF-веса слов: чем реже слово в перечне типовых объектов, тем больше вес.
|
||||
|
||||
Общие слова («информационные», «управление», «контроль») встречаются почти
|
||||
у всех 392 объектов и почти ничего не значат; редкие («доступом», «СКУД»,
|
||||
«сигнализации») — сильно различают объекты.
|
||||
"""
|
||||
global _IDF_CACHE
|
||||
if _IDF_CACHE is not None:
|
||||
return _IDF_CACHE
|
||||
import math
|
||||
df = {}
|
||||
for c in cands:
|
||||
for w in set(_tokenize(c['name'])):
|
||||
df[w] = df.get(w, 0) + 1
|
||||
n = max(len(cands), 1)
|
||||
weights = {w: math.log((n + 1) / (df.get(w, 0) + 1)) + 1.0 for w in df}
|
||||
_IDF_CACHE = weights
|
||||
return weights
|
||||
|
||||
|
||||
def suggest_typical_object_program(isys, limit=10, sphere=None):
|
||||
"""Подбор типового отраслевого объекта КИИ по характеристикам объекта.
|
||||
|
||||
Логика: значимые слова из названия/назначения/описания объекта КИИ
|
||||
сопоставляются со значимыми словами названий типовых объектов
|
||||
(перечень Распоряжения ПРФ № 360-р, 392 объекта). Скоринг по IDF:
|
||||
редкие слова (например, «доступом», «сигнализации») весят больше общих.
|
||||
Пример: объект «СКУД» / «контроль и управление доступом» →
|
||||
«ИС, АСУ, ИТКС, обеспечивающие контроль и управление доступом на территорию».
|
||||
|
||||
Возвращает список dict: {sphere, num, name, score} — по убыванию релевантности.
|
||||
"""
|
||||
text = ' '.join([
|
||||
getattr(isys, 'name', '') or '',
|
||||
getattr(isys, 'purpose', '') or '',
|
||||
getattr(isys, 'description', '') or '',
|
||||
])
|
||||
obj_words = set(_tokenize(text))
|
||||
if not obj_words:
|
||||
return []
|
||||
|
||||
# Фильтр по сфере (важно: не подбирать ОПК-объекту сферу из здравоохранения):
|
||||
# 1) сфера, переданная явно (выбрана в комбобоксе);
|
||||
# 2) сфера, уже заполненная у объекта (isys.sphere);
|
||||
# 3) автодетект по названию/назначению/описанию;
|
||||
# 4) иначе — все сферы (подбор без фильтра).
|
||||
used_sphere = sphere
|
||||
if not used_sphere:
|
||||
used_sphere = (getattr(isys, 'sphere', '') or '').strip() or None
|
||||
if not used_sphere:
|
||||
used_sphere = detect_sphere(isys)
|
||||
|
||||
cands = _load_all_typical()
|
||||
if used_sphere:
|
||||
cands = [c for c in cands if c['sphere'] == used_sphere]
|
||||
if not cands:
|
||||
# Сфера определена, но объектов нет — не расширяем на другие сферы,
|
||||
# возвращаем пусто (оператор выберет сферу вручную)
|
||||
return []
|
||||
|
||||
weights = _idf_weights(cands)
|
||||
|
||||
scored = []
|
||||
for c in cands:
|
||||
tw = set(_tokenize(c['name']))
|
||||
common = tw & obj_words
|
||||
if common:
|
||||
score = sum(weights.get(w, 1.0) for w in common)
|
||||
scored.append({'sphere': c['sphere'], 'num': c['num'],
|
||||
'name': c['name'], 'score': round(score, 2),
|
||||
'matched': sorted(common)})
|
||||
|
||||
scored.sort(key=lambda x: (-x['score'], x['name']))
|
||||
return scored[:limit]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Автоопределение сферы деятельности объекта по ключевым словам
|
||||
# ---------------------------------------------------------------
|
||||
SPHERE_KEYWORDS = [
|
||||
('Здравоохранение', ['медицин', 'здрав', 'поликлиник', 'больниц', 'лечеб', 'клиник', 'фармацевт', 'аптек', 'пациент', 'врач', 'диагностик', 'лабораторн', 'медпомощ']),
|
||||
('Наука', ['научн', 'исследован', 'разработк', 'лаборатор', 'академи', 'институт', 'ниокр', 'наука']),
|
||||
('Транспорт', ['транспорт', 'перевоз', 'автомобил', 'железнодорожн', 'авиа', 'морск', 'речн', 'метрополитен', 'вокзал', 'логистик', 'движени']),
|
||||
('Связь', ['связ', 'телекоммуникац', 'оператор связи', 'интернет-провайдер', 'передач данных', 'сети связи', 'телефон']),
|
||||
('Энергетика', ['энерг', 'электро', 'теплоснабж', 'электроснабж', 'генерац', 'подстанц', 'котельн', 'энергосистем']),
|
||||
('Государственная регистрация прав', ['регистрац', 'кадастр', 'недвижим', 'реестр']),
|
||||
('Банковская сфера и финансовый рынок', ['банк', 'финанс', 'кредит', 'платеж', 'расчет', 'страхов', 'клирин', 'депозитар', 'пенсионн', 'фондов']),
|
||||
('Топливно-энергетический комплекс', ['нефт', 'газ', 'топлив', 'трубопровод', 'месторожден', 'переработк', 'уголь', 'нефте']),
|
||||
('Атомная энергетика', ['атомн', 'ядерн', 'радиацион', 'аэс', 'реактор', 'атом']),
|
||||
('Оборонная промышленность', ['оборон', 'военн', 'вооружен', 'оружи', 'оборонно', 'опк', 'госзаказ', 'госконтракт', 'боеприпас', 'ввт', 'минобороны']),
|
||||
('Ракетно-космическая промышленность', ['ракет', 'космическ', 'спутник', 'космодром', 'орбит', 'космос', 'пуск']),
|
||||
('Горнодобывающая промышленность', ['горн', 'добыч', 'шахт', 'рудник', 'карьер', 'месторожден']),
|
||||
('Металлургическая промышленность', ['металлург', 'прокат', 'стал', 'чугун', 'цветн', 'сплав']),
|
||||
('Химическая промышленность', ['химическ', 'хим', 'нефтехим', 'взрывчат', 'кислот', 'реактив', 'удобрен']),
|
||||
]
|
||||
|
||||
|
||||
def detect_sphere(isys):
|
||||
"""Определяет сферу деятельности объекта КИИ по названию/назначению/описанию.
|
||||
|
||||
Возвращает название сферы или None, если определить не удалось.
|
||||
"""
|
||||
text = ' '.join([
|
||||
getattr(isys, 'name', '') or '',
|
||||
getattr(isys, 'purpose', '') or '',
|
||||
getattr(isys, 'description', '') or '',
|
||||
]).lower()
|
||||
best = None
|
||||
best_score = 0
|
||||
for sphere, keywords in SPHERE_KEYWORDS:
|
||||
score = sum(1 for kw in keywords if kw in text)
|
||||
if score > best_score:
|
||||
best = sphere
|
||||
best_score = score
|
||||
return best if best_score > 0 else None
|
||||
+3474
-396
File diff suppressed because it is too large
Load Diff
+752
-95
@@ -19,7 +19,7 @@ def _norm_name(s):
|
||||
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
||||
# ============================================================
|
||||
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
||||
# Группы: A — общие, B — 152-ФЗ
|
||||
# Группы: A — общие, B — 187-ФЗ
|
||||
|
||||
VARIABLE_DEFS = [
|
||||
# === Полное название организации (6 падежей) ===
|
||||
@@ -57,10 +57,22 @@ VARIABLE_DEFS = [
|
||||
('{{company_ogrn_date}}', 'A', 'Дата ОГРН', 'company:ogrn_date'),
|
||||
('{{company_kpp}}', 'A', 'КПП', 'company:kpp'),
|
||||
('{{security_department}}', 'A', 'Наименование структурного подразделения по безопасности', 'company:security_department'),
|
||||
('{{item.pd_actions}}', 'B', 'Действия с ПДн (в цикле ИС)', 'item:pd_actions'),
|
||||
('{{company_okved}}', 'A', 'Основной ОКВЭД', 'company:okved'),
|
||||
('{{company_okved_extra}}', 'A', 'Дополнительные ОКВЭД', 'company:okved_extra'),
|
||||
('{{company_profit}}', 'A', 'Сумма прибыли за предыдущий год (руб.)', 'company:profit'),
|
||||
('{{company_profit_tax}}', 'A', 'Сумма налога с прибыли (руб.)', 'company:profit_tax'),
|
||||
('{{company_income_5y}}', 'A', 'Доход за 5 лет суммарно (руб.) — показатель 8', 'company:income_5y'),
|
||||
('{{company_income_year_1}}', 'A', 'Доход за 5 лет назад (руб.) — показатель 8 (обратная совместимость)', 'company:income_year_1'),
|
||||
('{{company_income_year_2}}', 'A', 'Доход за 4 года назад (руб.) — показатель 8', 'company:income_year_2'),
|
||||
('{{company_income_year_3}}', 'A', 'Доход за 3 года назад (руб.) — показатель 8', 'company:income_year_3'),
|
||||
('{{company_income_year_4}}', 'A', 'Доход за 2 года назад (руб.) — показатель 8', 'company:income_year_4'),
|
||||
('{{company_income_year_5}}', 'A', 'Доход за прошлый год (руб.) — показатель 8', 'company:income_year_5'),
|
||||
('{{company_income_loss}}', 'A', 'Суммарный экономический ущерб от нарушения критических процессов (руб.) — показатель 8', 'company:income_loss'),
|
||||
|
||||
# === Комиссия ===
|
||||
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
|
||||
('{{fstek_district}}', 'A', 'Наименование управления ФСТЭК России (по адресу организации)', 'fstek_district'),
|
||||
('{{fstek_recipient_address}}', 'A', 'Адрес управления ФСТЭК России для направления сведений', 'fstek_recipient_address'),
|
||||
|
||||
# === Специальные переменные ===
|
||||
('{{date_year}}', 'A', 'Текущий год', 'date:year'),
|
||||
@@ -68,23 +80,16 @@ VARIABLE_DEFS = [
|
||||
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
||||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||||
|
||||
# === 152-ФЗ ===
|
||||
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
||||
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
||||
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
||||
('{{name}}', 'B', 'Название ИС (алиас)', 'is:name'),
|
||||
('{{description}}', 'B', 'Описание ИС (алиас)', 'is:description'),
|
||||
('{{category}}', 'B', 'Категория ИС (алиас)', 'is:category'),
|
||||
# === 187-ФЗ ===
|
||||
('{{is_security_requirements}}', 'B', 'Требования к защите объекта КИИ', 'security_reqs'),
|
||||
('{{is_list}}', 'B', 'Список всех объектов КИИ (нумерованный)', 'is_list'),
|
||||
('{{is_name}}', 'B', 'Наименование первого объекта КИИ', 'is:name'),
|
||||
('{{name}}', 'B', 'Название объекта КИИ (алиас)', 'is:name'),
|
||||
('{{description}}', 'B', 'Описание объекта КИИ (алиас)', 'is:description'),
|
||||
('{{category}}', 'B', 'Категория значимости объекта КИИ (алиас)', 'is:category'),
|
||||
('{{number}}', 'B', 'Номер (алиас)', 'doc_number'),
|
||||
('{{pd_count}}', 'B', 'Количество записей ПДн (алиас)', 'is:pd_count'),
|
||||
('{{pd_list}}', 'B', 'Список ПДн (алиас)', 'is:pd_list'),
|
||||
('{{pd_subjects}}', 'B', 'Субъекты ПДн (алиас)', 'is:pd_subjects'),
|
||||
('{{threat_type}}', 'B', 'Тип угроз (алиас)', 'is:threat_type'),
|
||||
('{{users}}', 'B', 'Пользователи ИС (алиас)', 'is:users'),
|
||||
('{{defence_level}}', 'B', 'Уровень защищённости (алиас)', 'is:defence_level'),
|
||||
('{{users}}', 'B', 'Пользователи ОКИИ (алиас)', 'is:users'),
|
||||
('{{full_name_genitive}}', 'A', 'Полное наименование (род.п., алиас)', 'decl:full_name:gent'),
|
||||
('{{document}}', 'A', 'Название бумажного документа (алиас)', 'company:paper_documents'),
|
||||
('{{storage}}', 'A', 'Место хранения (алиас)', 'company:paper_storage'),
|
||||
('{{responsible_fio_accs}}', 'A', 'ФИО ответственного (вин.п.)', 'fio_acl:responsible'),
|
||||
('{{commission1}}', 'A', 'Должность члена комиссии 1', 'commission_member:0:position'),
|
||||
('{{commission1_fio}}', 'A', 'ФИО члена комиссии 1', 'commission_member:0:fio'),
|
||||
@@ -138,7 +143,7 @@ def build_replacements(
|
||||
is_list: List = None,
|
||||
doc_number: str = "",
|
||||
doc_date: str = "",
|
||||
direction: str = "152fz",
|
||||
direction: str = "187fz",
|
||||
) -> Dict[str, str]:
|
||||
"""Строит полный словарь замен {{переменная}} → значение."""
|
||||
if is_list is None:
|
||||
@@ -159,7 +164,7 @@ def build_replacements(
|
||||
|
||||
replacements = {}
|
||||
for var_name, group, desc, source in VARIABLE_DEFS:
|
||||
if direction == '152fz' and group not in ('A', 'B'):
|
||||
if direction == '187fz' and group not in ('A', 'B'):
|
||||
continue
|
||||
value = _resolve(source, ctx)
|
||||
replacements[var_name] = str(value) if value else '—'
|
||||
@@ -221,13 +226,22 @@ def _add_is_indexed_vars(replacements: Dict[str, str], isys, idx: int):
|
||||
'is_address': getattr(isys, 'address', '') or '—',
|
||||
'is_description': isys.description or '—',
|
||||
'is_has_internet': 'да' if isys.is_internet else 'нет',
|
||||
'is_defence_level': str(isys.defence_level or '—'),
|
||||
'is_category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
||||
'is_pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
||||
'is_category': isys.category or '—',
|
||||
'is_users': ', '.join(isys.users_list or []) or '—',
|
||||
'is_pd_count': str(isys.pd_count or '—'),
|
||||
'is_purpose': isys.purpose or '—',
|
||||
'is_structure': getattr(isys, 'structure', '') or '—',
|
||||
'is_software': getattr(isys, 'software', '') or '—',
|
||||
'is_sphere': getattr(isys, 'sphere', '') or '—',
|
||||
'is_object_type': getattr(isys, 'object_type', '') or '—',
|
||||
'is_architecture': getattr(isys, 'architecture', '') or '—',
|
||||
'is_typical_object': getattr(isys, 'typical_object', '') or '—',
|
||||
'is_net_category': getattr(isys, 'net_category', '') or '—',
|
||||
'is_net_operator': getattr(isys, 'net_operator', '') or '—',
|
||||
'is_net_purpose': getattr(isys, 'net_purpose', '') or '—',
|
||||
'is_net_method': getattr(isys, 'net_method', '') or '—',
|
||||
'is_hw_sw': getattr(isys, 'hw_sw', '') or '—',
|
||||
'is_os_sw': getattr(isys, 'os_sw', '') or '—',
|
||||
'is_app_sw': getattr(isys, 'app_sw', '') or '—',
|
||||
}
|
||||
for f, v in fields.items():
|
||||
replacements[f'{{{{{f}_{idx}}}}}'] = str(v)
|
||||
@@ -295,68 +309,63 @@ def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
|
||||
'address': getattr(isys, 'address', '') or '—',
|
||||
'is_has_lan': 'Да' if isys.is_local_network else 'Нет',
|
||||
'has_internet': 'да' if isys.is_internet else 'нет',
|
||||
'defence_level': str(isys.defence_level or '—'),
|
||||
'threat_type': str(isys.threat_type or '3'),
|
||||
'category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
||||
'pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
||||
'pd_count': str(isys.pd_count or '—'),
|
||||
'category': isys.category or '—',
|
||||
'users': ', '.join(isys.users_list or []) or '—',
|
||||
'users_loop': users_loop,
|
||||
'defense_tools_list': ', '.join(isys.defense_tools_list or []) or '—',
|
||||
'processing_modes': isys.processing_modes or '—',
|
||||
'pd_subjects': '; '.join(isys.pd_subjects_list or []) or '—',
|
||||
'is_purpose': isys.purpose or '—',
|
||||
'structure': getattr(isys, 'structure', '') or '—',
|
||||
'software': getattr(isys, 'software', '') or '—',
|
||||
'pd_actions': '; '.join(isys.pd_actions_list or []) or '—',
|
||||
# 236 приказ
|
||||
'purpose': isys.purpose or '—',
|
||||
'sphere': getattr(isys, 'sphere', '') or '—',
|
||||
'object_type': getattr(isys, 'object_type', '') or '—',
|
||||
'architecture': getattr(isys, 'architecture', '') or '—',
|
||||
'typical_object': getattr(isys, 'typical_object', '') or '—',
|
||||
'subject_name': getattr(isys, 'subject_name', '') or '—',
|
||||
'subject_address': getattr(isys, 'subject_address', '') or '—',
|
||||
'subject_element': getattr(isys, 'subject_element', '') or '—',
|
||||
'subject_inn': getattr(isys, 'subject_inn', '') or '—',
|
||||
'subject_kpp': getattr(isys, 'subject_kpp', '') or '—',
|
||||
'net_category': getattr(isys, 'net_category', '') or '—',
|
||||
'net_operator': getattr(isys, 'net_operator', '') or '—',
|
||||
'net_purpose': getattr(isys, 'net_purpose', '') or '—',
|
||||
'net_method': getattr(isys, 'net_method', '') or '—',
|
||||
'hw_sw': getattr(isys, 'hw_sw', '') or '—',
|
||||
'os_sw': getattr(isys, 'os_sw', '') or '—',
|
||||
'app_sw': getattr(isys, 'app_sw', '') or '—',
|
||||
'balance_value': getattr(isys, 'balance_value', '') or '—',
|
||||
'attacker_category': getattr(isys, 'attacker_category', '') or '—',
|
||||
'attacker_level': getattr(isys, 'attacker_level', '') or '—',
|
||||
'attacker_types': getattr(isys, 'attacker_types', '') or '—',
|
||||
'threats': getattr(isys, 'threats', '') or '—',
|
||||
# Алиасы под шаблон «Сведения о результатах категорирования» (ФСТЭК)
|
||||
'objective': getattr(isys, 'purpose', '') or '—',
|
||||
'standard_name': getattr(isys, 'typical_object', '') or '—',
|
||||
'network_category': getattr(isys, 'net_category', '') or '—',
|
||||
'provider': getattr(isys, 'net_operator', '') or '—',
|
||||
'interaction_purpose': getattr(isys, 'net_purpose', '') or '—',
|
||||
'interaction_method': getattr(isys, 'net_method', '') or '—',
|
||||
'hardware': getattr(isys, 'hw_sw', '') or '—',
|
||||
'system_software': getattr(isys, 'os_sw', '') or '—',
|
||||
'defense_tools': ', '.join(isys.defense_tools_list or []) or '—',
|
||||
'component': getattr(isys, 'subject_element', '') or '—',
|
||||
'exploiting_org': getattr(isys, 'subject_name', '') or '—',
|
||||
'exploiting_inn': getattr(isys, 'subject_inn', '') or '—',
|
||||
'domain': getattr(isys, 'domain_name', '') or '—',
|
||||
'threat_actor': getattr(isys, 'attacker_category', '') or '—',
|
||||
'threat_justification': getattr(isys, 'threats', '') or '—',
|
||||
'incidents': getattr(isys, 'incidents', '') or '—',
|
||||
'criteria_values': getattr(isys, 'criteria_values', '') or '—',
|
||||
'criteria_justification': getattr(isys, 'criteria_justification', '') or '—',
|
||||
# Орг. меры: если не заполнены вручную — автогенерация по опроснику
|
||||
# (с учётом ответов раздела «Физическая безопасность»).
|
||||
'org_measures': (getattr(isys, 'org_measures', '') or '')
|
||||
or _build_org_measures_text(company, isys),
|
||||
'tech_measures': getattr(isys, 'tech_measures', '') or '—',
|
||||
})
|
||||
loops['information_systems'] = items
|
||||
|
||||
# Бумажные документы
|
||||
def _storage_with_prep(storage: str) -> str:
|
||||
"""«сейф» → «в сейфе» (предлог + предложный падеж).
|
||||
Если уже начинается с предлога — оставить как есть."""
|
||||
if not storage:
|
||||
return ''
|
||||
s = storage.strip()
|
||||
low = s.lower()
|
||||
if low.startswith(('в ', 'на ', 'под ', 'за ', 'у ', 'при ', 'из ', 'со ', 'во ', 'над ', 'перед ')):
|
||||
return s
|
||||
words = s.split()
|
||||
out = []
|
||||
# Исключения предложного падежа: «в шкафу», «в углу» (не «в шкафе»)
|
||||
loct_exceptions = {'шкаф': 'шкафу', 'угол': 'углу', 'край': 'краю', 'рот': 'рту', 'мост': 'мосту'}
|
||||
for w in words:
|
||||
core = w.strip('.,;')
|
||||
if not core or core.isdigit() or core.startswith('№'):
|
||||
out.append(w)
|
||||
continue
|
||||
if core.lower() in loct_exceptions:
|
||||
out.append(loct_exceptions[core.lower()])
|
||||
continue
|
||||
try:
|
||||
p = dc.morph.parse(core)[0]
|
||||
inf = p.inflect({'loct'})
|
||||
if inf:
|
||||
out.append(inf.word)
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
out.append(w)
|
||||
return 'в ' + ' '.join(out)
|
||||
|
||||
paper_list = getattr(company, 'paper_documents_list', None) or []
|
||||
if paper_list:
|
||||
items = []
|
||||
for p in paper_list:
|
||||
storage = p.storage or ''
|
||||
items.append({
|
||||
'document': p.name or '',
|
||||
'storage': _storage_with_prep(storage), # «в сейфе»
|
||||
'storage_raw': storage, # «сейф» (как введено)
|
||||
})
|
||||
loops['paper_documents'] = items
|
||||
|
||||
# Сотрудники с доступом
|
||||
employees = getattr(company, 'employees_access', None) or []
|
||||
if employees:
|
||||
@@ -368,13 +377,652 @@ def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
|
||||
})
|
||||
loops['employees_access'] = items
|
||||
|
||||
# --- Шаблон «Сведения о результатах категорирования» (ФСТЭК) ---
|
||||
# Цикл объектов КИИ — тот же список, что information_systems.
|
||||
if is_list:
|
||||
loops['kii_objects'] = loops.get('information_systems', [])
|
||||
|
||||
# Цикл показателей критериев значимости (ПП-127): автоматически
|
||||
# формируем строки таблицы с обоснованием «применим/не применим» по сфере
|
||||
# и расчётом для показателей 8 и 9 (как в примере заказчика).
|
||||
loops['categorization_indicators'] = _build_categorization_indicators(company, is_list)
|
||||
|
||||
return loops
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ПОКАЗАТЕЛИ КРИТЕРИЕВ ЗНАЧИМОСТИ (ПП-127)
|
||||
# ============================================================
|
||||
|
||||
def _num(s):
|
||||
"""«121 118 270 руб», «1 000», «2 212.5» → число (float) или None."""
|
||||
s = str(s or '').strip()
|
||||
if not s:
|
||||
return None
|
||||
s = s.replace('\u00a0', ' ').replace(' ', '')\
|
||||
.replace('руб', '').replace('₽', '').replace('р.', '').replace(',', '.')
|
||||
try:
|
||||
return float(s)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _load_json(name):
|
||||
import json as _json
|
||||
import os as _os
|
||||
p = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)), 'dictionaries', name)
|
||||
try:
|
||||
with open(p, encoding='utf-8') as f:
|
||||
return _json.load(f)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def _calc_criterion_8_9(company, isys, num):
|
||||
"""Расчёт показателя 8 (ущерб субъекту КИИ) или 9 (ущерб бюджету РФ).
|
||||
|
||||
Возвращает (текст_обоснования, категория или None).
|
||||
Логика — как в авторасчёте программы (окно ПП-127), текст — по образцу
|
||||
заполнения заказчика для ОПК.
|
||||
"""
|
||||
# ΔДоходов: ущерб из вкладки «Организация» или балансовая стоимость объекта
|
||||
d = _num(getattr(company, 'income_loss', '') or '')
|
||||
if d is None:
|
||||
d = _num(getattr(isys, 'balance_value', '') or '')
|
||||
# Доходы за 5 лет: основное поле income_5y (суммарно), иначе — обратная совместимость по годам
|
||||
inc5 = _num(getattr(company, 'income_5y', '') or '')
|
||||
if inc5:
|
||||
r_avg = inc5 / 5.0
|
||||
else:
|
||||
incs = [_num(getattr(company, f'income_year_{i}', '') or '') for i in range(1, 6)]
|
||||
incs = [x for x in incs if x is not None]
|
||||
r_avg = (sum(incs) / len(incs)) if incs else None
|
||||
|
||||
if num == '8':
|
||||
# Фолбэк: ущерб не заполнен → оценка «доход за год × 5» (наихудший сценарий)
|
||||
d_estimated = False
|
||||
if d is None and r_avg:
|
||||
d = r_avg * 5
|
||||
d_estimated = True
|
||||
if d is None or not r_avg:
|
||||
return ("Показатель применим. Для расчёта заполните данные организации: "
|
||||
"«Снижение уровня дохода» и доходы за 5 лет, или балансовую стоимость объекта КИИ.", None)
|
||||
val_pct = d / r_avg * 100
|
||||
if val_pct > 20:
|
||||
cat = 'I'
|
||||
elif val_pct > 10:
|
||||
cat = 'II'
|
||||
elif val_pct >= 1:
|
||||
cat = 'III'
|
||||
else:
|
||||
cat = None
|
||||
text = (f"Реализация компьютерных атак на объект не приведет к снижению доходов субъекта КИИ. "
|
||||
f"Объем среднегодового дохода субъекта КИИ (с учетом НДС), усредненный за прошедший "
|
||||
f"5-летний период, составляет {r_avg:,.2f} руб. ")
|
||||
if d_estimated:
|
||||
text += (f"Снижение уровня дохода не заполнено, поэтому оценка ущерба принята по наихудшему "
|
||||
f"сценарию: доход за год × 5 лет = {r_avg:,.2f} × 5 = {d:,.2f} руб. ")
|
||||
else:
|
||||
text += (f"В случае выхода из строя объекта КИИ потребуется замена, установка и настройка, "
|
||||
f"стоимость которых составит около {d:,.2f} руб. ")
|
||||
text += (f"Абсолютный ущерб субъекта КИИ: U={d:,.2f} руб. "
|
||||
f"Показатель ущерба субъекту КИИ: U_8 = U / R_год = {d:,.2f} / {r_avg:,.2f} "
|
||||
f"= {val_pct:.6f} %.")
|
||||
if cat:
|
||||
text += (f" Так как {val_pct:.6f} % более 1% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"присваивается {cat}-я категория значимости.")
|
||||
else:
|
||||
text += (f" Так как {val_pct:.6f} % менее 1% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
# num == '9'
|
||||
if d is None:
|
||||
return ("Показатель применим. Для расчёта заполните данные организации: "
|
||||
"«Снижение уровня дохода» или балансовую стоимость объекта КИИ.", None)
|
||||
profit = _num(getattr(company, 'profit', '') or '')
|
||||
tax = _num(getattr(company, 'profit_tax', '') or '')
|
||||
if profit and tax is not None and profit > 0:
|
||||
knp = tax / profit
|
||||
dnp = d * knp
|
||||
if d > profit:
|
||||
dnp = tax
|
||||
else:
|
||||
knp = 0.0
|
||||
dnp = 0.0
|
||||
DEFAULT_BUDGET_AVG = 21228053357700.0 # Бусредн — прогноз годового дохода федбюджета, усреднённый за 3 года
|
||||
b = DEFAULT_BUDGET_AVG
|
||||
val_pct = dnp / b * 100
|
||||
if val_pct > 0.001:
|
||||
cat = 'I'
|
||||
elif val_pct > 0.0006:
|
||||
cat = 'II'
|
||||
elif val_pct > 0.0003:
|
||||
cat = 'III'
|
||||
else:
|
||||
cat = None
|
||||
text = (f"Реализация компьютерных атак на объект не приведет к снижению доходов федерального бюджета. "
|
||||
f"Объем прогнозируемого годового дохода федерального бюджета Российской Федерации, "
|
||||
f"усредненный за планируемый трехлетний период, составляет {b:,.2f} руб. "
|
||||
f"Коэффициент выплат (отчислений) от налога на прибыль субъекта КИИ = {knp:.4f}. "
|
||||
f"В случае выхода из строя объекта КИИ потребуется замена, установка и настройка, "
|
||||
f"стоимость которых составит около {d:,.2f} руб. "
|
||||
f"Снижение выплат (отчислений) от налога на прибыль субъекта КИИ в бюджеты Российской Федерации "
|
||||
f"Уабс = {d:,.2f} × {knp:.4f} = {dnp:,.2f} руб. (приближённо). "
|
||||
f"Показатель ущерба бюджетам = {val_pct:.6f} %.")
|
||||
if cat:
|
||||
text += (f" Так как {val_pct:.6f} % более 0,0003% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"присваивается {cat}-я категория значимости.")
|
||||
else:
|
||||
text += (f" Так как {val_pct:.6f} % менее 0,0003% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
|
||||
def _calc_criterion_8_opk(company, isys):
|
||||
"""Показатель 8 (ущерб субъекту КИИ) по отраслевым особенностям ОПК
|
||||
(ПП РФ № 796 от 27.06.2026, п. 18–20):
|
||||
|
||||
U_8 = U_у8 / R_год × 100%,
|
||||
U_у8 = C + A + X,
|
||||
|
||||
где C — суммарные затраты на объект КИИ;
|
||||
A — затраты на возмещение ущерба потерпевшим и их имуществу (если инцидент
|
||||
не влечёт ущерба, A = 0);
|
||||
X — ущерб от ранее произошедших компьютерных инцидентов (если инцидентов
|
||||
не было или нет данных, X = 0);
|
||||
R_год — усреднённый объём годового дохода за прошедший 5-летний период.
|
||||
|
||||
Значения C/A/X берутся из criterion_input (ключи 8_c, 8_a, 8_x);
|
||||
если C не заполнен — используется балансовая стоимость объекта КИИ.
|
||||
Возвращает (текст_обоснования, категория или None).
|
||||
"""
|
||||
crit = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||
c_val = _num(crit.get('8_c', '') or '') or _num(getattr(isys, 'balance_value', '') or '')
|
||||
a_val = _num(crit.get('8_a', '') or '') or 0.0
|
||||
x_val = _num(crit.get('8_x', '') or '') or 0.0
|
||||
|
||||
inc5 = _num(getattr(company, 'income_5y', '') or '')
|
||||
if inc5:
|
||||
r_avg = inc5 / 5.0
|
||||
else:
|
||||
incs = [_num(getattr(company, f'income_year_{i}', '') or '') for i in range(1, 6)]
|
||||
incs = [x for x in incs if x is not None]
|
||||
r_avg = (sum(incs) / len(incs)) if incs else None
|
||||
|
||||
if c_val is None or not r_avg:
|
||||
return ("Показатель применим (отраслевые особенности ОПК, ПП РФ № 796). "
|
||||
"Для расчёта заполните суммарные затраты на объект КИИ (C) и доходы за 5 лет "
|
||||
"(или балансовую стоимость объекта КИИ).", None)
|
||||
|
||||
u_y8 = c_val + a_val + x_val
|
||||
val_pct = u_y8 / r_avg * 100
|
||||
if val_pct > 20:
|
||||
cat = 'I'
|
||||
elif val_pct > 10:
|
||||
cat = 'II'
|
||||
elif val_pct >= 1:
|
||||
cat = 'III'
|
||||
else:
|
||||
cat = None
|
||||
|
||||
text = (f"Показатель 8 рассчитывается по отраслевым особенностям ОПК "
|
||||
f"(ПП РФ № 796 от 27.06.2026): U_8 = U_у8 / R_год × 100%, U_у8 = C + A + X. "
|
||||
f"Суммарные затраты на объект КИИ: C = {c_val:,.2f} руб. "
|
||||
f"Затраты на возмещение ущерба потерпевшим: A = {a_val:,.2f} руб. "
|
||||
f"Ущерб от ранее произошедших компьютерных инцидентов: X = {x_val:,.2f} руб. "
|
||||
f"Ущерб субъекту КИИ: U_у8 = {c_val:,.2f} + {a_val:,.2f} + {x_val:,.2f} "
|
||||
f"= {u_y8:,.2f} руб. "
|
||||
f"Усреднённый годовой доход за 5-летний период: R_год = {r_avg:,.2f} руб. "
|
||||
f"Показатель ущерба: U_8 = {u_y8:,.2f} / {r_avg:,.2f} × 100% = {val_pct:.6f} %.")
|
||||
if cat:
|
||||
text += (f" Так как {val_pct:.6f} % более 1% (пороговое значение для 3-й категории "
|
||||
f"значимости), по данному показателю объекту присваивается {cat}-я категория значимости.")
|
||||
else:
|
||||
text += (f" Так как {val_pct:.6f} % менее 1% (пороговое значение для 3-й категории "
|
||||
f"значимости), по данному показателю объекту категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
|
||||
def _calc_criterion_13_opk(company, isys, num):
|
||||
"""Показатели 13а/13б по отраслевым особенностям ОПК (ПП РФ № 796, п. 34, 38):
|
||||
|
||||
13а: V = (1 − (V_max − V_инц) / V_ГОЗ) × 100% (снижение объёма продукции ГОЗ)
|
||||
13б: T = (1 − (t_норм / t_ненорм)) × 100% (увеличение времени изготовления)
|
||||
|
||||
Значения берутся из criterion_input: 13a_vmax, 13a_vinc, 13a_vgoz;
|
||||
13b_tnorm, 13b_tnonorm. Категория — по порогам ПП-127 (CRITERION_QUANT_THRESHOLDS).
|
||||
Возвращает (текст_обоснования, категория или None).
|
||||
"""
|
||||
crit = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||
num = str(num)
|
||||
if num == '13а':
|
||||
vmax = _num(crit.get('13a_vmax', '') or '')
|
||||
vinc = _num(crit.get('13a_vinc', '') or '')
|
||||
vgoz = _num(crit.get('13a_vgoz', '') or '')
|
||||
if vmax is None or vinc is None or not vgoz:
|
||||
return ("Показатель 13а рассчитывается по отраслевым особенностям ОПК "
|
||||
"(ПП РФ № 796): V = (1 − (V_max − V_инц) / V_ГОЗ) × 100%. "
|
||||
"Заполните V_max, V_инц, V_ГОЗ (объёмы продукции ГОЗ).", None)
|
||||
val = (1 - (vmax - vinc) / vgoz) * 100
|
||||
formula = (f"V = (1 − ({vmax:,.2f} − {vinc:,.2f}) / {vgoz:,.2f}) × 100% = {val:.6f} %")
|
||||
else: # 13б
|
||||
tnorm = _num(crit.get('13b_tnorm', '') or '')
|
||||
tnon = _num(crit.get('13b_tnonorm', '') or '')
|
||||
if tnorm is None or tnon is None or tnon <= 0:
|
||||
return ("Показатель 13б рассчитывается по отраслевым особенностям ОПК "
|
||||
"(ПП РФ № 796): T = (1 − (t_норм / t_ненорм)) × 100%. "
|
||||
"Заполните t_норм и t_ненорм (время операций по ГОЗ).", None)
|
||||
val = (1 - tnorm / tnon) * 100
|
||||
formula = (f"T = (1 − ({tnorm:,.2f} / {tnon:,.2f})) × 100% = {val:.6f} %")
|
||||
|
||||
cat = _criterion_other_cat(num, val)
|
||||
text = (f"Показатель {num} рассчитывается по отраслевым особенностям ОПК "
|
||||
f"(ПП РФ № 796 от 27.06.2026): {formula}.")
|
||||
if cat:
|
||||
text += (f" По порогам ПП-127 это соответствует {cat}-й категории значимости.")
|
||||
else:
|
||||
text += " По порогам ПП-127 категория значимости не присваивается."
|
||||
return text, cat
|
||||
|
||||
|
||||
# Пороги показателя 10 и подпоказателей 10.1–10.7 (ПП-127, «Значение показателя»)
|
||||
CRITERION_10_THRESHOLDS = {
|
||||
# num: (категория → (нижняя граница, верхняя граница)), границы «включительно»
|
||||
# Показатель 10 — среднедневное количество операций (млн ед.):
|
||||
# III — менее или равно 70, II — более 70 но менее или равно 120, I — более 120.
|
||||
'10': {'I': (120, None), 'II': (70, 120), 'III': (None, 70)},
|
||||
'10.1': {'I': (10, None), 'II': (1, 10), 'III': (None, 1)}, # трлн руб.
|
||||
'10.2': {'I': (25, None), 'II': (10, 25), 'III': (None, 10)}, # тыс. шт.
|
||||
'10.3': {'I': (2000, None), 'II': (1000, 2000), 'III': (50, 1000)}, # млрд руб.
|
||||
'10.4': {'I': (5000, None), 'II': (1500, 5000), 'III': (100, 1500)}, # млрд руб.
|
||||
'10.5': {'I': (150, None), 'II': (100, 150), 'III': (25, 100)}, # договоров
|
||||
'10.6': {'I': (5000, None), 'II': (1000, 5000), 'III': (100, 1000)}, # млрд руб.
|
||||
'10.7': {'I': (500, None), 'II': (200, 500), 'III': (30, 200)}, # млн ед.
|
||||
}
|
||||
|
||||
|
||||
def _criterion_10_cat(num, value):
|
||||
"""Категория значимости по показателю 10 / 10.x (ПП-127).
|
||||
|
||||
value — численное значение показателя. Возвращает 'I'/'II'/'III' или None.
|
||||
"""
|
||||
thr = CRITERION_10_THRESHOLDS.get(str(num))
|
||||
if not thr or value is None:
|
||||
return None
|
||||
for cat in ('I', 'II', 'III'):
|
||||
lo, hi = thr[cat]
|
||||
if lo is not None and value < lo:
|
||||
continue
|
||||
if hi is not None and value > hi:
|
||||
continue
|
||||
return cat
|
||||
return None
|
||||
|
||||
|
||||
def _calc_criterion_10(company, isys, num):
|
||||
"""Расчёт показателя 10 (прекращение финансовых операций) и подпоказателей 10.1–10.7.
|
||||
|
||||
Показатель 10: среднедневное количество операций = операции за год (млн ед.) / дни.
|
||||
Подпоказатели 10.x: значение показателя (в своих единицах) сравнивается с порогами.
|
||||
Возвращает (текст_обоснования, категория или None).
|
||||
"""
|
||||
num = str(num)
|
||||
if num not in CRITERION_10_THRESHOLDS:
|
||||
return None, None
|
||||
if num == '10':
|
||||
ops = _num(getattr(company, 'ops_count', '') or '')
|
||||
days = _num(getattr(company, 'calendar_days', '') or '')
|
||||
if days is None or days <= 0:
|
||||
days = 365
|
||||
if ops is None:
|
||||
return ("Показатель применим. Для расчёта заполните данные организации: "
|
||||
"«Количество операций за прошедший год (млн ед.)».", None)
|
||||
val = ops / days
|
||||
cat = _criterion_10_cat('10', val)
|
||||
text = (f"Показатель прекращения финансовых операций оценивается среднедневным количеством "
|
||||
f"операций, проведенных клиентами по банковским счетам и (или) без открытия банковского "
|
||||
f"счета. Количество операций за прошедший год: {ops:,.2f} млн ед. Количество календарных "
|
||||
f"дней в году: {days:.0f}. Среднедневное количество операций: {ops:,.2f} / {days:.0f} "
|
||||
f"= {val:.6f} млн ед./день.")
|
||||
else:
|
||||
value = _num(getattr(company, 'criterion10_value', '') or '')
|
||||
if value is None:
|
||||
return (f"Показатель применим. Для расчёта заполните значение показателя "
|
||||
f"«{num}» в данных организации.", None)
|
||||
val = value
|
||||
cat = _criterion_10_cat(num, val)
|
||||
text = (f"Показатель {num} оценивается значением показателя: {val:,.6f} "
|
||||
f"(в единицах, установленных ПП-127).")
|
||||
if cat:
|
||||
text += (f" Согласно Постановлению № 127 полученное значение соответствует "
|
||||
f"{cat}-й категории значимости, по данному показателю объекту присваивается "
|
||||
f"{cat}-я категория значимости.")
|
||||
else:
|
||||
text += (" Значение не достигает порога III категории — по данному показателю "
|
||||
"категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
|
||||
# Пороги количественных показателей 1–7, 11–14 (ПП-127), кроме 8/9/10:
|
||||
# num → [(категория, lo, lo_incl, hi, hi_incl)] — значение соответствует, если
|
||||
# (lo is None или value > lo при not lo_incl / value >= lo при lo_incl) и
|
||||
# (hi is None или value < hi при not hi_incl / value <= hi при hi_incl).
|
||||
CRITERION_QUANT_THRESHOLDS = {
|
||||
'1': [('III', 1, True, 50, True), ('II', 50, False, 500, True), ('I', 500, False, None, False)], # человек
|
||||
'2б': [('III', 2, True, 1000, False), ('II', 1000, True, 5000, False), ('I', 5000, True, None, False)], # тыс. чел.
|
||||
'3б': [('III', 2, True, 1000, False), ('II', 1000, True, 5000, False), ('I', 5000, True, None, False)], # тыс. чел.
|
||||
'4а': [('III', 3, True, 1000, False), ('II', 1000, True, 5000, False), ('I', 5000, True, None, False)], # тыс. абонентов
|
||||
'5а': [('III', 12, False, 24, True), ('II', 6, False, 12, True), ('I', None, False, 6, True)], # часов
|
||||
'5б': [('III', None, False, 30, True), ('II', 30, False, 70, True), ('I', 70, False, None, False)], # %
|
||||
'11б': [('III', 2, True, 1000, False), ('II', 1000, True, 5000, False), ('I', 5000, True, None, False)], # тыс. чел.
|
||||
'13а': [('III', 0, False, 10, True), ('II', 10, False, 15, True), ('I', 15, False, None, False)], # %
|
||||
'13б': [('III', 0, False, 10, True), ('II', 10, False, 40, True), ('I', 40, False, None, False)], # %
|
||||
'14': [('III', 2, False, 4, True), ('II', 1, False, 2, True), ('I', None, False, 1, True)], # часов
|
||||
}
|
||||
|
||||
# Качественные показатели (выбор одного из вариантов III/II/I): 2а, 3а, 3в, 3г, 4б, 6, 7, 11а, 12, 13.1
|
||||
CRITERION_CHOICE_INDICATORS = {'2а', '3а', '3в', '3г', '4б', '6', '7', '11а', '12', '13.1'}
|
||||
|
||||
# Единицы измерения количественных показателей (для подписи в окне авторасчёта)
|
||||
CRITERION_QUANT_UNITS = {
|
||||
'1': 'человек', '2б': 'тыс. человек', '3б': 'тыс. человек', '4а': 'тыс. абонентов',
|
||||
'5а': 'часов', '5б': '%', '11б': 'тыс. человек', '13а': '%', '13б': '%', '14': 'часов',
|
||||
}
|
||||
|
||||
|
||||
def _criterion_other_cat(num, value):
|
||||
"""Категория количественного показателя (кроме 8/9/10) по значению (ПП-127)."""
|
||||
rules = CRITERION_QUANT_THRESHOLDS.get(str(num))
|
||||
if not rules or value is None:
|
||||
return None
|
||||
for cat, lo, lo_incl, hi, hi_incl in rules:
|
||||
ok = True
|
||||
if lo is not None:
|
||||
ok = ok and ((value >= lo) if lo_incl else (value > lo))
|
||||
if hi is not None:
|
||||
ok = ok and ((value <= hi) if hi_incl else (value < hi))
|
||||
if ok:
|
||||
return cat
|
||||
return None
|
||||
|
||||
|
||||
def _calc_criterion_other(company, isys, num, value=None, choice=None):
|
||||
"""Расчёт остальных показателей критериев значимости (1–7, 11–14).
|
||||
|
||||
Количественные (1, 2б, 3б, 4а, 5а, 5б, 11б, 13а, 13б, 14): value — число,
|
||||
категория по порогам ПП-127 (CRITERION_QUANT_THRESHOLDS).
|
||||
Качественные (2а, 3а, 3в, 3г, 4б, 6, 7, 11а, 12, 13.1): choice — текст
|
||||
выбранного варианта из criteria_127.json (values), категория — его ключ.
|
||||
Возвращает (текст_обоснования, категория или None).
|
||||
"""
|
||||
num = str(num)
|
||||
if num in CRITERION_CHOICE_INDICATORS:
|
||||
# Качественный показатель: выбираем один из вариантов III/II/I
|
||||
criteria = _load_json('criteria_127.json')
|
||||
for grp in (criteria if isinstance(criteria, list) else []):
|
||||
for ind in grp.get('indicators', []):
|
||||
if str(ind.get('num', '')) == num:
|
||||
vals = ind.get('values', {}) or {}
|
||||
if not choice:
|
||||
return (f"Показатель применим. Для расчёта выберите один из вариантов "
|
||||
f"(III/II/I категория) в окне авторасчёта.", None)
|
||||
cat = None
|
||||
for c, txt in vals.items():
|
||||
if txt and str(txt).strip() == str(choice).strip():
|
||||
cat = c
|
||||
break
|
||||
if not cat:
|
||||
return (f"Показатель применим. Выбранный вариант не найден в перечне значений.", None)
|
||||
text = (f"Показатель {num}: {choice}. Согласно Постановлению № 127 данному значению "
|
||||
f"соответствует {cat}-я категория значимости.")
|
||||
return text, cat
|
||||
return (f"Показатель применим. Варианты значений не найдены в справочнике.", None)
|
||||
|
||||
# Количественный показатель
|
||||
if value is None:
|
||||
unit = CRITERION_QUANT_UNITS.get(num, '')
|
||||
return (f"Показатель применим. Для расчёта введите значение показателя"
|
||||
+ (f" ({unit})" if unit else "") + ".", None)
|
||||
cat = _criterion_other_cat(num, value)
|
||||
unit = CRITERION_QUANT_UNITS.get(num, '')
|
||||
text = (f"Показатель {num}: значение показателя — {value:,.6f} {unit}. "
|
||||
f"Согласно Постановлению № 127 ")
|
||||
if cat:
|
||||
text += (f"данному значению соответствует {cat}-я категория значимости.")
|
||||
else:
|
||||
text += ("значение не достигает порога III категории — категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
|
||||
def _build_org_measures_text(company, isys=None):
|
||||
"""Формирует текст организационных мер из словаря org_measures.json
|
||||
с учётом ответов раздела «Физическая безопасность» опросника.
|
||||
|
||||
Если в опроснике указано «нет» (охрана, сигнализация, тревожная кнопка,
|
||||
видеонаблюдение, металлические двери и т.п.) — соответствующие пункты
|
||||
из текста убираются. Пункты, не привязанные к средствам, остаются всегда.
|
||||
"""
|
||||
import os as _os
|
||||
items = _load_json('org_measures.json')
|
||||
if not isinstance(items, list):
|
||||
items = []
|
||||
ps = getattr(company, 'physical_security', None) or {}
|
||||
|
||||
def _no(*keys):
|
||||
"""True, если хотя бы по одному ключу явно указано «нет»/«не имеется»."""
|
||||
for k in keys:
|
||||
v = str(ps.get(k) or '').strip().lower()
|
||||
if v in ('нет', 'не имеется', 'нет,', '0', 'false'):
|
||||
return True
|
||||
return False
|
||||
|
||||
rules = [
|
||||
# (подстрока пункта, ключи physical_security, при которых пункт УБИРАЕМ)
|
||||
('Физическая охрана периметра', ('physicalGuard', 'physicalTurnstile', 'physicalVisitorLog')),
|
||||
('Имеется система видеонаблюдения', ('physicalVideoSurveillance',)),
|
||||
('Здания не имеют окон', ('physicalMetalDoors',)),
|
||||
('охранно-пожарная сигнализация и тревожная кнопка', ('physicalFireAlarm', 'physicalPanicButton')),
|
||||
('охранно-пожарная сигнализация', ('physicalFireAlarm',)),
|
||||
('тревожная кнопка', ('physicalPanicButton',)),
|
||||
('турникетом на проходной', ('physicalTurnstile',)),
|
||||
('СКУД', ('physicalTurnstile',)),
|
||||
('учет посетителей', ('physicalVisitorLog',)),
|
||||
]
|
||||
|
||||
out = []
|
||||
for line in items:
|
||||
line = str(line).strip()
|
||||
if not line:
|
||||
continue
|
||||
drop = False
|
||||
for sub, keys in rules:
|
||||
if sub in line and _no(*keys):
|
||||
drop = True
|
||||
break
|
||||
if not drop:
|
||||
out.append(line)
|
||||
return '\n'.join(out) if out else '—'
|
||||
|
||||
|
||||
def _build_categorization_indicators(company, is_list):
|
||||
"""Строки таблицы показателей критериев значимости для шаблона
|
||||
«Сведения о результатах категорирования» (цикл categorization_indicators).
|
||||
|
||||
Для каждого показателя ПП-127 (5 групп, показатели 1–14 с подпунктами):
|
||||
section, name, number, value, justification, category.
|
||||
- Если показатель не применим для сферы объекта — value = «Показатель не применим.»,
|
||||
justification — текст из словаря обоснований по сфере (или заготовка).
|
||||
- Если применим (8/9) — автоматический расчёт из данных организации.
|
||||
"""
|
||||
import re as _re
|
||||
criteria = _load_json('criteria_127.json')
|
||||
applicability = _load_json('criteria_applicability.json')
|
||||
justifications = _load_json('criteria_justifications.json')
|
||||
if not isinstance(criteria, list):
|
||||
criteria = []
|
||||
|
||||
sphere = getattr(is_list[0], 'sphere', '') if is_list else ''
|
||||
sphere = (sphere or '').strip()
|
||||
appl = applicability.get(sphere) if isinstance(applicability, dict) else None
|
||||
just = (justifications.get(sphere) or {}) if isinstance(justifications, dict) else {}
|
||||
|
||||
isys = is_list[0] if is_list else None
|
||||
|
||||
rows = []
|
||||
first_cat_num = None # номер показателя, по которому объект отнесён к I категории (п. 6 ПП-127)
|
||||
# Введённые значения показателей (из опросника/окна авторасчёта): {num: значение}
|
||||
crit_in = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||
for grp in criteria:
|
||||
section = grp.get('criteria', '')
|
||||
for ind in grp.get('indicators', []):
|
||||
num = str(ind.get('num', ''))
|
||||
name = ind.get('name', '')
|
||||
m = _re.match(r'(\d+)', num)
|
||||
grp_num = m.group(1) if m else num
|
||||
applicable = appl is None or grp_num in appl
|
||||
|
||||
value = '—'
|
||||
justification = '—'
|
||||
category = '—'
|
||||
if first_cat_num is not None:
|
||||
# Остановка расчёта: объект уже отнесён к I категории по одному из показателей
|
||||
# (п. 6 Правил, утв. ПП РФ № 127: «расчет по остальным показателям не проводится»).
|
||||
if applicable:
|
||||
value = '—'
|
||||
justification = (f"Расчёт по данному показателю не проводится: объект отнесён к "
|
||||
f"I категории значимости по показателю {first_cat_num} "
|
||||
f"(п. 6 Правил категорирования, утв. ПП РФ № 127).")
|
||||
category = '—'
|
||||
else:
|
||||
value = 'Показатель не применим.'
|
||||
justification = (just.get(num) or just.get(grp_num)
|
||||
or f"Показатель не применим к объекту КИИ организации сферы «{sphere or '—'}».")
|
||||
elif not applicable:
|
||||
value = 'Показатель не применим.'
|
||||
justification = (just.get(num) or just.get(grp_num)
|
||||
or f"Показатель не применим к объекту КИИ организации сферы «{sphere or '—'}».")
|
||||
elif grp_num in ('8', '9'):
|
||||
if grp_num == '8' and sphere == 'Оборонная промышленность':
|
||||
# ОПК: U_8 = (C+A+X)/R_год×100% (ПП РФ № 796, п. 18–20)
|
||||
text, cat = _calc_criterion_8_opk(company, isys)
|
||||
else:
|
||||
text, cat = _calc_criterion_8_9(company, isys, grp_num)
|
||||
if text:
|
||||
value = text
|
||||
if cat:
|
||||
category = f"{cat}-я категория"
|
||||
else:
|
||||
category = '—'
|
||||
elif grp_num == '10':
|
||||
text, cat = _calc_criterion_10(company, isys, num)
|
||||
if text:
|
||||
value = text
|
||||
if cat:
|
||||
category = f"{cat}-я категория"
|
||||
else:
|
||||
category = '—'
|
||||
elif num in ('13а', '13б') and sphere == 'Оборонная промышленность':
|
||||
# ОПК: V = (1−(V_max−V_инц)/V_ГОЗ)×100%, T = (1−(t_норм/t_ненорм))×100%
|
||||
# (ПП РФ № 796, п. 34, 38) — при наличии введённых составляющих
|
||||
_opk_crit = dict(getattr(isys, 'criterion_input', None) or {}) if isys else {}
|
||||
_opk_ready = bool(_opk_crit.get('13a_vmax', '') or _opk_crit.get('13a_vinc', '') or _opk_crit.get('13a_vgoz', '') or _opk_crit.get('13b_tnorm', '') or _opk_crit.get('13b_tnonorm', ''))
|
||||
if _opk_ready:
|
||||
text, cat = _calc_criterion_13_opk(company, isys, num)
|
||||
if text:
|
||||
value = text
|
||||
if cat:
|
||||
category = f"{cat}-я категория"
|
||||
else:
|
||||
category = '—'
|
||||
else:
|
||||
value = '—'
|
||||
justification = (just.get(num) or just.get(grp_num)
|
||||
or 'Показатель применим к объекту КИИ; значение определяется по данным объекта (для ОПК — по формулам ПП РФ № 796: V = (1−(V_max−V_инц)/V_ГОЗ)×100%, T = (1−(t_норм/t_ненорм))×100%).')
|
||||
else:
|
||||
# Применимый показатель без формульного авторасчёта (1–7, 11–14):
|
||||
# если значение введено (опросник/окно авторасчёта) — считаем категорию по порогам
|
||||
if num in crit_in and str(crit_in.get(num, '')).strip():
|
||||
raw = str(crit_in.get(num, '')).strip()
|
||||
# Число или выбранный вариант (качественный)
|
||||
try:
|
||||
_v = float(raw.replace(' ', '').replace(',', '.').replace('\u00a0', ''))
|
||||
text, cat = _calc_criterion_other(company, isys, num, value=_v)
|
||||
except ValueError:
|
||||
text, cat = _calc_criterion_other(company, isys, num, choice=raw)
|
||||
if text:
|
||||
value = text
|
||||
if cat:
|
||||
category = f"{cat}-я категория"
|
||||
else:
|
||||
category = '—'
|
||||
else:
|
||||
value = '—'
|
||||
justification = (just.get(num) or just.get(grp_num)
|
||||
or 'Показатель применим к объекту КИИ; значение определяется по данным объекта.')
|
||||
|
||||
# Если по показателю присвоена I категория — фиксируем для остановки расчёта
|
||||
# (точное совпадение «I-я категория», а не startswith('I'), иначе ловится II/III)
|
||||
if first_cat_num is None and category.startswith('I-я'):
|
||||
first_cat_num = num
|
||||
|
||||
rows.append({
|
||||
'section': section,
|
||||
'name': name,
|
||||
'number': num,
|
||||
'value': value,
|
||||
'justification': justification,
|
||||
'category': category,
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
# ============================================================
|
||||
# РЕЗОЛВЕР ЗНАЧЕНИЙ
|
||||
# ============================================================
|
||||
|
||||
def _find_fstek_office(address_text: str):
|
||||
"""Определяет управление ФСТЭК России по адресу организации.
|
||||
|
||||
Ищет город/регион из адреса в словаре fstek_offices.json.
|
||||
Возвращает dict {'name': ..., 'address': ...} или None.
|
||||
"""
|
||||
import json as _json
|
||||
import os as _os
|
||||
if not address_text:
|
||||
return None
|
||||
dict_path = _os.path.join(_os.path.dirname(__file__), 'dictionaries', 'fstek_offices.json')
|
||||
try:
|
||||
with open(dict_path, encoding='utf-8') as _f:
|
||||
offices = _json.load(_f)
|
||||
except Exception:
|
||||
return None
|
||||
hay = address_text.lower()
|
||||
# Нормализация сокращений: обл.→область, респ.→республика, г.о.→городской округ
|
||||
hay = hay.replace('обл.', 'область').replace('обл,', 'область')
|
||||
hay = hay.replace('респ.', 'республика').replace('респ,', 'республика')
|
||||
hay = hay.replace('г.о.', 'городской округ').replace('г.о,', 'городской округ')
|
||||
hay = hay.replace('ао', 'автономный округ')
|
||||
best = None
|
||||
best_len = 0
|
||||
for off in offices:
|
||||
# Ищем по городам
|
||||
for city in off.get('cities', []):
|
||||
if city in hay and len(city) > best_len:
|
||||
best = off
|
||||
best_len = len(city)
|
||||
# Ищем по регионам (область/край/республика)
|
||||
for region in off.get('regions', []):
|
||||
if region in hay and len(region) > best_len:
|
||||
best = off
|
||||
best_len = len(region)
|
||||
return best
|
||||
|
||||
|
||||
def _resolve(source: str, ctx: Dict) -> str:
|
||||
"""Преобразует ключ-источник в значение, подставляя из контекста."""
|
||||
company = ctx.get('company')
|
||||
@@ -422,6 +1070,16 @@ def _resolve(source: str, ctx: Dict) -> str:
|
||||
if source in commission_data:
|
||||
return str(commission_data[source])
|
||||
|
||||
# Управление ФСТЭК (адресат отправки сведений о категорировании)
|
||||
if source in ('fstek_recipient_address', 'fstek_district', 'fstek_office_name'):
|
||||
addr = getattr(company, 'address', '') or ''
|
||||
off = _find_fstek_office(addr)
|
||||
if off:
|
||||
if source == 'fstek_recipient_address':
|
||||
return off.get('address', '') or ''
|
||||
return off.get('name', '') or ''
|
||||
return ''
|
||||
|
||||
# Даты
|
||||
if source.startswith('date:'):
|
||||
fmt = source.split(':', 1)[1]
|
||||
@@ -448,11 +1106,6 @@ def _resolve(source: str, ctx: Dict) -> str:
|
||||
'name': 'name',
|
||||
'description': 'description',
|
||||
'category': 'category',
|
||||
'defence_level': 'defence_level',
|
||||
'threat_type': 'threat_type',
|
||||
'pd_list': lambda: ', '.join(first.personal_data_list or []),
|
||||
'pd_count': 'pd_count',
|
||||
'pd_subjects': lambda: '; '.join(first.pd_subjects_list or []),
|
||||
'users': lambda: ', '.join(first.users_list or []),
|
||||
}
|
||||
if field in field_map:
|
||||
@@ -501,21 +1154,25 @@ def _get_fio(company: Company, who: str) -> str:
|
||||
|
||||
|
||||
def _get_security_text(ctx: Dict) -> str:
|
||||
"""Возвращает текст требований для уровня защищённости."""
|
||||
defence_level = ctx.get('extended_fields', {}).get('defence_level', '4')
|
||||
"""Возвращает текст требований к защите объекта КИИ по категории значимости."""
|
||||
is_list = ctx.get('is_list', [])
|
||||
if not defence_level and is_list:
|
||||
defence_level = str(getattr(is_list[0], 'defence_level', '4') or '4')
|
||||
dl = str(defence_level).strip()[0]
|
||||
category = ''
|
||||
if is_list:
|
||||
category = str(getattr(is_list[0], 'category', '') or '').strip()
|
||||
|
||||
TEXTS = {
|
||||
'1': """Для обеспечения 1-го уровня защищённости...""",
|
||||
'2': """Для обеспечения 2-го уровня защищённости...""",
|
||||
'3': """Для обеспечения 3-го уровня защищённости...""",
|
||||
'4': """Для обеспечения 4-го уровня защищённости персональных данных необходимо:
|
||||
— режим безопасности помещений;
|
||||
— сохранность носителей ПДн;
|
||||
— утверждение перечня лиц с доступом к ПДн;
|
||||
— использование сертифицированных СЗИ.""",
|
||||
'1': """Для значимого объекта КИИ 1-й категории значимости необходимо:
|
||||
— создание системы безопасности значимого объекта КИИ;
|
||||
— обеспечение защиты информации от компьютерных атак;
|
||||
— категорирование объектов КИИ и контроль его результатов.""",
|
||||
'2': """Для значимого объекта КИИ 2-й категории значимости необходимо:
|
||||
— создание системы безопасности значимого объекта КИИ;
|
||||
— обеспечение защиты информации от компьютерных атак.""",
|
||||
'3': """Для значимого объекта КИИ 3-й категории значимости необходимо:
|
||||
— обеспечение защиты информации от компьютерных атак.""",
|
||||
}
|
||||
return TEXTS.get(dl, TEXTS['4'])
|
||||
if category in TEXTS:
|
||||
return TEXTS[category]
|
||||
return """Для объекта КИИ, не являющегося значимым, необходимо:
|
||||
— принятие мер по защите информации, предусмотренных законодательством РФ о безопасности КИИ."""
|
||||
|
||||
|
||||
+587
-3
@@ -14,6 +14,18 @@
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
def _load_prompt(name):
|
||||
"""Загружает промт из dictionaries/prompts/<name>.json (system/rules/response_format)."""
|
||||
import os as _os
|
||||
p = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)),
|
||||
'dictionaries', 'prompts', name + '.json')
|
||||
try:
|
||||
with open(p, encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
import urllib.parse
|
||||
@@ -65,6 +77,17 @@ _ADDRESS_ABBR = {
|
||||
'СТ': 'ст.', 'Ш': 'ш.', 'Р-Н': 'р-н.', 'Б-Р': 'б-р.', 'МКР': 'мкр.',
|
||||
'НАБ': 'наб.', 'ТУП': 'туп.', 'ПР-Д': 'пр-д.', 'КРП': 'крп.',
|
||||
'Г.': 'г.', 'УЛ.': 'ул.', 'Д.': 'д.', 'Р-Н.': 'р-н.',
|
||||
'Г.О': 'г.о.', 'Г.О.': 'г.о.', 'ГО': 'г.о.', 'М.О': 'м.о.', 'М.О.': 'м.о.',
|
||||
}
|
||||
|
||||
# Слова, которые в адресах пишутся со строчной буквы (кроме случаев,
|
||||
# когда стоят первым словом после точки — «г. Электроугли» и т.п.)
|
||||
_ADDRESS_LOWERCASE = {
|
||||
'область', 'край', 'республика', 'округ', 'район', 'город', 'поселок',
|
||||
'посёлок', 'деревня', 'село', 'станица', 'хутор', 'аул', 'улица',
|
||||
'проспект', 'переулок', 'бульвар', 'шоссе', 'набережная', 'площадь',
|
||||
'проезд', 'тупик', 'аллея', 'линия', 'дом', 'строение', 'корпус',
|
||||
'квартира', 'офис', 'помещение', 'этаж', 'комната', 'участок',
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +128,12 @@ def _normalize_text(text):
|
||||
|
||||
|
||||
def normalize_address(text):
|
||||
"""«Г Крымск» → «г. Крымск», «Р-Н» → «р-н.» и т.п."""
|
||||
"""«Г Крымск» → «г. Крымск», «Р-Н» → «р-н.», «МОСКОВСКАЯ ОБЛАСТЬ» → «Московская область».
|
||||
|
||||
Приводит адрес к нормальному регистру: каждое слово с заглавной буквы,
|
||||
адресные сокращения (г., ул., д., обл., г.о.) и административные термины
|
||||
(область, район, город, дом и т.п.) — со строчной.
|
||||
"""
|
||||
if not text:
|
||||
return ''
|
||||
parts = re.split(r'(\s+|[,\;])', text)
|
||||
@@ -115,7 +143,70 @@ def normalize_address(text):
|
||||
out.append(_ADDRESS_ABBR[p.strip().upper()])
|
||||
else:
|
||||
out.append(p)
|
||||
return ''.join(out)
|
||||
res = ''.join(out)
|
||||
# КАПС → нормальный регистр по словам
|
||||
res = _normalize_text(res)
|
||||
# Адресные термины — со строчной буквы (сохраняя пунктуацию)
|
||||
words = res.split(' ')
|
||||
fixed = []
|
||||
for w in words:
|
||||
m = re.match(r'^([^\w]*)([\w\-]+)([^\w]*)$', w, re.UNICODE)
|
||||
if m:
|
||||
pre, core, post = m.group(1), m.group(2), m.group(3)
|
||||
if core.lower() in _ADDRESS_LOWERCASE:
|
||||
fixed.append(pre + core.lower() + post)
|
||||
continue
|
||||
fixed.append(w)
|
||||
return ' '.join(fixed)
|
||||
|
||||
|
||||
def resolve_okved(code, dadata_token=None):
|
||||
"""Код ОКВЭД → «код название» (без дефиса), например «27.32.2 Производство силовых кабелей».
|
||||
|
||||
Принимает и голый код («27.32.2»), и строку с названием
|
||||
(«27.32.2 - Производство силовых кабелей» / «27.32.2 Производство силовых кабелей»).
|
||||
1) Ищем в словаре dictionaries/okved.json (строки вида «27.32.2 Название»).
|
||||
2) Если не нашли — запрашиваем DaData suggest okved2 (если есть токен).
|
||||
3) Иначе возвращаем нормализованную строку «код название» как есть.
|
||||
"""
|
||||
code = (code or '').strip()
|
||||
if not code:
|
||||
return ''
|
||||
# Извлекаем код из входной строки: «27.32.2 - Производство силовых кабелей» → «27.32.2»
|
||||
m = re.match(r'^\s*(\d+(?:\.\d+)*)', code)
|
||||
code_only = m.group(1) if m else code.split()[0].rstrip('.')
|
||||
# 1) Словарь
|
||||
try:
|
||||
import os, json
|
||||
dict_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries', 'okved.json')
|
||||
with open(dict_path, encoding='utf-8') as f:
|
||||
items = json.load(f)
|
||||
for item in items:
|
||||
item = str(item).strip()
|
||||
# совпадение по коду: «27.32.2» или «27.32.2 Название»
|
||||
if item.split()[0].rstrip('.') == code_only.rstrip('.'):
|
||||
return item
|
||||
except Exception:
|
||||
pass
|
||||
# 2) DaData suggest okved2
|
||||
if dadata_token:
|
||||
try:
|
||||
js, err = _http_json('https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/okved2',
|
||||
data={'query': code_only, 'count': 1},
|
||||
headers={'Content-Type': 'application/json',
|
||||
'Authorization': f'Token {dadata_token}'})
|
||||
if not err and js:
|
||||
sugg = (js.get('suggestions') or [])
|
||||
if sugg and sugg[0].get('value'):
|
||||
return sugg[0]['value']
|
||||
except Exception:
|
||||
pass
|
||||
# 3) Нормализуем вход: «27.32.2 - Название» → «27.32.2 Название» (без дефиса)
|
||||
rest = code[len(m.group(1)):].strip() if m else ''
|
||||
rest = rest.lstrip('- ').strip()
|
||||
if rest:
|
||||
return f"{code_only} {rest}"
|
||||
return code_only
|
||||
|
||||
|
||||
def _fmt_date(value):
|
||||
@@ -178,6 +269,38 @@ def check_declension_morpher(text):
|
||||
# ============================================================
|
||||
# DADATA — проверка организации по ИНН (нужен API-ключ)
|
||||
# ============================================================
|
||||
def get_company_finance_dadata(inn, token):
|
||||
"""Доходы организации по годам через DaData (findById/party → finance/finance_history).
|
||||
|
||||
Возвращает (dict, ошибка): dict вида
|
||||
{'finance': {...}, 'history': [{'year': 2023, 'income': 123456, ...}, ...]}
|
||||
finance — показатели за последний отчётный год;
|
||||
history — история по годам (если доступна в тарифе DaData).
|
||||
"""
|
||||
if not token:
|
||||
return None, "Не задан API-ключ DaData (Файл → Настройки → API)"
|
||||
inn = (inn or '').replace(' ', '')
|
||||
if not inn or not inn.isdigit():
|
||||
return None, "Введите ИНН для загрузки доходов"
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Token {token}',
|
||||
}
|
||||
js, err = _http_json(DADATA_URL, data={'query': inn}, headers=headers)
|
||||
if err:
|
||||
return None, err
|
||||
suggestions = (js or {}).get('suggestions') or []
|
||||
if not suggestions:
|
||||
return None, f"DaData: организация с ИНН {inn} не найдена"
|
||||
d = suggestions[0].get('data') or {}
|
||||
return {
|
||||
'finance': d.get('finance') or {},
|
||||
'history': d.get('finance_history') or [],
|
||||
'inn': d.get('inn', inn),
|
||||
'name': (d.get('name') or {}).get('short_with_opf') or suggestions[0].get('value', ''),
|
||||
}, None
|
||||
|
||||
|
||||
def check_company_dadata(inn, token):
|
||||
"""Проверка организации по ИНН/ОГРН через DaData.
|
||||
|
||||
@@ -220,6 +343,21 @@ def check_company_dadata(inn, token):
|
||||
chief_fio = _normalize_text(management.get('name', ''))
|
||||
ogrn_date = _fmt_date(d.get('ogrn_date') or '')
|
||||
|
||||
# ОКВЭД: основной (okved) и дополнительные (okveds — список словарей с 'code')
|
||||
okved = d.get('okved', '') or ''
|
||||
okved_extra = []
|
||||
raw_okveds = d.get('okveds') or []
|
||||
if isinstance(raw_okveds, str):
|
||||
# DaData иногда отдаёт доп. ОКВЭД одной строкой через запятую
|
||||
raw_okveds = [x.strip() for x in raw_okveds.split(',') if x.strip()]
|
||||
for ok in raw_okveds:
|
||||
if isinstance(ok, dict):
|
||||
code = (ok.get('code') or '').strip()
|
||||
else:
|
||||
code = str(ok or '').strip()
|
||||
if code:
|
||||
okved_extra.append(code)
|
||||
|
||||
return {
|
||||
'source': 'DaData',
|
||||
'source_url': 'https://dadata.ru',
|
||||
@@ -236,6 +374,8 @@ def check_company_dadata(inn, token):
|
||||
'chief_position': chief_position,
|
||||
'chief_fio': chief_fio,
|
||||
'inn': d.get('inn', inn),
|
||||
'okved': okved,
|
||||
'okved_extra': okved_extra,
|
||||
},
|
||||
'apply': {'company': {
|
||||
'full_name': full_name,
|
||||
@@ -246,6 +386,8 @@ def check_company_dadata(inn, token):
|
||||
'ogrn_date': ogrn_date,
|
||||
'chief_position': chief_position,
|
||||
'chief_fio': chief_fio,
|
||||
'okved': okved,
|
||||
'okved_extra': okved_extra,
|
||||
}},
|
||||
}, None
|
||||
|
||||
@@ -389,6 +531,320 @@ def compare_company_values(old_data, new_data):
|
||||
return diffs
|
||||
|
||||
|
||||
def _ubi_code(text):
|
||||
"""Извлекает код УБИ из строки: «УБИ.069 Название» → '069'."""
|
||||
m = re.search(r'уби\.?\s*(\d+)', str(text), re.IGNORECASE)
|
||||
return m.group(1) if m else None
|
||||
|
||||
|
||||
def _norm_threat_text(text):
|
||||
"""Нормализация названия угрозы для сравнения: без ведущего номера, лишних пробелов."""
|
||||
s = re.sub(r'^\d+[.)]?\s*', '', str(text or '').strip())
|
||||
s = re.sub(r'\s+', ' ', s).strip()
|
||||
return s.lower()
|
||||
|
||||
|
||||
def normalize_threats(text):
|
||||
"""Склеить переносы внутри названий угроз.
|
||||
|
||||
В опросниках встречается «УБИ.209 Угроза … памяти\r\nядра процессора» —
|
||||
перенос строки внутри одного названия. Строка, не начинающаяся с «УБИ.»,
|
||||
считается продолжением предыдущей угрозы.
|
||||
"""
|
||||
if not text:
|
||||
return ''
|
||||
lines = [ln.strip() for ln in str(text).split('\n') if ln.strip()]
|
||||
out = []
|
||||
for ln in lines:
|
||||
if re.match(r'^уби\.?\s*\d+', ln, re.IGNORECASE):
|
||||
out.append(ln)
|
||||
elif out:
|
||||
out[-1] = out[-1] + ' ' + ln
|
||||
else:
|
||||
out.append(ln)
|
||||
return '\n'.join(out)
|
||||
|
||||
|
||||
def split_okved_extra(value):
|
||||
"""Разбить okvedExtra (строка или список строк с переносами) на записи «код название».
|
||||
|
||||
В опросниках доп. ОКВЭД часто идут склеенными:
|
||||
«25.93 — Производство изделий из проволоки\nцепей и пружин 32.12.1 — …»
|
||||
Здесь «цепей и пружин» — продолжение названия 25.93, а 32.12.1 — новый код.
|
||||
Нарезаем по кодам ОКВЭД (двузначный код с точками в начале записи).
|
||||
"""
|
||||
if isinstance(value, (list, tuple)):
|
||||
text = ' '.join(str(x) for x in value)
|
||||
else:
|
||||
text = str(value or '')
|
||||
text = text.replace('\r', ' ').replace('\n', ' ')
|
||||
# Нарезаем по началу кода ОКВЭД: «25.93», «32.12.1», «43.2» …
|
||||
# Перед кодом не должно быть цифры или точки (чтобы не разрезать «25.93» на «25» и «93»)
|
||||
parts = re.split(r'(?<!\d)(?<!\.)(?=\d{2}(?:\.\d+)*\b)', text)
|
||||
out = []
|
||||
for p in parts:
|
||||
p = p.strip().strip('-—– ').strip()
|
||||
if p and re.match(r'^\d{2}(?:\.\d+)*', p):
|
||||
out.append(p)
|
||||
return out
|
||||
|
||||
|
||||
def filter_threats_by_dict(lines, threats_full=None):
|
||||
"""Приводит строки угроз (от ИИ или оператора) к каноническому виду по словарю УБИ.
|
||||
|
||||
Для каждой строки:
|
||||
- есть код УБИ → берём полную запись «УБИ.xxx Название» из словаря;
|
||||
- кода нет → ищем по названию (точное совпадение или вхождение);
|
||||
- не нашлось в словаре → отбрасываем (мусор от ИИ).
|
||||
|
||||
Возвращает уникальные канонические строки, отсортированные по коду УБИ.
|
||||
"""
|
||||
if threats_full is None:
|
||||
from .threat_rules import load_threats
|
||||
threats_full = load_threats()
|
||||
by_code = {}
|
||||
by_norm = {}
|
||||
for t in threats_full:
|
||||
t = str(t).strip()
|
||||
code = _ubi_code(t)
|
||||
if code:
|
||||
by_code.setdefault(code, t)
|
||||
by_norm.setdefault(_norm_threat_text(t), t)
|
||||
out = []
|
||||
seen = set()
|
||||
for line in lines or []:
|
||||
line = str(line or '').strip()
|
||||
if not line:
|
||||
continue
|
||||
code = _ubi_code(line)
|
||||
canon = None
|
||||
if code and code in by_code:
|
||||
canon = by_code[code]
|
||||
else:
|
||||
n = _norm_threat_text(line)
|
||||
if n in by_norm:
|
||||
canon = by_norm[n]
|
||||
else:
|
||||
for k, v in by_norm.items():
|
||||
if n and (n in k or k in n):
|
||||
canon = v
|
||||
break
|
||||
if canon and canon not in seen:
|
||||
seen.add(canon)
|
||||
out.append(canon)
|
||||
out.sort(key=lambda t: (int(_ubi_code(t)) if _ubi_code(t) else 10 ** 9, t))
|
||||
return out
|
||||
|
||||
|
||||
def suggest_threats_ai(security_data, api_key, model='deepseek-chat', base_url=None,
|
||||
threats=None):
|
||||
"""ИИ предлагает актуальные угрозы БИ для объекта КИИ по его характеристикам.
|
||||
|
||||
security_data — dict: name, description, object_type, sphere, architecture,
|
||||
attacker_category, has_internet (True/False), etc.
|
||||
threats — актуальный перечень угроз БДУ ФСТЭК (список строк «УБИ.xxx …»).
|
||||
Передаётся в промт целиком, чтобы ИИ выбирал из свежего списка,
|
||||
а не из своего устаревшего.
|
||||
|
||||
Возвращает (список_угроз, ошибка).
|
||||
"""
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
|
||||
if threats:
|
||||
threat_list_text = "\n".join(f"{i+1}. {t}" for i, t in enumerate(threats))
|
||||
else:
|
||||
threat_list_text = "(перечень не предоставлен — используй знания об актуальном перечне БДУ ФСТЭК)"
|
||||
|
||||
pr = _load_prompt('suggest_threats')
|
||||
system = pr.get('system', (
|
||||
"Ты — эксперт ФСТЭК по безопасности критической информационной инфраструктуры "
|
||||
"(187-ФЗ, приказ ФСТЭК № 236). На основе характеристик объекта КИИ выбери из "
|
||||
"предоставленного перечня АКТУАЛЬНЫЕ угрозы безопасности информации."
|
||||
))
|
||||
rules = pr.get('rules', [])
|
||||
resp = pr.get('response_format',
|
||||
'Верни ТОЛЬКО JSON: {"threats": ["УБИ.xxx Название угрозы", ...]} '
|
||||
'— от 10 до 40 угроз, без пояснений.')
|
||||
user = (
|
||||
"Характеристики объекта КИИ:\n"
|
||||
f"{json.dumps(security_data, ensure_ascii=False)}\n\n"
|
||||
"Актуальный перечень угроз БДУ ФСТЭК (выбирай ТОЛЬКО из него, "
|
||||
"используй полные строки как они есть):\n"
|
||||
f"{threat_list_text}\n\n"
|
||||
+ ("Правила:\n" + "\n".join(f"{i+1}. {r}" for i, r in enumerate(rules)) + "\n\n" if rules else "")
|
||||
+ resp
|
||||
)
|
||||
js, err = _ai_json(api_key, model, base_url, system, user)
|
||||
if err:
|
||||
return None, err
|
||||
threats_out = js.get('threats') or []
|
||||
if isinstance(threats_out, str):
|
||||
threats_out = [threats_out]
|
||||
threats_out = [str(t).strip() for t in threats_out if str(t).strip()]
|
||||
# Жёсткий фильтр по словарю: мусор отбрасываем, «УБИ.069» дополняем
|
||||
# полным названием из актуального перечня (если он передан).
|
||||
threats_out = filter_threats_by_dict(threats_out, threats)
|
||||
return threats_out, None
|
||||
|
||||
|
||||
def check_threats_ai(security_data, current_threats, api_key, model='deepseek-chat', base_url=None):
|
||||
"""ИИ проверяет уже подобранный список угроз БИ для объекта КИИ.
|
||||
|
||||
В отличие от suggest_threats_ai (подбор заново), эта функция проверяет
|
||||
текущий список: какие угрозы лишние, каких не хватает, и даёт комментарий.
|
||||
|
||||
Возвращает (dict, ошибка):
|
||||
{"лишние": [...], "недостающие": [...], "комментарий": "..."}
|
||||
"""
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
if not current_threats:
|
||||
return None, "Список угроз пуст — сначала подберите угрозы (программой или ИИ)."
|
||||
|
||||
system = (
|
||||
"Ты — эксперт ФСТЭК по безопасности критической информационной инфраструктуры "
|
||||
"(187-ФЗ, приказ ФСТЭК № 236). Проверь, корректно ли подобран перечень угроз "
|
||||
"безопасности информации для объекта КИИ."
|
||||
)
|
||||
user = (
|
||||
"Характеристики объекта КИИ:\n"
|
||||
f"{json.dumps(security_data, ensure_ascii=False)}\n\n"
|
||||
"Текущий перечень угроз (подобран программой или оператором):\n"
|
||||
+ "\n".join(f"{i+1}. {t}" for i, t in enumerate(current_threats))
|
||||
+ "\n\nВерни ТОЛЬКО JSON: "
|
||||
'{"лишние": ["угрозы, которые НЕ актуальны для этого объекта"], '
|
||||
'"недостающие": ["актуальные угрозы, которых в списке не хватает"], '
|
||||
'"комментарий": "краткий вывод по перечню"}'
|
||||
)
|
||||
js, err = _ai_json(api_key, model, base_url, system, user)
|
||||
if err:
|
||||
return None, err
|
||||
# «Недостающие»/«лишние» — тоже через словарь: мусор отбрасываем,
|
||||
# неполные строки («УБИ.069») дополняем полными названиями.
|
||||
missing = filter_threats_by_dict(js.get('недостающие') or [])
|
||||
extra = filter_threats_by_dict(js.get('лишние') or [])
|
||||
comment = js.get('комментарий') or ''
|
||||
return {'лишние': extra, 'недостающие': missing, 'комментарий': comment}, None
|
||||
|
||||
|
||||
def suggest_object_params_ai(object_data, api_key, model='deepseek-chat', base_url=None,
|
||||
typical_objects=None, spheres=None, object_types=None,
|
||||
architectures=None):
|
||||
"""ИИ предлагает характеристики объекта КИИ: назначение, сферу, тип, архитектуру,
|
||||
типовой отраслевой объект — как рекомендацию оператору.
|
||||
|
||||
object_data — dict: name, description, purpose (что известно об объекте).
|
||||
typical_objects — перечень типовых отраслевых объектов (из 360-р) для выбора;
|
||||
spheres / object_types / architectures — полные списки допустимых значений.
|
||||
Промт берётся из dictionaries/prompts/suggest_object_params.json.
|
||||
|
||||
Возвращает (dict, ошибка): dict с полями
|
||||
назначение / сфера / тип / архитектура / типовой_объект / обоснование.
|
||||
"""
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
|
||||
# Загрузка промта из папки dictionaries/prompts
|
||||
import os as _os
|
||||
prompt_path = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)),
|
||||
'dictionaries', 'prompts', 'suggest_object_params.json')
|
||||
try:
|
||||
with open(prompt_path, encoding='utf-8') as _f:
|
||||
prompt = json.load(_f)
|
||||
except Exception:
|
||||
prompt = {}
|
||||
system = prompt.get('system', 'Ты — эксперт ФСТЭК по категорированию объектов КИИ.')
|
||||
rules = prompt.get('rules', [])
|
||||
response_format = prompt.get('response_format',
|
||||
'Верни ТОЛЬКО JSON: {"назначение": "...", "сфера": "...", '
|
||||
'"тип": "...", "архитектура": "...", "типовой_объект": "...", '
|
||||
'"обоснование": "..."}')
|
||||
|
||||
# Полные списки допустимых значений
|
||||
def _fmt_list(name, items):
|
||||
if not items:
|
||||
return f"{name}: (список не предоставлен)"
|
||||
return f"{name}:\n" + "\n".join(f"- {i}" for i in items)
|
||||
|
||||
lists_text = "\n\n".join([
|
||||
_fmt_list("Сферы деятельности (выбирай ТОЛЬКО из них)", spheres),
|
||||
_fmt_list("Типы объектов (выбирай ТОЛЬКО из них)", object_types),
|
||||
_fmt_list("Архитектуры (выбирай ТОЛЬКО из них)", architectures),
|
||||
])
|
||||
if typical_objects:
|
||||
to_text = "\n".join(f"{i+1}. {t}" for i, t in enumerate(typical_objects[:400]))
|
||||
else:
|
||||
to_text = "(перечень не предоставлен)"
|
||||
|
||||
rules_text = "\n".join(f"{i+1}. {r}" for i, r in enumerate(rules))
|
||||
|
||||
user = (
|
||||
"Информация об объекте КИИ:\n"
|
||||
f"{json.dumps(object_data, ensure_ascii=False)}\n\n"
|
||||
f"{lists_text}\n\n"
|
||||
"Перечень типовых отраслевых объектов КИИ (Распоряжение ПРФ № 360-р, "
|
||||
"выбирай ТОЛЬКО из него, копируй строку дословно):\n"
|
||||
f"{to_text}\n\n"
|
||||
"Правила:\n"
|
||||
f"{rules_text}\n\n"
|
||||
f"{response_format}"
|
||||
)
|
||||
js, err = _ai_json(api_key, model, base_url, system, user)
|
||||
if err:
|
||||
return None, err
|
||||
return {
|
||||
'назначение': str(js.get('назначение', '') or '').strip(),
|
||||
'сфера': str(js.get('сфера', '') or '').strip(),
|
||||
'тип': str(js.get('тип', '') or '').strip(),
|
||||
'архитектура': str(js.get('архитектура', '') or '').strip(),
|
||||
'типовой_объект': str(js.get('типовой_объект', '') or '').strip(),
|
||||
'обоснование': str(js.get('обоснование', '') or '').strip(),
|
||||
}, None
|
||||
|
||||
|
||||
def check_security_ai(security_data, api_key, model='deepseek-chat', base_url=None):
|
||||
"""ИИ-проверка заполненной вкладки «Безопасность» объекта КИИ (236 приказ).
|
||||
|
||||
Возвращает (результат, ошибка): результат — dict с 'оценка', 'замечания', 'предложения'.
|
||||
"""
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
pr = _load_prompt('check_security')
|
||||
system = pr.get('system', (
|
||||
"Ты — эксперт по безопасности критической информационной инфраструктуры (187-ФЗ, "
|
||||
"приказ ФСТЭК России от 25.12.2017 № 239 и приказ № 236). Проверь заполненные "
|
||||
"сведения об объекте КИИ и дай рекомендации."
|
||||
))
|
||||
system = system + " " + pr.get('response_format',
|
||||
'Верни ТОЛЬКО 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):
|
||||
"""ИИ-проверка реквизитов организации.
|
||||
|
||||
@@ -399,7 +855,8 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
system = (
|
||||
"Ты — юрист по защите персональных данных (152-ФЗ). Проверь реквизиты организации. "
|
||||
"Ты — специалист по проверке реквизитов организации (ИНН/КПП/ОГРН). "
|
||||
"Проверь реквизиты организации. "
|
||||
"Верни ТОЛЬКО JSON: "
|
||||
'{"оценка": "ok|warning|error", '
|
||||
'"замечания": ["..."], '
|
||||
@@ -438,3 +895,130 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None
|
||||
},
|
||||
'apply': {'company': {k: v for k, v in proposals.items() if v}},
|
||||
}, None
|
||||
|
||||
|
||||
def suggest_defense_ai(defense_data, api_key, model='deepseek-chat', base_url=None):
|
||||
"""ИИ-рекомендации по средствам защиты информации (СЗИ).
|
||||
|
||||
По категории значимости, угрозам и текущему списку СЗИ ИИ подсказывает,
|
||||
чего не хватает (например, антивирус, СКЗИ, SIEM и т.п.).
|
||||
Возвращает (результат, ошибка).
|
||||
"""
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
pr = _load_prompt('suggest_defense')
|
||||
system = pr.get('system', (
|
||||
"Ты — эксперт по безопасности КИИ (187-ФЗ, приказ ФСТЭК № 239). "
|
||||
"По категории значимости объекта и актуальным угрозам определи, каких "
|
||||
"средств защиты информации (СЗИ) не хватает, и дай рекомендации."
|
||||
))
|
||||
system = system + " " + pr.get('response_format',
|
||||
'Верни ТОЛЬКО JSON: {"рекомендации": [{"сзи": "...", "зачем": "..."}], '
|
||||
'"общие_советы": ["..."]}')
|
||||
user = json.dumps(defense_data, ensure_ascii=False)
|
||||
js, err = _ai_json(api_key, model, base_url, system, user)
|
||||
if err:
|
||||
return None, err
|
||||
recs = js.get('рекомендации') or []
|
||||
if not isinstance(recs, list):
|
||||
recs = []
|
||||
tips = js.get('общие_советы') or []
|
||||
if isinstance(tips, str):
|
||||
tips = [tips]
|
||||
return {
|
||||
'source': 'ИИ',
|
||||
'source_url': 'openai-совместимый API',
|
||||
'disclaimer': AI_DISCLAIMER,
|
||||
'kind': 'defense',
|
||||
'data': {
|
||||
'рекомендации': recs,
|
||||
'общие_советы': tips,
|
||||
},
|
||||
}, None
|
||||
|
||||
|
||||
def check_criteria_ai(criteria_data, api_key, model='deepseek-chat', base_url=None):
|
||||
"""ИИ-проверка категорий значимости по ПП РФ № 127.
|
||||
|
||||
По данным объекта КИИ (назначение, сфера, категории по показателям,
|
||||
обоснования) ИИ проверяет корректность присвоенных категорий.
|
||||
Возвращает (результат, ошибка).
|
||||
"""
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
pr = _load_prompt('check_criteria')
|
||||
system = pr.get('system', (
|
||||
"Ты — эксперт по категорированию объектов КИИ (187-ФЗ, ПП РФ № 127). "
|
||||
"Проверь присвоенные категории значимости по показателям критериев."
|
||||
))
|
||||
system = system + " " + pr.get('response_format',
|
||||
'Верни ТОЛЬКО JSON: {"оценка": "ok|warning|error", '
|
||||
'"замечания": [{"показатель": "...", "категория": "...", "комментарий": "..."}], '
|
||||
'"предложения": {"criteria_values": "...", "criteria_justification": "..."}, '
|
||||
'"итоговая_категория": "I|II|III|—"}')
|
||||
user = json.dumps(criteria_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': 'criteria',
|
||||
'data': {
|
||||
'rating': js.get('оценка', 'warning'),
|
||||
'notes': notes,
|
||||
'proposals': proposals,
|
||||
'final_category': js.get('итоговая_категория', ''),
|
||||
},
|
||||
}, None
|
||||
|
||||
|
||||
def check_object_partial(blocks, api_key, model='deepseek-chat', base_url=None, workdir=None):
|
||||
"""ИИ-проверка объекта КИИ по частям.
|
||||
|
||||
blocks: список [{'name': 'Реквизиты организации', 'prompt': 'check_company', 'data': {...}}, ...]
|
||||
Для каждого блока: данные пишутся во временный файл (вход), вызывается ИИ,
|
||||
ответ пишется во временный файл (выход). Возвращает (results, errors):
|
||||
results — [{name, ok, text, files: {in: путь, out: путь}}].
|
||||
"""
|
||||
import tempfile
|
||||
import os
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
if workdir is None:
|
||||
workdir = tempfile.mkdtemp(prefix='dokogen_ai_')
|
||||
results = []
|
||||
for i, blk in enumerate(blocks, 1):
|
||||
name = blk.get('name', f'Блок {i}')
|
||||
prompt_name = blk.get('prompt', 'check_security')
|
||||
data = blk.get('data', {})
|
||||
f_in = os.path.join(workdir, f'block_{i}_input.json')
|
||||
f_out = os.path.join(workdir, f'block_{i}_output.json')
|
||||
try:
|
||||
with open(f_in, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
except Exception:
|
||||
pass
|
||||
pr = _load_prompt(prompt_name)
|
||||
system = pr.get('system', 'Ты — эксперт по КИИ (187-ФЗ). Проверь данные и дай рекомендации.')
|
||||
system = system + ' ' + pr.get('response_format', 'Верни ТОЛЬКО JSON с результатом проверки.')
|
||||
user = json.dumps(data, ensure_ascii=False)
|
||||
js, err = _ai_json(api_key, model, base_url, system, user)
|
||||
if err:
|
||||
results.append({'name': name, 'ok': False, 'text': f'Ошибка: {err}', 'files': {'in': f_in, 'out': f_out}})
|
||||
continue
|
||||
try:
|
||||
with open(f_out, 'w', encoding='utf-8') as f:
|
||||
json.dump(js, f, ensure_ascii=False, indent=2)
|
||||
except Exception:
|
||||
pass
|
||||
text = json.dumps(js, ensure_ascii=False, indent=2)
|
||||
results.append({'name': name, 'ok': True, 'text': text, 'files': {'in': f_in, 'out': f_out}})
|
||||
return results, None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Генератор документов 152-ФЗ
|
||||
"""DokoGen — Генератор документов 187-ФЗ
|
||||
Точка входа: python main.py
|
||||
"""
|
||||
import sys
|
||||
|
||||
+10
-10
@@ -30,8 +30,8 @@ def create_test_template():
|
||||
doc.add_paragraph('<!-- loop:information_systems -->')
|
||||
doc.add_paragraph('=== IS: {{item.name}} ===')
|
||||
doc.add_paragraph('Category: {{item.category}}')
|
||||
doc.add_paragraph('Threat type: {{item.threat_type}}')
|
||||
doc.add_paragraph('Defence level: {{item.defence_level}}')
|
||||
doc.add_paragraph('Sphere: {{item.sphere}}')
|
||||
doc.add_paragraph('Object type: {{item.object_type}}')
|
||||
doc.add_paragraph('')
|
||||
|
||||
# Inner loop: commission
|
||||
@@ -72,12 +72,12 @@ def test_zip_loop_expansion():
|
||||
CommissionMember(role='Член комиссии', position='Бухгалтер', fio='Сидорова А.А.'),
|
||||
],
|
||||
information_systems=[
|
||||
InformationSystem(name='ИС-1 "Бухгалтерия"', category='специальные',
|
||||
pd_count='менее 100 000', threat_type='1', defence_level='2'),
|
||||
InformationSystem(name='ИС-2 "Кадры"', category='иные',
|
||||
pd_count='более 100 000', threat_type='3', defence_level='4'),
|
||||
InformationSystem(name='ИС-3 "Склад"', category='общедоступные',
|
||||
pd_count='менее 100 000', threat_type='2', defence_level='3'),
|
||||
InformationSystem(name='ОКИИ-1 "Платёжный шлюз"', category='1',
|
||||
sphere='Банковская сфера и финансовый рынок', object_type='АСУ'),
|
||||
InformationSystem(name='ОКИИ-2 "Кадры"', category='2',
|
||||
sphere='Наука', object_type='ИС'),
|
||||
InformationSystem(name='ОКИИ-3 "Склад"', category='3',
|
||||
sphere='Здравоохранение', object_type='ИС'),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -87,7 +87,7 @@ def test_zip_loop_expansion():
|
||||
is_list=company.information_systems,
|
||||
doc_number='001',
|
||||
doc_date='31.07.2026',
|
||||
direction='152fz'
|
||||
direction='187fz'
|
||||
)
|
||||
|
||||
loops = replacements.get('_loops', {})
|
||||
@@ -135,7 +135,7 @@ def test_zip_loop_expansion():
|
||||
is_list=company.information_systems,
|
||||
doc_number='001',
|
||||
doc_date='31.07.2026',
|
||||
direction='152fz'
|
||||
direction='187fz'
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user