diff --git a/dokogen/threat_rules.py b/dokogen/threat_rules.py index ad14621..0408f0b 100644 --- a/dokogen/threat_rules.py +++ b/dokogen/threat_rules.py @@ -205,3 +205,140 @@ def suggest_threats_program(isys, threats=None, limit=None): if limit and len(result) > limit: result = result[:limit] 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 diff --git a/dokogen/ui.py b/dokogen/ui.py index c7d30cc..afe60b4 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -980,6 +980,8 @@ class DokoGenApp: # --- Панель действий (сверху, всегда видна) --- sec_actions = ttk.Frame(sec_frame) 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="⬇️ Загрузить угрозы ФСТЭК", command=self._download_fstec_threats).pack(side=tk.LEFT, padx=2) ttk.Button(sec_actions, text="⚙️ Подобрать угрозы (программа)", @@ -2047,6 +2049,63 @@ class DokoGenApp: text="⚠ Список угроз ещё не скачан — нажмите «⬇️ Загрузить угрозы ФСТЭК»", 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): """Подобрать угрозы логикой программы по характеристикам объекта.""" if self._current_is_idx is None: