feat: подбор угроз — инвертированная логика (все угрозы минус неактуальные)

- threat_rules: по умолчанию актуальны ВСЕ угрозы БДУ ФСТЭК, исключаются
  заведомо неактуальные: нет интернета → убраны удалённый доступ/веб/почта/
  беспроводные/DDoS/облачные/грид; АСУ → убраны BIOS/грид/виртуализация/браузер
  (теперь 170-200 угроз на объект вместо 15 «самых похожих»)
- _has_internet уточнён: «Отсутствует» у оператора / «изолирован» / «не требуется»
  → внешней сети нет; сеть общего пользования или реальный оператор → есть
- verify.suggest_threats_ai: свежий перечень ФСТЭК (227 угроз) передаётся в промт,
  ИИ выбирает из него, а не из устаревших знаний; + человекочитаемая сводка объекта
- ui: кнопки программа/ИИ без лимита 20; после подбора угрозы сохраняются в модель
  (_collect_is) — при открытии словаря галочки выбранных угроз стоят
- models.from_dict: фильтрация неизвестных полей вложенных объектов — старые файлы
  с удалёнными полями (attacker_characteristics) больше не падают
This commit is contained in:
2026-08-04 17:59:44 +04:00
parent 8569b478d4
commit 6c2e4a0c38
4 changed files with 108 additions and 58 deletions
+19 -6
View File
@@ -1900,7 +1900,7 @@ class DokoGenApp:
isys = self.model.information_systems[self._current_is_idx]
try:
from .threat_rules import suggest_threats_program
threats = suggest_threats_program(isys, limit=20)
threats = suggest_threats_program(isys)
except Exception as e:
messagebox.showerror("Ошибка", str(e))
return
@@ -1912,6 +1912,7 @@ class DokoGenApp:
lb.delete(0, tk.END)
for t in threats:
lb.insert(tk.END, t)
self._collect_is() # сохранить в модель — галочки в словаре будут стоять
self.log(f"⚙️ Угрозы подобраны программой: {len(threats)} шт.")
def _suggest_threats_ai_click(self):
@@ -1925,7 +1926,8 @@ class DokoGenApp:
return
self._collect_is()
isys = self.model.information_systems[self._current_is_idx]
from .threat_rules import _has_internet
from .threat_rules import _has_internet, load_threats
has_internet = _has_internet(isys)
data = {
'name': isys.name or '',
'description': isys.description or '',
@@ -1934,16 +1936,26 @@ class DokoGenApp:
'architecture': isys.architecture or '',
'net_category': isys.net_category or '',
'attacker_category': isys.attacker_category or '',
'has_internet': _has_internet(isys),
'has_internet': has_internet,
'summary': (
f"Объект КИИ: {isys.name or ''}. "
f"Тип: {isys.object_type or ''}. "
f"Сфера: {isys.sphere or ''}. "
f"Выход в интернет: {'есть' if has_internet else 'нет'}. "
f"Категория нарушителя: {isys.attacker_category or ''}. "
f"Сети: {isys.net_category or ''}"
),
}
self.log(f"🤖 Запрос к ИИ: подбор угроз для «{isys.name}»")
threats_full = load_threats() # свежий перечень с сайта ФСТЭК
self.log(f"🤖 Запрос к ИИ: подбор угроз для «{isys.name}» (перечень: {len(threats_full)} угроз)")
self._verify_log("Подбор угроз через ИИ…")
def _run():
try:
from .verify import suggest_threats_ai
threats, err = suggest_threats_ai(
data, settings['ai_api_key'], settings.get('ai_model'), settings.get('ai_base_url'))
data, settings['ai_api_key'], settings.get('ai_model'),
settings.get('ai_base_url'), threats=threats_full)
self.root.after(0, lambda: self._suggest_threats_ai_done(threats, err))
except Exception as e:
self.root.after(0, lambda: self._suggest_threats_ai_done(None, str(e)))
@@ -1962,6 +1974,7 @@ class DokoGenApp:
lb.delete(0, tk.END)
for t in threats:
lb.insert(tk.END, t)
self._collect_is() # сохранить в модель — галочки в словаре будут стоять
self.log(f"🤖 ИИ подобрал угроз: {len(threats)}")
self._verify_log(f"🤖 ИИ: {len(threats)} угроз")
@@ -1973,7 +1986,7 @@ class DokoGenApp:
self._collect_is()
isys = self.model.information_systems[self._current_is_idx]
from .threat_rules import suggest_threats_program
program = suggest_threats_program(isys, limit=20)
program = suggest_threats_program(isys)
current = [str(self.is_threats_listbox.get(i)) for i in range(self.is_threats_listbox.size())]
if not current:
messagebox.showinfo("Сравнение", "Сначала подберите угрозы (программой или ИИ).")