feat: автозаполнение вкладки «Безопасность» по данным объекта

Новая кнопка « Автозаполнить» в панели действий — заполняет пустые поля
логически, без выдумок:
- категория нарушителя: есть интернет → внешний, нет → внутренний
- уровень возможностей: Н1 (внутренний) / Н2 (внешний)
- виды нарушителей: по категории из словаря attacker_types.json
- угрозы: подбор программой (скоринг по характеристикам объекта)
- инциденты: выводятся из подобранных угроз по ключевым словам
- орг. меры: базовый набор (контролируемая зона, доступ, документы,
  ответственные, обучение, инциденты, перечень лиц)
- тех. меры: базовые + доп. по средствам защиты объекта (СЗИ:
  межсетевой экран → МЭ, антивирус, СОВ, СКЗИ, виртуализация)
Незаполненное пользователем не перезаписывает.
This commit is contained in:
2026-08-04 20:35:29 +04:00
parent 1ef8485b13
commit 68caaf9b63
2 changed files with 196 additions and 0 deletions
+137
View File
@@ -205,3 +205,140 @@ def suggest_threats_program(isys, threats=None, limit=None):
if limit and len(result) > limit: if limit and len(result) > limit:
result = result[:limit] result = result[:limit]
return result return result
# ---------------------------------------------------------------
# Автозаполнение вкладки «Безопасность» по данным объекта (логическое)
# ---------------------------------------------------------------
# Ключевые слова: угроза → тип инцидента (словарь incidents.json)
INCIDENT_RULES = [
('отказ в обслуживании', 'отказ в обслуживании'),
('несанкционированн', 'несанкционированный доступ'),
('утечк', 'утечка данных (нарушение конфиденциальности)'),
('модификац', 'модификация (подмена) данных'),
('подмен', 'модификация (подмена) данных'),
('функционирован', 'нарушение функционирования технических средств'),
('техническ', 'нарушение функционирования технических средств'),
('использован', 'несанкционированное использование вычислительных ресурсов объекта'),
]
# Базовый набор орг. мер (всегда актуальны для значимого объекта)
BASE_ORG_MEASURES = [
'Установление контролируемой зоны',
'Контроль физического доступа к объекту',
'Разработка документов (регламентов, инструкций, руководств) по обеспечению безопасности объекта',
'Назначение ответственных за обеспечение безопасности объекта',
'Обучение и инструктаж персонала',
'Регламентация действий при компьютерных инцидентах',
'Утверждение перечня лиц, имеющих доступ к объекту',
]
# Базовый набор тех. мер
BASE_TECH_MEASURES = [
'Идентификация и аутентификация пользователей',
'Управление доступом',
'Ограничение программной среды',
'Антивирусная защита',
'Защита информации от несанкционированного доступа (СЗИ от НСД)',
'Регистрация и мониторинг событий безопасности',
'Резервное копирование и восстановление',
'Защита технических средств и линий связи',
]
# Средства защиты → доп. тех. меры
DEFENSE_TOOL_MEASURES = [
('межсетев', 'Межсетевое экранирование'),
('firewall', 'Межсетевое экранирование'),
('сетев', 'Межсетевое экранирование'),
('обнаруж', 'Обнаружение вторжений (СОВ)'),
('ids', 'Обнаружение вторжений (СОВ)'),
('ips', 'Обнаружение вторжений (СОВ)'),
('криптограф', 'Криптографическая защита информации (СКЗИ)'),
('vipnet', 'Криптографическая защита информации (СКЗИ)'),
('континент', 'Криптографическая защита информации (СКЗИ)'),
('виртуализ', 'Защита среды виртуализации'),
('vmware', 'Защита среды виртуализации'),
('hyper-v', 'Защита среды виртуализации'),
]
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. Тех. меры — базовые + по СЗИ объекта
if not (getattr(isys, 'tech_measures', '') or '').strip():
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)
return res
+59
View File
@@ -980,6 +980,8 @@ class DokoGenApp:
# --- Панель действий (сверху, всегда видна) --- # --- Панель действий (сверху, всегда видна) ---
sec_actions = ttk.Frame(sec_frame) sec_actions = ttk.Frame(sec_frame)
sec_actions.pack(fill=tk.X, pady=(0, 6)) sec_actions.pack(fill=tk.X, pady=(0, 6))
ttk.Button(sec_actions, text="⚡ Автозаполнить",
command=self._auto_fill_security_click).pack(side=tk.LEFT, padx=2)
ttk.Button(sec_actions, text="⬇️ Загрузить угрозы ФСТЭК", ttk.Button(sec_actions, text="⬇️ Загрузить угрозы ФСТЭК",
command=self._download_fstec_threats).pack(side=tk.LEFT, padx=2) command=self._download_fstec_threats).pack(side=tk.LEFT, padx=2)
ttk.Button(sec_actions, text="⚙️ Подобрать угрозы (программа)", ttk.Button(sec_actions, text="⚙️ Подобрать угрозы (программа)",
@@ -2047,6 +2049,63 @@ class DokoGenApp:
text="⚠ Список угроз ещё не скачан — нажмите «⬇️ Загрузить угрозы ФСТЭК»", text="⚠ Список угроз ещё не скачан — нажмите «⬇️ Загрузить угрозы ФСТЭК»",
foreground='#c62828') foreground='#c62828')
def _auto_fill_security_click(self):
"""⚡ Автозаполнение вкладки «Безопасность» по данным объекта (логическое).
Заполняет только пустые поля:
категория/уровень/виды нарушителя по интернету и типу объекта;
угрозы подбор программой (скоринг);
инциденты из подобранных угроз;
орг./тех. меры базовый набор + по средствам защиты объекта.
"""
if self._current_is_idx is None:
messagebox.showinfo("Автозаполнение", "Сначала выберите объект КИИ в списке слева")
return
self._collect_is()
isys = self.model.information_systems[self._current_is_idx]
try:
from .threat_rules import auto_fill_security, load_threats
threats_full = load_threats()
fill = auto_fill_security(isys, threats=threats_full)
except Exception as e:
messagebox.showerror("Ошибка", str(e))
return
if not fill:
messagebox.showinfo("Автозаполнение", "Все поля безопасности уже заполнены.")
return
# Применяем к виджетам
if 'attacker_category' in fill and hasattr(self, 'is_attacker_category_combo'):
self.is_attacker_category_combo.set(fill['attacker_category'])
if 'attacker_level' in fill and hasattr(self, 'is_attacker_level_combo'):
self.is_attacker_level_combo.set(fill['attacker_level'])
for key, attr in [('attacker_types', 'is_attacker_types_listbox'),
('threats', 'is_threats_listbox'),
('incidents', 'is_incidents_listbox'),
('org_measures', 'is_org_measures_listbox'),
('tech_measures', 'is_tech_measures_listbox')]:
if key in fill and hasattr(self, attr):
lb = getattr(self, attr)
lb.delete(0, tk.END)
for line in fill[key].split('\n'):
if line.strip():
lb.insert(tk.END, line.strip())
self._collect_is() # сохранить в модель
filled = ', '.join({
'attacker_category': 'нарушитель',
'attacker_level': 'уровень',
'attacker_types': 'виды нарушителя',
'threats': 'угрозы',
'incidents': 'инциденты',
'org_measures': 'орг. меры',
'tech_measures': 'тех. меры',
}.get(k, k) for k in fill)
self.log(f"⚡ Автозаполнение выполнено: {filled}")
messagebox.showinfo("Автозаполнение",
f"Заполнено: {filled}.\n\nНезаполненные поля (если есть) требуют ручного ввода "
f"или данных из опросника.")
def _suggest_threats_program_click(self): def _suggest_threats_program_click(self):
"""Подобрать угрозы логикой программы по характеристикам объекта.""" """Подобрать угрозы логикой программы по характеристикам объекта."""
if self._current_is_idx is None: if self._current_is_idx is None: