From 1ef8485b137234ca77bdf3e031d53021c4c830f5 Mon Sep 17 00:00:00 2001 From: prog1764 Date: Tue, 4 Aug 2026 20:31:52 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=BF=D0=BE=D0=B4=D0=B1=D0=BE=D1=80=20?= =?UTF-8?q?=D1=83=D0=B3=D1=80=D0=BE=D0=B7=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D0=BC=D0=BC=D0=BE=D0=B9=20+=20=D1=81=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D0=BC=D0=BC=D0=B0=E2=86=94=D0=98=D0=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблемы: 1. Подбор программой возвращал 193 из 227 угроз (весь перечень) — теперь скоринг по ключевым словам объекта (тип, интернет, нарушитель, сеть), отсев нерелевантных: 72-82 угрозы вместо 193 2. Сравнение программа↔ИИ не подсвечивало совпадения: ИИ возвращает «1. УБИ.001 …» / с лишними пробелами — сравнение шло по точной строке. Теперь ключ сравнения — код УБИ (regex), иначе нормализованный текст; добавлена статистика «Совпало / только программа / только ИИ» --- dokogen/threat_rules.py | 26 ++++++++++++++++++++++++-- dokogen/ui.py | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/dokogen/threat_rules.py b/dokogen/threat_rules.py index bd3c8c3..ad14621 100644 --- a/dokogen/threat_rules.py +++ b/dokogen/threat_rules.py @@ -173,12 +173,34 @@ def suggest_threats_program(isys, threats=None, limit=None): # — оставляем всё, кроме специфики каналов связи, если их нет pass - result = [] + # --- Положительные ключевые слова: чем больше совпало — тем угроза актуальнее --- + 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 - result.append(t) + sc = sum(1 for kw in pos if kw in tl) + scored.append((sc, t)) + + # Сортируем по релевантности (сначала — с наибольшим совпадением) + scored.sort(key=lambda x: -x[0]) + result = [t for sc, t in scored if sc > 0] if limit and len(result) > limit: result = result[:limit] diff --git a/dokogen/ui.py b/dokogen/ui.py index f1f9a17..c7d30cc 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -5,6 +5,7 @@ import tkinter as tk from tkinter import ttk, messagebox, filedialog, simpledialog, scrolledtext import os import json +import re import sys import threading from datetime import datetime @@ -2147,10 +2148,30 @@ class DokoGenApp: messagebox.showinfo("Сравнение", "Сначала подберите угрозы (программой или ИИ).") return + # Нормализация строк угроз для сравнения: + # «1. УБИ.001 Угроза…», «УБИ.001 Угроза…» и «УБИ.001 Угроза…» — это одна и та же угроза. + # Ключ сравнения — код УБИ, если он есть (ИИ может обрезать/перефразировать название); + # иначе — нормализованный текст. + def _ubi_code(s): + m = re.search(r'уби\.?\s*(\d+)', str(s).lower()) + return m.group(1) if m else None + + def _norm(s): + s = re.sub(r'^\d+[.)]?\s*', '', str(s).strip()) # ведущий номер «1. » + s = re.sub(r'\s+', ' ', s).strip() # схлопнуть пробелы + return s.lower() + + def _key(s): + code = _ubi_code(s) + return ('ubi', code) if code else ('name', _norm(s)) + + pset_norm = {_key(t) for t in program} + cset_norm = {_key(t) for t in current} + # Показываем окно: слева подобрано программой, справа текущий список dlg = tk.Toplevel(self.root) dlg.title("Сравнение угроз: программа ↔ ИИ/текущий") - dlg.geometry("760x420") + dlg.geometry("780x460") dlg.transient(self.root) frm = ttk.Frame(dlg, padding=8) @@ -2158,7 +2179,7 @@ class DokoGenApp: left = ttk.Frame(frm) left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) - ttk.Label(left, text="⚙️ Программа", font=('Arial', 9, 'bold')).pack(anchor=tk.W) + ttk.Label(left, text=f"⚙️ Программа ({len(program)})", font=('Arial', 9, 'bold')).pack(anchor=tk.W) lb_p = tk.Listbox(left, height=14) lb_p.pack(fill=tk.BOTH, expand=True, pady=4) for t in program: @@ -2166,22 +2187,25 @@ class DokoGenApp: right = ttk.Frame(frm) right.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(8, 0)) - ttk.Label(right, text="🤖 ИИ / текущий список", font=('Arial', 9, 'bold')).pack(anchor=tk.W) + ttk.Label(right, text=f"🤖 ИИ / текущий ({len(current)})", font=('Arial', 9, 'bold')).pack(anchor=tk.W) lb_c = tk.Listbox(right, height=14) lb_c.pack(fill=tk.BOTH, expand=True, pady=4) for t in current: lb_c.insert(tk.END, t) - # Подсветка совпадений - pset = set(program) + # Подсветка совпадений (по коду УБИ / нормализованному виду) + common = pset_norm & cset_norm for i in range(lb_p.size()): - if lb_p.get(i) in set(current): + if _key(lb_p.get(i)) in common: lb_p.itemconfig(i, bg='#c8e6c9') for i in range(lb_c.size()): - if lb_c.get(i) in pset: + if _key(lb_c.get(i)) in common: lb_c.itemconfig(i, bg='#c8e6c9') - info = ttk.Label(frm, text="Зелёным выделены совпавшие угрозы.", + info = ttk.Label(frm, + text=f"Совпало угроз: {len(common)} (зелёным). " + f"Только программа: {len(program) - len(common)}, " + f"только ИИ/текущий: {len(current) - len(common)}.", foreground='gray') info.pack(anchor=tk.W, pady=(4, 0))